원인 분석
1. N+1 쿼리 — DashboardService
영상 목록을 전체 조회한 뒤, 각 영상마다 분석 결과를 별도 쿼리로 조회하고 있었음.
// 영상 N개 → DB 쿼리 1 + N번 발생
for (Video v : videos) {
Optional<AnalysisResult> ar = analysisResultRepository.findByVideoVideoId(v.getVideoId());
...
}
2. Presigned URL 직렬 생성 — S3Service
최근 영상 목록(최대 10개)을 렌더링할 때, 영상마다 AWS 서명 호출을 동기로 실행했었음. 이 경우 캐시가 없어 매 요청마다 반복 실행되었습니다.
3. 불필요한 전체 목록 로드
총 영상 수, 총 분석 시간, 평균 점수를 계산하기 위해 삭제되지 않은 영상 전체를 메모리로 로드한 뒤 Java 루프로 집계함.
개선 내용 : N+1 제거 및 집계 쿼리 도입
| 항목 |
이전 |
이후 |
| 총 영상 수 |
전체 목록 로드 후 .size() |
COUNT 쿼리 |
| 총 분석 시간 |
전체 로드 후 Java sum() |
SUM(duration_seconds) JPQL |
| 평균 점수 |
N+1 반복 조회 후 Java 평균 |
AVG(top_player_score) JPQL |
| 최근 영상 목록 |
전체 로드 후 .limit(10) |
PageRequest.of(0, 10) |
이제는 영상 수와 무관하게 고정 4개 쿼리로 처리됨.
Presigned URL Redis 캐싱
S3Service.generatePresignedUrl()에 @Cacheable("presignedThumbnailUrls") 적용
- 캐시 키: 원본 S3 URL
- Redis TTL: 52분 (presigned URL 유효시간 1시간보다 짧게 설정)
- Redis 장애 시 캐시 미스로 처리하여 애플리케이션 전체 중단 방지
변경 파일
build.gradle — Redis, Cache 의존성 추가
application.yml — Redis 연결 설정 (REDIS_HOST, REDIS_PORT 환경변수로 덮어쓰기 가능)
BackendApplication.java — @EnableCaching 추가
config/RedisCacheConfig.java — 신규, 캐시 TTL 및 직렬화 설정
domain/video/repository/VideoRepository.java — Pageable 오버로드, sumDurationSecondsByUserId JPQL 추가
domain/analysis/repository/AnalysisResultRepository.java — avgTopPlayerScoreByUserId JPQL 추가
config/S3Service.java — @Cacheable 적용, 잘못된 URL 방어 처리
domain/dashboard/service/DashboardService.java — 집계 쿼리 기반으로 전면 리팩터링
참고
- Redis 서버가 실행 중이지 않으면 캐시 미스로 처리되어 기존과 동일하게 동작하니 주의.
- 프론트엔드 API 응답 구조는 변경하지 않음.
원인 분석
1. N+1 쿼리 —
DashboardService영상 목록을 전체 조회한 뒤, 각 영상마다 분석 결과를 별도 쿼리로 조회하고 있었음.
2. Presigned URL 직렬 생성 —
S3Service최근 영상 목록(최대 10개)을 렌더링할 때, 영상마다 AWS 서명 호출을 동기로 실행했었음. 이 경우 캐시가 없어 매 요청마다 반복 실행되었습니다.
3. 불필요한 전체 목록 로드
총 영상 수, 총 분석 시간, 평균 점수를 계산하기 위해 삭제되지 않은 영상 전체를 메모리로 로드한 뒤 Java 루프로 집계함.
개선 내용 : N+1 제거 및 집계 쿼리 도입
이제는 영상 수와 무관하게 고정 4개 쿼리로 처리됨.
Presigned URL Redis 캐싱
S3Service.generatePresignedUrl()에@Cacheable("presignedThumbnailUrls")적용변경 파일
build.gradle— Redis, Cache 의존성 추가application.yml— Redis 연결 설정 (REDIS_HOST,REDIS_PORT환경변수로 덮어쓰기 가능)BackendApplication.java—@EnableCaching추가config/RedisCacheConfig.java— 신규, 캐시 TTL 및 직렬화 설정domain/video/repository/VideoRepository.java— Pageable 오버로드,sumDurationSecondsByUserIdJPQL 추가domain/analysis/repository/AnalysisResultRepository.java—avgTopPlayerScoreByUserIdJPQL 추가config/S3Service.java—@Cacheable적용, 잘못된 URL 방어 처리domain/dashboard/service/DashboardService.java— 집계 쿼리 기반으로 전면 리팩터링참고