본문 바로가기

Spring&IntelliJ

@Embeddable에 대해서

@Embeddable은 JPA에서 값 타입(Value Type)을 정의할 때 사용하는 어노테이션입니다.

쉽게 말하면:

"이 클래스는 독립적인 테이블로 만들지 말고, 다른 엔티티의 컬럼에 포함시켜서 사용해라"

라는 의미입니다.


1. 일반적인 엔티티(Entity)와 비교

예를 들어 회원(Member)이 있다고 해보겠습니다.

 
@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private String city;
    private String street;
    private String zipcode;
}
 

DB 테이블은:

MEMBER
------------------
id
name
city
street
zipcode
 

이렇게 됩니다.

그런데 city, street, zipcode는 사실 하나의 개념입니다.

즉:

주소(Address)
 ├── city
 ├── street
 └── zipcode
 

라는 객체로 묶는 것이 더 객체지향적입니다.


2. Address를 값 타입으로 만들기

그래서 이렇게 만듭니다.

 
@Embeddable
@Getter
public class Address {

    private String city;

    private String street;

    private String zipcode;
}
 

여기서:

 
@Embeddable
 

의 의미는:

"이 객체는 다른 엔티티 내부에 포함될 수 있는 값 타입이다."

입니다.


3. 실제 사용하는 곳

Member에서:

 
@Entity
@Getter
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @Embedded
    private Address address;
}
 

@Embedded를 사용합니다.

관계:

Member Entity
      |
      |
      ↓
 Address Value Type
 

4. DB 테이블은 어떻게 될까?

중요한 점:

Address 테이블은 생성되지 않습니다.

많은 사람들이 여기서 헷갈립니다.

생성되는 테이블:

MEMBER
--------------------------------
id
name
city
street
zipcode
 

입니다.

즉:

자바에서는:

Member
 ├── id
 ├── name
 └── Address
       ├── city
       ├── street
       └── zipcode
 

이지만

DB에서는:

MEMBER
 ├── id
 ├── name
 ├── city
 ├── street
 └── zipcode
 

처럼 펼쳐집니다.


5. 왜 사용하는가?

(1) 의미 있는 객체로 묶기

주소를 그냥 String 3개로 관리하면:

 
private String city;
private String street;
private String zipcode;
 

의미가 분산됩니다.

하지만:

 
private Address address;
 

하면:

"이 값들은 주소라는 하나의 개념이다"

라고 표현할 수 있습니다.


(2) 재사용 가능

예를 들어:

 
Member
 └── Address

Delivery
 └── Address
 

둘 다 사용할 수 있습니다.

 
@Entity
public class Delivery {

    @Embedded
    private Address address;
}
 

그러면:

MEMBER 테이블

id
name
city
street
zipcode


DELIVERY 테이블

id
city
street
zipcode
 

이 됩니다.


6. Entity와 Embedded Value Type의 차이

구분Entity@Embeddable
식별자 필요 O (@Id 필요) X
생명주기 관리 O 소유 Entity에 의존
별도 테이블 O X
관계 매핑 가능 불가능
목적 독립적인 객체 값을 묶는 객체

예:

회원(Member):

Member
 └── id = 1
 

주소:

Address
 └── id?
 

주소는 독립적으로 존재할 필요가 없습니다.

따라서 Entity가 아니라 값 타입입니다.


7. 주의할 점: 값 타입은 불변으로 만드는 것이 좋음

현재 코드:

 
@Embeddable
@Getter
public class Address {
    private String city;
}
 

보통은:

 
@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;
    }
}
 

처럼 생성자를 통해 값을 넣고 변경 메서드를 제공하지 않는 방식으로 설계합니다.

왜냐하면:

 
member.getAddress().setCity("서울");
 

같은 변경이 발생하면 같은 Address 객체를 공유하는 다른 객체까지 영향을 받을 수 있기 때문입니다.


정리하면:

@Embeddable은 "이 클래스는 테이블이 아니라 다른 엔티티 안에 컬럼으로 들어가는 값 타입이다"라는 의미입니다.
Address라는 별도 테이블은 생성되지 않고, Member 테이블의 city, street, zipcode 컬럼으로 저장됩니다.