cd ..

Memory of Year

7인 팀 프로젝트에서 백엔드 리드로 API·배포 기준을 잡고, 편지 목록 N+1을 31→1로 줄여 p95를 40% 낮췄습니다.

31→1
편지 목록 쿼리

@Query 최적화로 목록 조회 N+1을 단일 쿼리로 정리

40%
편지 목록 p95

22.8ms에서 13.8ms로 낮춘 조회 응답 시간

143.7 RPS
앨범 API

k6 부하테스트에서 에러율 0%로 확인한 처리량

Java 17Spring Boot 3Spring SecurityJWTSpring Data JPAMySQLH2AWS S3SwaggerDockerGitHub Actionsk6JaCoCo
Key Result

편지 목록 API: 쿼리 31회 → 1회, p95 22.8ms → 13.8ms(↓40%), 평균 11.5ms → 6.7ms(↓42%) 개선.

개요

7인 팀 프로젝트에서 가장 먼저 풀어야 했던 문제는 편지 목록과 앨범 조회의 N+1 쿼리였습니다. 백엔드 리드로 인증, 앨범·편지·사진 API, S3 업로드, Docker 기반 개발 환경을 설계했고, @Query와 @EntityGraph 최적화로 편지 목록 p95를 40% 낮췄습니다. DTO와 API 구조도 함께 정리해 팀원들이 같은 기준으로 기능을 확장할 수 있게 만들었습니다.

백엔드 리드 역할

  • 전체 API 설계 및 백엔드 아키텍처 구성 (14개 DTO, 6개 Controller)
  • 앨범·편지·사진 CRUD, JWT 인증(토큰 블랙리스트 로그아웃) 구현
  • AWS S3 연동 이미지 업로드 파이프라인 구축
  • N+1 문제 해결 및 k6 부하 테스트로 Before/After 성능 측정
  • 팀원 3명에게 JPA 연관관계, 쿼리 최적화, 인덱스 설계 코드 리뷰 진행

성능 최적화

  • 편지 목록: N+1(31쿼리) → @Query 서브쿼리 COUNT로 1쿼리 최적화
  • 앨범 조회: Lazy 로딩 3쿼리 → @EntityGraph로 1쿼리
  • 인덱스 4개 추가: idx_album_owner, idx_letter_album_id, idx_letter_created_at, idx_photo_letter_id
  • k6 테스트: 회원가입 16.5 RPS, 로그인 49.7 RPS, 앨범 143.7 RPS, 편지 96.7 RPS

인프라 구성

  • Docker Compose: MySQL 8 + Spring Boot, healthcheck 기반 순차 기동
  • GitHub Actions CI: 자동 테스트 + JaCoCo 커버리지 리포트 생성
  • 환경 변수로 N+1 Before/After 재현 가능 (APP_PERF_USE_N1_LETTERS)
  • Swagger/OpenAPI 3.0 API 문서화

아키텍처

전체 아키텍처

ERD

전체 ERD

시퀀스 다이어그램

로그인
앨범 생성
편지 작성
사진 업로드

문제 원인

  1. 01편지 목록 조회 시 편지별 사진 개수를 위해 N+1 쿼리(1 + 30회 = 31회)가 발생해 p95 22.8ms로 응답이 느렸습니다.
  2. 02앨범 상세 조회 시 owner, letters를 Lazy 로딩해 3회 쿼리가 발생했습니다.
  3. 03성능 개선 전·후를 정량적으로 비교할 부하 테스트 환경이 없었습니다.
  4. 04팀원마다 로컬 MySQL 설정이 달라 환경 불일치 문제가 있었습니다.

해결 과정

  1. 01LetterRepository에 @Query로 서브쿼리 COUNT를 사용한 findLettersWithPhotoCountByAlbumId를 작성해 1회 쿼리로 최적화했습니다.
  2. 02AlbumRepository에 @EntityGraph(attributePaths = {"owner", "letters"})를 적용한 findByIdWithOwnerAndLetters로 JOIN FETCH 효과를 구현했습니다.
  3. 03k6 스크립트 5개(스모크, 회원가입, 로그인, 앨범, 편지)를 작성하고, APP_PERF_USE_N1_LETTERS 환경변수로 최적화 전·후를 재현해 비교 측정했습니다.
  4. 04Docker Compose로 MySQL 8 + Spring Boot 앱을 정의하고, depends_on + healthcheck(mysqladmin ping)로 안정적인 순차 기동을 구현했습니다.

결과

  1. 01편지 목록 API: 쿼리 31회 → 1회, p95 22.8ms → 13.8ms(↓40%), 평균 11.5ms → 6.7ms(↓42%) 개선.
  2. 02앨범 조회 API: 3회 → 1회 쿼리, 4개 인덱스(idx_album_owner, idx_letter_album_id, idx_letter_created_at, idx_photo_letter_id) 추가.
  3. 03k6 부하테스트 결과: 회원가입 16.5 RPS(p95 115ms), 로그인 49.7 RPS(p95 116ms), 앨범 143.7 RPS(p95 12.9ms), 편지 96.7 RPS(p95 13.8ms), 모두 에러율 0%.
  4. 04GitHub Actions CI에서 push 시 자동 테스트 + JaCoCo 커버리지 리포트 생성, 아티팩트 7일 보관.