@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의 차이
| 식별자 필요 | 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 컬럼으로 저장됩니다.
'Spring&IntelliJ' 카테고리의 다른 글
| n+1문제에 대해서 (0) | 2026.06.16 |
|---|---|
| protected기본생성자를 사용하면 왜 개발자는 함부로 생성하지 못하고 JPA같은 프레임워크는 접근이 가능한거야? (0) | 2026.06.16 |
| @Inheritance에 대해서 (3가지 전략) (0) | 2026.06.16 |
| cascade = CascadeType.ALL은 어떨때 주로 쓰이고 왜 쓰여? (0) | 2026.06.16 |
| h2 파일 생성 실패시, Database not found 애러 해결방법 (0) | 2026.06.05 |