티스토리 뷰

Entity에서 일급 컬렉션을?

최근 프로젝트를 진행하던 중 두 Entity를 양방향으로 묶고, 로직을 추가하던 중에 @OneToMany 부분의 List를 가공해야 하는 일이 생겼습니다. 컬렉션을 가공할 일이 생기면 가장 먼저 떠오르는 것은 일급 컬렉션인데요. 지금까지 그럴 일이 없어서인지 한번도 엔티티 안에서 일급 컬렉션을 사용해 본 적이 없었다는 것을 깨달았습니다.

도메인 레이어를 풍성하게

상황은 다음과 같았습니다.

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Orders extends BaseEntity {

    // ...

    @OneToMany(mappedBy = "orders", cascade = CascadeType.PERSIST, orphanRemoval = true)
    private List<OrderDetail> orderDetails = new ArrayList<>();

    // ...
}

위 예시의 주문을 의미하는 Orders 엔티티와 주문 상세를 의미하는 OrderDetail 엔티티는 서로 관계가 긴밀하다고 판단을 하여 양방향 매핑으로 묶었습니다. (양방향 매핑은 꼭 필요한 경우가 아니면 자제하는 것이 좋습니다. 관리해야 할 포인트가 늘어나고, 사실상 단방향 매핑만으로 모든 관계를 구현할 수 있기 때문입니다.)

 

요구 사항을 구현하다 보니 Orders의 orderDetails 리스트를 stream 등으로 가공해야 할 일이 생겼습니다. 물론 해당 엔티티를 담당하는 서비스 레이어에서 담당해도 되겠지만, 도메인 레이어를 풍성하게 만드는 것이 좋기 때문에 해당 엔티티 내부에 넣어야겠다고 생각했습니다.

 

컬렉션의 가공, 하면 떠오르는 것이 바로 일급 컬렉션입니다. 일급 컬렉션은 내부에 컬렉션을 가지고 있는 객체인데요. 다양한 장점을 가지고 있기 때문에 OOP를 지향하기 위해서는 반드시 익혀야 할 개념입니다. 일급 컬렉션의 장점에 대해서는 이 글을 참고하시면 좋습니다.

다만 엔티티 내부의 리스트를 일급 컬렉션으로 묶어본 적이 없었어서, 방법이 없나 찾아보았는데요. 생각보다 간단했습니다.

답은 @Embeddable!

@Embeddable은 엔티티의 여러 필드들을 하나로 묶어서 관리하고 싶을 때 사용합니다. @Embeddable은 실제로는 하나의 객체지만, DB의 테이블에서는 해당 객체 내부에 있는 필드들이 풀어져서 만들어집니다. @Embeddable의 자바 주석을 따라 들어가 보시면 친절한 예시가 나와있습니다. 다음과 같이 객체에서는 주소(Address)라는 이름으로 street, city, state, zipcode를 묶어서 관리하고 싶을 때 사용하면 됩니다. zipcode 자체도 또 @Embeddable 객체네요.

 

@Embeddable

다음과 같이 @Embeddable을 사용해 쉽게 해결할 수 있었습니다.

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
public class Orders extends BaseEntity {

    // ...

    // @Embedded
    private OrderDetails orderDetails = new OrderDetails();

    // ...
}

@Getter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@Embeddable
public class OrderDetails {

    @OneToMany(mappedBy = "orders", cascade = CascadeType.PERSIST, orphanRemoval = true)
    private List<OrderDetail> details = new ArrayList<>();

    // ...
}

OrderDetails라는 복수 형태의 이름으로 일급 컬렉션을 묶고, @Embeddable을 사용했습니다. @Embeddable은 해당 클래스 위에 선언합니다. 혹은 Orders의 orderDetails 필드에 @Embedded라고 선언해도 됩니다. 둘 다 명시해줘도 되고, 둘 중 하나만 선언해주어도 동작합니다. 저는 개인적으로 해당 클래스에 @Embeddable만 선언하는 것을 선호합니다. OrderDetails 클래스가 일반 엔티티가 아니라 엔티티에 내장된 객체라는 것을 명시해줄 수 있기 때문입니다.

 

프로젝트 중에 간단하지만 새로운 사실을 알게 되어 빠르게 정리해 보았습니다. 잘 알고 있다고 생각했던 두 개념이 합쳐질 수 있다는 사실은 사소하지만 그냥 넘어가서는 안된다고 생각합니다. 일급 컬렉션과 @Embeddable 모두 적절한 곳에 잘 쓰면 객체지향적인 설계를 할 수 있는 중요한 개념들이기 때문에 익혀두시면 좋습니다. 읽어주셔서 감사합니다! :)

댓글
댓글쓰기 폼