ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot 고급 시리즈 5화 – JPA 설정 최적화 전략: 성능을 결정짓는 기본기
    기술과 산업/언어 및 프레임워크 2025. 6. 16. 18:33
    728x90

    Spring Boot에서 JPA를 사용할 때 반드시 알아야 할 설정 최적화 전략을 정리합니다. DDL 자동 생성, 지연 로딩, N+1 문제, 배치 사이즈, 식별자 전략 등 실무에서 성능과 안정성을 좌우하는 핵심 개념을 상세히 설명합니다.

     

    도입 – 왜 JPA 설정은 ‘기본기’ 이상이어야 하는가?

     

    Spring Boot에서 spring-boot-starter-data-jpa를 사용하는 것은 간단합니다. 그러나 그 설정을 ‘기본값’ 그대로 사용하는 것은 매우 위험합니다.

    엔티티의 설계와 JPA의 설정은 시스템의 성능, 확장성, 유지보수성을 결정짓는 핵심 요소입니다.

     

    이 글에서는 실무에서 반드시 고려해야 할 JPA 설정 최적화 전략을 정리합니다.

     


     

    1. DDL 자동 생성은 개발 환경에서만

     

    Spring Boot의 기본 설정은 다음과 같습니다:

    spring:
      jpa:
        hibernate:
          ddl-auto: create-drop

    이 설정은 애플리케이션이 실행될 때 테이블을 자동으로 생성하고, 종료 시 제거합니다.

     

     

    추천 전략

     

    • 개발 환경: create, update, 또는 create-drop 사용
    • 운영 환경: 반드시 none 사용
    spring:
      jpa:
        hibernate:
          ddl-auto: none

    운영 환경에서는 테이블 변경을 JPA에 맡기지 말고, Flyway 또는 Liquibase를 통한 버전 기반 마이그레이션 도구를 사용해야 합니다.

     


     

    2. 지연 로딩(LAZY)과 즉시 로딩(EAGER) – 언제 어떻게?

     

    JPA 관계 설정에서 가장 큰 성능 함정은 @ManyToOne(fetch = FetchType.EAGER)입니다.

    기본값이 EAGER이기 때문에, 관계가 있는 모든 데이터를 쿼리 한 번에 가져오게 되며, 의도치 않은 N+1 문제가 발생할 수 있습니다.

     

     

    추천 전략

     

    • 모든 연관 관계는 명시적으로 fetch = FetchType.LAZY 선언
    • 즉시 로딩이 반드시 필요한 경우에만 EAGER 사용
    • 조회 최적화는 fetch join이나 DTO 매핑으로 해결
    @ManyToOne(fetch = FetchType.LAZY)
    private Member member;

     


     

    3. N+1 문제의 진단과 해결

     

     

    문제 발생 예

    List<Order> orders = orderRepository.findAll();
    for (Order order : orders) {
        System.out.println(order.getMember().getName());
    }

    → 위 코드는 Order 1건당 1개의 Member 쿼리를 발생시켜 총 N+1회 DB 접근

     

     

    해결 방안

     

    1. Fetch Join
    @Query("SELECT o FROM Order o JOIN FETCH o.member")
    List<Order> findAllWithMember();

     

    1. EntityGraph 활용
    @EntityGraph(attributePaths = {"member"})
    List<Order> findAll();

     

    1. DTO Projection

     

    엔티티가 아닌 DTO에 직접 쿼리 결과를 매핑해 쿼리를 최적화

     


     

    4. 배치 사이즈 조정 – 지연 로딩 성능 향상

     

    JPA는 지연 로딩 시 각 연관 엔티티를 하나씩 조회합니다. 이때 hibernate.default_batch_fetch_size를 설정하면 IN 절을 이용해 묶음 조회가 가능합니다.

    spring:
      jpa:
        properties:
          hibernate:
            default_batch_fetch_size: 100

     

    • 100개 단위로 묶어서 SQL 1회에 조회
    • 특히 OneToMany, ManyToOne 관계의 컬렉션 조회 성능 향상

     


     

    5. 식별자 생성 전략 – 어떤 전략을 선택해야 하나?

    전략설명주의사항

    IDENTITY DB가 자동 생성 즉시 INSERT 필요. 배치 처리에 불리
    SEQUENCE 시퀀스 객체 사용 오라클, PostgreSQL에서 성능 우수
    TABLE 테이블 기반 키 관리 성능 낮음, 대부분 비추천
    UUID 고유 ID 생성 정렬/검색 비효율적, DB 인덱스 부담

     

    추천 전략

     

    • PostgreSQL, Oracle: SEQUENCE 사용 + allocationSize 튜닝
    • MySQL: IDENTITY 사용 (그러나 배치 쓰기엔 비효율적)
    • 분산 시스템: UUID 또는 외부 ID 서버 전략 적용

     


     

    6. 읽기 전용 쿼리에서는 readOnly 설정

     

    JPA에서는 불필요한 dirty checking을 방지하기 위해 @Transactional(readOnly = true) 설정이 유용합니다.

    쿼리의 목적이 단순 조회일 경우, 영속성 컨텍스트에서 변경 감지를 생략하여 성능을 향상시킬 수 있습니다.

    @Transactional(readOnly = true)
    public List<Member> findAllMembers() {
        return memberRepository.findAll();
    }

     


     

    기본값은 결코 기본이 아니다

     

    JPA는 강력하지만, 기본값에 의존하면 언제나 성능 문제와 유지보수 비용의 함정에 빠지기 쉽습니다.

    이번 글에서 다룬 설정 항목들은 단순한 체크리스트가 아니라, 개발 초기 단계에서 반드시 명확히 결정되어야 할 전략입니다.

     

    잘 정리된 설정은 프로젝트의 안정성과 성능을 담보하는 근간이 됩니다.

    728x90
Designed by Tistory.