신비한 개발사전

@Embedded, @Embeddable로 객체지향적인 JPA 엔티티 설계하기 본문

Backend

@Embedded, @Embeddable로 객체지향적인 JPA 엔티티 설계하기

jbilee 2025. 5. 25. 14:23

JPA를 사용해보면서 엔티티라는 개념에 익숙해질 때쯤, "DB에 의존적인 설계"라는 표현을 접하게 됐다.

@Entity
class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private int age;
    private String street;
    private String city;
    private int zip;
}

 

생각해보니 예시의 User 엔티티는 데이터베이스 테이블의 칼럼과 정확히 일치하도록 필드를 둔 것이고, 따라서 DB에 의존한다는 말이 나온 것이다. JPA가 없는 상황에서 동일한 정보로 User 객체를 만들어야 했다면, 아래처럼 주소와 관련된 값들은 별도의 Address 클래스로 빼도록 설계했을 것이다.

// User 클래스
class User {

    private Long id;
    private String name;
    private int age;

    // 주소의 세부적인 정보는 Address의 책임
    private Address address;
}

// Address 클래스
class Address {

    private String street;
    private String city;
    private int zip;
}

 

JPA를 도입하면 위와 같은 객체지향적인 클래스 설계가 불가능한가? 그렇진 않다.

 

@Embedded, @Embeddable 애노테이션을 사용하면 엔티티의 일부 필드를 클래스로 분리해도 모두 한 테이블의 칼럼으로 생성되도록 구현할 수 있다.

  • @Embedded: 분리된 클래스를 저장하는 필드에 적용한다.
  • @Embeddable: 분리된 클래스에 적용한다. @Entity는 테이블을 생성하기 때문에, @Embeddable로 대체해야 한다.
@Entity
class User {

    // ...중략...

    @Embedded
    private Address address;
}

@Embeddable // @Entity 대신 사용
class Address {

    private String street;
    private String city;
    private int zip;
}

 

이렇게 임베딩 방식을 차용하면 자바 코드에서는 Address 객체를 참조할 수 있고, 실제 데이터베이스 테이블에는 address 테이블 없이 street, city, zip 칼럼이 user 테이블에 유지된다.

 

@Embeddable로 클래스를 분리한 상황에서 연관관계 매핑이 필요하면, 한 엔티티 클래스였을 때 했던 것처럼 매핑이 필요한 필드에 애노테이션을 붙여주면 된다:

@Entity
class User {

    // ...중략...

    @Embedded
    private Address address;
}

@Embeddable
class Address {

    private String street;

    // User <-> City 간 N:1 연관관계 표현
    @ManyToOne
    private City city;

    private int zip;
}

@Entity
class City {

    private String name;
}

 

 

참고: