[ JPA ] 강의 {실전! 스프링 부트와 JPA 활용2} 기억해야 할 내용

유기훈·2022년 10월 7일
0

JPA

목록 보기
13/13

인프런 {실전! 스프링 부트와 JPA 활용2} 강의를 완강 후 기억해야 할 내용을 3가지 뽑았다.

json 스펙을 맞추기 위해 감싸라

    @Data
    @AllArgsConstructor
    static class Result<T> { // json 스펙을 맞추기 위한 감싸기 용도
        private int count;
        private T data;
    }

Rest api 개발 시 json 스펙을 맞추어야 한다.
예를 들어 여러 데이터가 있는데 데이터형이 배열일 수도 있고 일반 자료형일 수도 있다. 이 데이터들을 그대로 json방식으로 변환하면 오류가 발생한다. 그러니 보내려는 주 내용을 제네릭을 사용한 data에 담아 보내도록 하자. 그러면 데이터 자료형이 다르더라도 문제없다.

Dto를 사용하자

    @Data
    static class OrderItemDto {
        private String itemName;
        private int orderPrice;
        private int count;

        public OrderItemDto(OrderItem orderItem) {
            itemName = orderItem.getItem().getName();
            orderPrice = orderItem.getOrderPrice();
            count = orderItem.getCount();
        }
    }

dto란?: Entity를 그대로 내보낸다면 Entity변경시 api spec또한 변경이 된다. 그러면 안되므로 client에서 필요한 데이터들만 담아서 주도록 하자. 필요한 데이터들이 멤버변수로 들어있는 클래스가 dto이다. 물론 request를 받을 때에도 dto사용이 가능하다.

장점

  • request를 dto를 사용해서 받을 시 request로 어떤 값이 넘어 오는지 알 수 있고 그러므로 관리성이 좋아진다.
  • api 스펙 변경에 유용하다.
  • 필요한 것들만 모아서 response로 보낼 수 있다. ( 비밀번호와 같은 중요정보의 유출이 없다)

N+1 문제와 최적화

N+1에 자세한 내용이 담긴 글: https://incheol-jung.gitbook.io/docs/q-and-a/spring/n+1

Entity에 Collection 변수가 들어있을 경우 발생하는 문제들이 많다. toMany 로 엮인 관계들 같은 경우에 query가 Collection의 length만큼 나간다던지와 같은 문제들이다.

해결책: fetch join과 default_batch_fetch_size를 활용하자.

    public List<Order> findAllWithItem() {
        return em.createQuery(
                "select distinct o from Order o" + // distinct가 있으면 Order 중에 id값 중복이 있으면 중복을 제거한다.
                        " join fetch o.member m" +
                        " join fetch o.delivery d" +
                        " join fetch o.orderItems oi" +
                        " join fetch oi.item i", Order.class)
                .getResultList(); // 메모리에서 페이징을 시도하는 문제가 발생할 수 있다.
    }

위와 같은 fetch join과 distinct를 사용하여 N+1문제를 해결 할 수 있다. 하지만 단순한 fetch join으로는 페이징을 사용하지 못하는 단점이 있다. 그래서 default_batch_fetch_size 를 활용하자. default_batch_fetch_size를 yml에 작성하고

    public List<Order> findAllWithMemberDelivery(int offset, int limit) {
        return em.createQuery(
                "select o from Order o" +
                        " join fetch o.member m" +
                        " join fetch o.delivery d", Order.class)
                .setFirstResult(offset)
                .setMaxResults(limit)
                .getResultList();
    }

다음과 같이 @ToOne인 변수들만 fetch join을 하면 컬렉션이나, 프록시 객체를 한꺼번에 설정한 size 만큼 IN 쿼리로 조회한다. 이 방법은 보이는 것 처럼 페이징도 가능하다.

OSIV

osiv란 요청이 시작되는 순간부터 끝날 때 까지 영속성 컨텍스트가 살아있는 상태를 말한다. 이는 트래픽이 많은 api server에서 문제를 일으킬 수 있다. 예를 들어 api 요청 로직에 외부 api 호출이 있는데 외부 api 호출이 만약 3분이 걸린다면 3분동안 영속성 컨텍스트가 살아있고 이는 그 시간동안 db connect pool을 잡고 있는 것이다. osiv는 default 값이 true이기 때문에 application.yml에 open-in-view: false를 써주어야 한다.

그래서 OSIV를 사용하지마?: 고객 서비스를 하는 트래픽이 많은 api server는 OSIV를 끄고, ADMIN 처럼 커넥션을 많이 사용하지 않는 곳에서는 OSIV를 켜도록 하자.

profile
개발할 수 있어 감사하다

0개의 댓글