Spring Boot, JPA, Querydsl을 활용하여 복잡한 도메인(회원, 상품, 주문)의 요구사항을 구현하고, N+1 문제 해결 및 대용량 트래픽을 고려한 쿼리 성능 튜닝(최적화)을 진행한 RESTful API 서버입니다.
- Language: Java
- Framework: Spring Boot, Spring Data JPA, Querydsl
- Database: MySQL, H2
단순한 CRUD를 넘어 실무에서 마주하는 JPA의 성능 이슈와 동적 쿼리의 한계를 극복하는 데 초점을 맞추었습니다. 복잡한 테이블 조인과 일대다(1:N) 컬렉션 조회 시 발생하는 치명적인 성능 저하 원인을 분석하고, 이를 리팩토링 과정을 거쳐 튜닝했습니다.
- 회원(Member), 상품(Item), 주문(Order) 도메인의 복잡한 비즈니스 로직을 분석하고, 객체지향적인 엔티티 설계 및 테이블 매핑 진행
- 엔티티가 직접 외부에 노출되어 스펙이 변경되는 것을 방지하기 위해 API 응답용 DTO를 별도로 설계하여 유지보수성과 안전성 확보
- 순수 JPA 로직을 Spring Data JPA로 전환하며 공통화된 CRUD 인터페이스 적용
- 실무에서 불필요하거나 위험할 수 있는 자동화 기능은 배제하고, 꼭 필요한 기능만 선별하여 안정적인 데이터 접근 계층(Data Access Layer) 구축.
- 이슈: 주문 목록을 조회할 때, 회원과 상품 정보(컬렉션 데이터)를 지연 로딩으로 가져오면서 수많은 단건 쿼리가 폭주하는 N+1 문제 및 성능 저하 발생.
- 해결 및 튜닝 과정 (6단계 리팩토링):
- 엔티티 직접 노출: 문제점 파악 (순환 참조, 스펙 노출)
- DTO 변환: API 스펙 안정화, 여전히 N+1 문제 존재
- Fetch Join 적용: 일반적인 다대일(N:1) 관계의 쿼리를 한 번의 조인 쿼리로 최적화
- JPA 컬렉션 한계 돌파: 일대다(1:N) 페이징 처리 시 발생하는 OOM 위험 분석
hibernate.default_batch_fetch_size적용: 컬렉션 조회를 페이징 가능하도록 유지하면서, 쿼리 발생 횟수를 1번의 IN 쿼리로 획기적으로 단축- DTO 직접 조회: 화면에 최적화된 복잡한 쿼리를 별도의 DTO로 한 번에 조회하도록 튜닝하여 극한의 성능 확보
- 이슈: 회원 상태나 주문 기간 등 다양한 검색 조건이 조합되는 복잡한 동적 쿼리를 순수 JPQL이나 Criteria로 작성 시, 가독성이 현저히 떨어지고 런타임 에러 발생 위험이 높음
- 해결: Querydsl을 도입하여 자바 코드로 SQL을 작성
- 컴파일 시점에 문법 오류를 100% 잡아내어 런타임 안정성 보장
- 조건문 분기를 통한 깔끔한 동적 쿼리 작성으로 복잡한 실무 검색 로직 해결
- 코드가 직관적이고 재사용이 가능해져 비즈니스 로직 구현에만 집중할 수 있는 환경 구축
- 데이터베이스 커넥션이 뷰(View)나 API 응답 끝까지 유지되면서, 커넥션 풀이 말라버려 장애가 발생하는 OSIV 개념을 이해
- 실무 환경의 트래픽을 고려하여 OSIV를 적절히 끄고, 트랜잭션 범위 안에서 강제 초기화를 진행하는 명확한 아키텍처 설계 지향