티스토리 뷰

API를 통해서 원하는 엔티티(or DTO)를 조회하는 방법에 대해 알아보았다. 해당 내용들을 정리해보자.

 

1. 엔티티 조회

V1 - 엔티티 조회해서 그대로 반환

  • DTO를 통하지 않고 쿼리를 통해 반환 받은 목록을 그대로 반환했다.
  • 엔티티를 그대로 반환하므로 매우 안좋은 방법이다.
  • 순환조회를 막기위해 Entity에 @JsonIgnore를 넣어서 막아야 한다.
    • 따라서 Entity 수정이 필연적으로 발생된다.

 

V2 - 엔티티 조회해서 DTO로 변환 후 반환

  • Entity를 바로 반환하지 않게 변환했지만 객체 내 LAZY 객체에 대해 접근하면서 즉시 로딩이 되면서 N + 1문제가 발생했다.
  • 또한, LAZY 객체 내 1 : N 객체가 존재해서 1 + N번 쿼리 (LAZY 객체) + N번 쿼리 (Many 객체) + N * M번 쿼리 가 발생했다.
    • 1번 조회 -> 2개의 결과, 2번 lazy객체 2개를 각각 조회 4번조회 -> toMany객체를 2번 조회 -> toMany객체 내 2개의 객체를 2회 조회해서 총 4번 조회 -> 총 11회 쿼리 발생
  • 엄청난 쿼리 수의 증가 문제가 있다.

 

V3 - 조회시 FETCH JOIN을 사용해서 Query 생성

  • Entity에 대한 쿼리문을 작성해서 조회
  • OneToOne, ManyToOne, OneToMany에 해당하는 모든 LAZY 객체를 FETCH JOIN해서 가져온다. 이때 toMany로 인해서 DB 테이블이 Many객체 기준으로 바뀌면서 Row수가 증가한다.
    이 결과값을 그대로 반환하면 중복된 객체를 가진 데이터를 반환하게 되므로 중복을 제거하기 위해 DISTINCT를 Query문제 추가해준다.
  • DISTINCT 사용시 주의
    • 페이징을 쿼리문에 사용할 수 없다. DB Table의 기준이 바뀌었으므로 내가 원하는 기준으로 적용해서 가져올 수가 없다. 사용시 메모리에 전부 DB값을 올려서 적용하므로 절대 사용하면 안된다. (메모리 에러 발생)
    • 컬렉션 객체는 하나까지만 FETCH JOIN을 사용하는 것을 지향한다. 그 이상은 부정합한 데이터를 반환할 가능성이 있다.

 

V3.1 - 조회시 FETCH JOIN + default_batch_fetch_size 를 적용

  • X to One 객체에 대해서는 FETCH JOIN을 사용해서 가져온다.
  • 나머지 (컬렉션) 객체에 대해서는 LAZY 로딩을 통해서 가져온다.
  • 이때, application.yaml에 (또는 컬렉션 객체에) BatchSize를 적용한다
    • BATCH SIZE를 적용하면 컬렉션 값을 가져올 때 INNER JOIN으로 모두 가져온다.
    • 따라서 N + 1 쿼리 문제를 -> 1 + 1로 바꿔준다. 가져올 때 한번에 가져오므로.
  • 페이징을 쿼리에 적용할 수 있다.

2. 쿼리 DTO 직접 조회

V4 - QueryDto를 생성해서 해당 DTO로 바로 조회

  • JPA에서 DTO를 직접 조회한다.
  • 가져오고 싶은 속성값을 DTO로 만들어서 쿼리 수행 때 바로 생성해서 반환한다.
  • 이때 컬렉션에 대해서는 따로 DTO를 만들고 이미 만들어진 DTO에 삽입해줘야 한다.
  • N + 1 문제가 발생한다.

결론

  1. 엔티티 조회 방식으로 우선 접근
    1. 기본적으로 FETCH JOIN으로 쿼리수를 최적화하자 (컬렉션이 없다면, X to One 관계)
    2. 컬렉션을 최적화
      1. 페이징이 필요하네? -> @BatchSize / hibernate.default_batch_fetch_size 설정 (100 ~ 1000 사이로)
      2. 페이징 없어도 되겠네? -> FETCH JOIN 사용
  2. 엔티티 조회로 안되면, 너무 많은 속성값이 있는 경우 DTO로 바로 조회
  3. DTO 조회도 안되면 NativeSql, 스프링 JdbcTemplate 사용한다

 

엔티티 조회 방식이 좋은 이유!

  • FETCH JOIN, batch size 등을 이용해서 코드를 거의 수정하지 않아도 되고, 옵션만 변경해서 다양한 성능 최적화를 시도할 수 있다.
  • DTO로 줄일 정도면 Redis(레디스) 등 캐시를 사용해야되는 규모까지 된 것
  • JPA가 많은 부분을 최적화를 해준다. (fetch join, batch size 등을 통해서)
반응형
Comments
반응형
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday