본문 바로가기
개발자 일지/JPA

[강의 후기] 인프런 김영한님 스프링부트와 JPA 활용2 강의 후기

by 네빌링 2024. 11. 6.
반응형

인프런 스프링 강의로 유명한 김영한님의 실전! 스프링 부트와 JPA 활용 2편을 공부하고 간단히 후기를 남겨보려고 합니다.

 

인프런-김영한님-스프링부트와-JPA활용-강의-후기-썸네일

 

[목차]

  1. 강의 느낀점
  2. 강의에서 기억할 것
  3. 마치며

[관련 포스팅]

  1. 인프런 김영한님 자바 ORM 표준 JPA 프로그래밍
  2. 인프런 김영한님 스프링부트와 JPA 활용1 강의 후기

 

1. 강의 느낀점

 

드디어 JPA 활용2 강의를 다 들었습니다.

예전에 들었을 때 활용1까지 이해도가 낮은 상태라 2를 도전하지 못했는데 이번에 완강할 수 있어서 좋았습니다.

2에서도 새로운 개념이나 주의사항, 고려해야할점 등을 많이 소개해주셔서 시간이 아깝지 않은 강의였네요.

 

내년 목표 실적을 설정할 때 JPA와 관련된 기능 추가하거나, 토이프로젝트를 꼭 해봐야곘습니다.

방대한 내용이니만큼 실무나 토이프로젝트 등에 적용하지 않으면 많이 휘발될 것 같습니다.

 

이번 포스팅에서도 강의를 들으며 기억해야할 점이나 느낀 점 등을 정리해보려고 합니다.

 

2. 강의에서 기억할 것

 

이번 강의는 JPA를 통해 웹쇼핑몰 샘플을 만드는 것이 아니라 API를 만드는 과정을 통해 강의를 진행했습니다.

활용1보다 당연히 난이도가 있는 것 같고 기초강의와 활용1강의를 제대로 들어야 무난하게 활용2도 이해할 수 있을 것 같습니다.

 

저도 100% 이해한 것은 아니지만 휘발되기 전에 최대한 간결하게 중요한 부분 위주로 정리해보려고 합니다.

 

  • API 응답은 엔티티로 그대로 노출하면 안 된다. DTO에 담아서 반환하는 것이 좋다. API스펙은 바뀔 수 있기 때문에 엔티티에 섞어 넣으면 추후 유지보수가 힘들다.
  • 수정시에는 merge()보다 더티체킹(변경감지)를 활용하자.
  • List로 바로 반환시 배열(jsonArray)로 나간다. 이러면 count 등을 추가할 수가 없다. Result와 DTO를 만들어 반환하자.
  • 엔티티를 직접 리턴시 양방향 연관관계일 경우 서로가 서로를 참조한다. 한 곳을 @JsonIgnore해야 무한루프가 돌지 않음
  • 1+N 문제 해결을 위해 xToOne관계만 join fetch로 우선 가져오자.(LAZY 무시 후 EAGER모드로 작동)
  • JPA에서 바로 DTO로 받을 수도 있다. JPQL에서 select절에 new 패키지경로.dto명(값, 값..)으로 원하는 값만 바로 가져올 수 있어서 API스펙 최적화된 JPQL을 만들 수 있다.
    • 그러나 엔티티를 받은 후 DTO로 변환하는 것을 더 권장한다. 재사용성이 더 좋고 원하는 select절만 받는 게 성능에 그렇게 크게 영향을 주진 않는다.(엄청 큰 데이터가 아니라면)
  •  1:N관계를 JPQL로 fetch join하면 뻥튀기될 수 있다. distinct 처리가 하이버네이트6부터 되긴 하지만..
    • ORDER-ORDER_ITEM의 1:N 관계 테이블을 가정하고 ORDER에 2개, ORDER_ITEM에 4개의 데이터가 있어서 ORDER를 기준으로 JOIN하면 4개가 나온다.
    • fetch join시 페이징 처리는 쓰면 안 됨 (DB 1:N중 N을 중심으로 페이징 계산을 하기 때문). 그리고 메모리에 올려서 페이징. 메모리에 1만개를 올려서 페이징을 한다면 OutofMemory발생 가능성.
  • 위와 같이 1:N관계 페치 조인으로 한계가 있다. 이를 해결하려면?
    • 우선 xToOne관계(N:1 or 1:1)만 fetch join을 사용한다. 그러면 뻥튀기 문제가 없다.
    • 나머지는 for문을 통해 사용한다. 그러면 jpql쪽에서 페이징(offset, limit) 기능을 사용할 수 있다.
    • 그러나 N(orderItem)쪽으로 인해 여전히 N+1문제가 발생할 수 있다.
    • 이를 위해 batchSize를 사용하자.
  • batchSize는 개별 적용, 글로벌 적용 모두 가능하다. 글로벌적은 application.yml등에 default_batch_fetch_size로 설정값을 주면 된다. 설정값을 적용하면 그 값만큼 in 쿼리 처리가 된다.
    • orderItem에서는 order_id in (1, 2)같이 in절로 한방에 조회가 된다.
  • 1:N DTO조회 역시 먼저 xToOne을 fetch join으로 가져오고, N은 for문을 통해 jpql로 또 가져온다.
  • OSIV
    • Open Session In View로 Sesion은 하이버네이트 초기 시절의 EntityManager다. 즉 Open EntityManager In View와 유사한 의미다.
    • 트랜잭션이 맺어지는 시점(@Transactional setAutoCommit(false))에 em과 db커넥션이 연결됨
    • 일반적으로 트랜잭션 종료시점에 커넥션을 반환하지만, OSIV 옵션이 true(default)면 고객에게 반환되는 시점까지 커넥션이 유지된다.
      • LAZY로딩을 트렌젝션 종료 후 할 수도 있기 때문에 이 옵션이 켜져있는 것이다. DB커넥션이 되어있어야 LAZY로딩이 가능하기 때문.
    • OSIV를 키는 것은 장단점이 있다.
      • 우선 DB 커넥션을 오래 물고 있을 수 있기 때문에 성능이 중요한 실시간 사용자 서비스에는 맞지 않는다.
      • 어드민처럼 동시접속자가 적은 곳은 키고 사용해도 괜찮다. 이 경우 LAZY로딩 코드를 트랜잭션 범위에 넣지 않아도 된다.
  • Spring Data JPA 간단 소개
    • 중복발생이 많이 되는 기본적인 쿼리 메소드(findAll(), findOne(), find() 등)을 따로 인터페이스로 만들어두어 사용할 수 있는 라이브러리(아래 코드 예시 참고)
    • JpaRepository<참고할 테이블, 테이블ID 타입>
    • 기본적인 쿼리 메소드를 제외하고는 따로 만들어줘야 함. 그러나 룰이 있기 때문에 룰을 지키면 아래처럼 findByName같은 메소드를 만들어 쓸 수 있다.
