-
Spring Boot 고급 시리즈 5화 – JPA 설정 최적화 전략: 성능을 결정짓는 기본기기술과 산업/언어 및 프레임워크 2025. 6. 16. 18:33728x90
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 접근
해결 방안
- Fetch Join
@Query("SELECT o FROM Order o JOIN FETCH o.member") List<Order> findAllWithMember();
- EntityGraph 활용
@EntityGraph(attributePaths = {"member"}) List<Order> findAll();
- 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'기술과 산업 > 언어 및 프레임워크' 카테고리의 다른 글
Mac에서 특정 위치 하위의 node_modules 폴더 전부 삭제하기 (0) 2025.06.16 Spring AI 시리즈 8화 – RAG 구축하기 (3): VectorStore 비교와 확장 전략 (2) 2025.06.16 전자정부 표준프레임워크 시리즈 14화 – 전자정부 프레임워크와 마이크로서비스 아키텍처(MSA)의 통합 전략 (1) 2025.06.16 전자정부 표준프레임워크 시리즈 13화 - 공통컴포넌트의 이해와 확장 전략 (1) 2025.06.11 FastAPI 시리즈 14화 - CORS 설정과 보안 헤더 적용하기 (1) 2025.06.09