[Reflection] [우아콘2020] 수십억건에서 QUERYDSL 사용하기 - Select 컬럼에 Entity 자제
본문 바로가기

Development/Reflection

[Reflection] [우아콘2020] 수십억건에서 QUERYDSL 사용하기 - Select 컬럼에 Entity 자제

[2019] Spring JPA의 사실과 오해 (youtube.com)

 

이 글에서는 위 동영상에서, Entity 보다는 Dto를 우선이라는 챕터에 대해 느낀 점을 작성합니다.

 Entity 보다는 Dto를 우선

우리가 ORM을 사용하면서 Entity를 조회하는 경우가 많다. Entity를 통째로 가져온다면, N + 1 문제라던가, 불필요한 칼럼까지 가져오는 문제가 발생할 수 있다. 이는 성능 이슈의 요소를 포함한다.

따라서 우리는 Entity를 조회할 필요가 있는지 따져볼 필요가 있다.

  1. Entity를 수정해야 하는가?
  2. 단지 데이터의 조회만 필요하는가?

조회칼럼 최소화 하기

QueryDSL에서는 as 표현식으로 만약 이미 알고있는 정보가 있다면 이를 제거할 수 있다.

Select 컬럼에 Entity 자제

Entity 자체를 쿼리 하지 말고, 필요한 칼럼만 DTO를 사용해서 조회하는 것이 성능적으로 좋다.

특히 @OneToOne 관계에서는 Lazy Loading이 안 되므로 무조건 N+1문제가 발생한다.

 

그런데, Entity간 연관관계를 맺으려면 
반대 Entity가 있어야하지 않나요?

이 부분이 나도 궁금했던 부분이고, 이 글을 작성하게 된 계기가 됐다.

이런 Store 엔티티가 있다고 치고,

@Entity
@Getter
@Setter
public class Store extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "store_id", columnDefinition = "uuid", updatable = false)
    private UUID storeId;

    @OneToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;

아래와 같이 Store를 만드는 코드가 있다면, 엔티티를 인자로 넣어줘야 하는거 아닌가? 싶었고 항상 그래왔다.

(아래 코드의 경우 User가 들어간다.)

    public Store toEntity(User user) {
        return Store.builder()
                .user(user)
                .storeName(storeName)
                .storeNumber(storeNumber)
                .category(category)
                .build();
    }

 

하지만 생각해보면 그럴 필요가 없다. 왜냐하면 연관된 Entity의 save를 위해서는 반대편 Entity의 ID만 있으면 된다.

    public Store toEntity(Long userId) {
        return Store.builder()
                .user(new User(userId))
                .storeName(storeName)
                .storeNumber(storeNumber)
                .category(category)
                .build();
    }

위와 같은 느낌으로 수정하면 된다.

즉, 우리는 엔티티를 저장하기 위해 다른 연관 관계의 엔티티를 통째로 불러올 필요가 없다.

 

느낀 점

뭔가 DTO를 적극 활용해서, 필요한 칼럼만 추출하는 습관을 가져야겠다고 느꼈다.