public interface MemberRepository extends JpaRepository<Member, Long> {
    //select m from Member m where m.name = :?
    List<Member> findByName(String name);
}
  • QueryDsl 소개
    • 복잡한 JPQL을 자바 코드처럼 짤 수 있음(아래 코드 참고)
    • build.gradle설정 꽤 많이 들어감..
    • 설정후 compileQueryDsl을 하면 build/generated하위에 QMember, QAddress 등 전용 엔티티 생성됨
    • 이후 리포지토리 등에서 사용하면 됨
    • 장점
      • 컴파일 시점에 문법 오류를 잡아줌
      • 재사용성(리팩토링 용이)
      • 동적쿼리 만들기 좋음
      • 직관적
public List<Order> findAll(OrderSearch orderSearch) {
        JPAQueryFactory query = new JPAQueryFactory(em);
        QOrder order = QOrder.order;
        QMember member = QMember.member;

        return query
                .select(order)
                .from(order)
                .join(order.member, member)
                .where(StringUtils.isEmpty(orderSearch.getOrderStatus()) ? null : order.status.eq(orderSearch.getOrderStatus()), StringUtils.isEmpty(orderSearch.getMemberName()) ? null : member.name.like(orderSearch.getMemberName()))
                .limit(1000)
                .fetch();
    }

 

3. 마치며

 

JPA강의를 다시 제대로 들어야지 라고 하면서 미루고 미루다가 올해 활용2까지 마무리할 수 있어서 다행인 것 같습니다.

요즘 사실 JPA가 대부분 자바,스프링 환경에서 필수적으로 쓰이고 있어서 레거시한 환경을 운영하는 제 입장에서는 실무에서 JPA를 아직 못쓰고 있어서 아쉬움이 많았는데, 강의를 통해 어느정도 감을 잡을 수 있었습니다.

 

물론 실무에 적용하면 다른 얘기가 되겠지만, JPA를 사용할 기회가 올 때 어느정도 방향성을 잡고 나갈 수 있을 것 같습니다. 내년에 회사에서 기능 적용을 작은 것부터라도 해볼 예정입니다.

 

감사합니다!

반응형