어제 스터디를 하면서 @Embedded는 "JPA에서 자주 사용하는 컬럼들을 모아둔 묶음"이라고 이해했다고 얘기했는데, 공부를 하며 틀린 부분이 있다는 걸 알게 되어 정리해본다. 결론적으로 말하면, @Embedded는 JPA가 미리 제공하는 내장 기능이 아니라, 개발자가 자주 사용하는 컬럼들을 직접 하나의 값 객체로 만들어서 재사용하는 방식이라고 이해하면 된다.
예를 들어 쇼핑몰을 생각해보자.
우리가 인터넷 쇼핑을 할 때 회원 정보에 "주소(Address)" 를 입력한다.
이 주소는 회원 정보(Member)뿐만 아니라, 주문(Order), 배송(Delivery) 정보에서도 사용될 수 있다.
이럴 때 매번 city, street, zipcode 필드를 반복해서 정의하기보다는 공통으로 사용하는 주소 구조를 @Embeddable 클래스로 묶어두고 필요한 곳에서 @Embedded로 포함시키면 유지보수도 쉽고 코드도 훨씬 깔끔하다.
@Embeddable
@Getter
public class Address {
private String city;
private String street;
private String zipcode;
protected Address(){
}
public Address(String city, String street, String zipcode){
this.city = city;
this.street = street;
this.zipcode = zipcode;
}
}
@Entity
@Getter @Setter
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
private String name;
@Embedded
private Address address;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
}
Member 엔티티에서 주소를 @Embedded로 사용하면 “Member는 주소 정보를 포함한다”는 의미가 명확하게 드러나고, 재사용성도 좋아져 유지보수에 유리하다.
@Entity
@Getter @Setter
public class Delivery {
@Id @GeneratedValue
@Column(name = "delivery_id")
private Long id;
@OneToOne(mappedBy = "delivery")
private Order order;
@Embedded
private Address adress;
@Enumerated(EnumType.STRING)
private DeliveryStatus deliveryStatus; // READY, COMP
}
Delivery도 마찬가지로 주소 정보가 필요하기 때문에 Address를 @Embedded로 재사용할 수 있다.
이 경우 DB에는 Delivery 테이블 안에 city, street, zipcode 컬럼으로 펼쳐진다.
컬럼으로 펼쳐진다는 말은 JOIN을 통해 별도의 테이블을 참조하는 것이 아니라 위 사진처럼
Address 클래스 안에 정의된 city, street, zipcode 등의 필드가 Member 테이블의 컬럼으로 직접 포함되어 저장된다는 의미다.
즉, @Embedded는 DB 설계 관점에서 봤을 때 테이블 간 관계를 만드는 것이 아니라 해당 엔티티의 일부처럼 데이터를 구성하는 것을 말한다.
근데, 문득 또 이런 생각이 들었다.
회원(Member)에 주소를 저장해둔다면 주문(Order)이나 배송(Delivery)에서 그걸 참조하면 되지 굳이 각자 Address를 가지는 이유가 뭘까?
주소는 시점마다 ‘복사되어 저장’돼야 하기 때문에, 공유하지 않고 각 엔티티에 @Embedded로 따로 저장한다.
예를들어
- 회원 홍길동이 서울시 강남구에 살고 있음 → Member.address = 강남
- 나중에 이 회원이 부산으로 이사함 → Member.address = 부산
그런데 과거의 주문 기록에서 주소는?
- 배송 주소는 그 시점의 주소인 강남이어야 하는데,
- Order.address가 Member.address를 참조하고 있었다면 이사한 순간 과거 주문 주소까지 부산으로 바뀌는 문제가 생긴다.
그래서 JPA에서는 주소를 참조하지 않고, 그 시점에 복사해서 박아두는 구조로 설계한다. Member, Order, Delivery는 Address를 "참조"하는게 아니라 그 당시의 Address를 @Embedded로 복사해서 저장하는 것!
앞으로 도메인을 분석하고 테이블을 설계할 때 이러한 부분도 함께 고려해야겠다는 생각이 들었다.
'BE > 스프링부트와 JPA 활용' 카테고리의 다른 글
[JPA] 2. 도메인 분석 설계 - 요구사항 분석 / 도메인 모델과 테이블 설계 / 엔티티 클래스 개발 (0) | 2025.05.04 |
---|---|
[JPA] 1. 프로젝트 환경설정 (0) | 2025.05.04 |