Skip to content

대시보드 페이지에 영상이 여러 개 있을 때 응답 시간이 영상 수에 비례해 선형으로 증가하는 문제 #21

Description

@coom1222

원인 분석

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.javaavgTopPlayerScoreByUserId JPQL 추가
  • config/S3Service.java@Cacheable 적용, 잘못된 URL 방어 처리
  • domain/dashboard/service/DashboardService.java — 집계 쿼리 기반으로 전면 리팩터링

참고

  • Redis 서버가 실행 중이지 않으면 캐시 미스로 처리되어 기존과 동일하게 동작하니 주의.
  • 프론트엔드 API 응답 구조는 변경하지 않음.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions