Fivefy 프로젝트/트러블슈팅

유효 재생 기준 적용과 Projection 기반 집계 구조 개선 과정

sudaruuu 2026. 4. 21. 17:12

들어가며

이전 글에서 Playback과 PopularChart를 구현하면서 재생 데이터를 단순히 count하는 것이 아니라, 의미 있는 기준으로 집계할 필요가 있다는 점을 정리했다.

 

이번 글에서는 그때 정의했던 유효 재생 기준을 실제 차트 집계 로직에 적용하는 과정과, 그 과정에서 발생한 문제를 어떻게 해결했는지 정리해보려고 한다.


문제 상황

기존 인기 차트는 snapshotDate를 기준으로 지난 1주일 간의 Playback 데이터를 조회하여 track별 재생 횟수(count) 기반으로 집계되고 있었다.

 

이 구조에서는 다음과 같은 문제가 있었다.

  • 짧게 재생하고 바로 종료해도 동일하게 count됨
  • 동일 세션에서 반복 재생이 그대로 반영됨

즉, 단순 재생 횟수만으로는 데이터 신뢰도를 보장할 수 없는 구조였다.
이를 해결하기 위해 다음과 같은 유효 재생 기준을 정의하였다.

  • playedDuration이 30초 이상인 경우만 집계
  • 동일 sessionId 내 같은 track은 1회만 집계

하지만 이 기준을 실제 집계 로직에 반영하는 과정에서 기존 테스트 코드가 모두 깨지는 문제가 발생하였다.


유효 재생 기준 적용

Playback 데이터를 그대로 count하는 대신,조건을 만족하는 데이터만 집계하도록 쿼리를 수정하였다.

  • playedDuration >= 30초
  • 동일 sessionId + trackId는 1회만 인정
  • 종료 상태(STOPPED, SKIPPED, COMPLETED)만 포함

이 기준을 적용하면서 단순히 많이 재생된 곡이 아니라 의미 있게 재생된 곡을 기준으로 차트를 생성할 수 있게 되었다.


Projection 도입

집계 결과에서 실제로 필요한 데이터는 다음 두 가지였다.

  • trackId
  • playCount

따라서 불필요한 컬럼 조회를 줄이기 위해 Projection 기반으로 조회 구조를 변경하였다.

public interface TrackPlayCountProjection {
    Long getTrackId();
    Long getPlayCount();
}

 

이 방식으로 변경하면서

  • 필요한 데이터만 조회할 수 있고
  • 불필요한 Entity 생성이 줄어들며
  • 조회 구조도 단순해지는 효과를 얻을 수 있었다.

테스트 코드 작동 문제

Projection을 적용한 이후, 기존 테스트 코드가 모두 동작하지 않았다.

기존에는 DTO를 사용하고 있었기 때문에 테스트에서 객체를 직접 생성할 수 있었다.

new TrackPlayCountDto(101L, 300L)

 

하지만 Projection은 인터페이스 기반이기 때문에 이렇게 직접 객체를 생성할 수 없다.

JPA가 쿼리 실행 시점에 구현체를 만들어 반환하는 구조이기 때문에 테스트 환경에서는 해당 객체를 사용할 수 없는 상태였다.


해결 방법

결국 테스트에서는 Projection을 직접 생성할 수 없기 때문에 Mockito를 사용하여 mock 객체로 대체하였다.

TrackPlayCountProjection projection = mock(TrackPlayCountProjection.class);

when(projection.getTrackId()).thenReturn(101L);
when(projection.getPlayCount()).thenReturn(300L);

 

반복되는 코드가 많아 헬퍼 메서드로 분리하였다.

private TrackPlayCountProjection mockProjection(Long trackId, Long playCount) {
    TrackPlayCountProjection projection = mock(TrackPlayCountProjection.class);
    lenient().when(projection.getTrackId()).thenReturn(trackId);
    lenient().when(projection.getPlayCount()).thenReturn(playCount);
    return projection;
}

 

이렇게 수정하면서 Projection 기반 구조에서도 테스트를 안정적으로 유지할 수 있게 되었다.


정리

이번 작업은 단순히 기능을 추가하는 작업이 아니라, 집계 기준, 조회 구조, 테스트 코드까지 함께 수정이 필요한 작업이었다.

유효 재생 기준을 적용하면서 Playback 데이터를 그대로 사용하는 것이 아니라 의미 있는 재생만을 기준으로 집계하도록 로직을 변경하였다. 또한 집계에 필요한 데이터만 조회하도록 Projection을 도입하여 불필요한 Entity 조회를 줄이고 조회 구조를 단순화하였다.

이 과정에서 Projection의 특성으로 인해 기존 테스트 코드가 동작하지 않는 문제가 발생했고, 이를 mock 객체를 활용하는 방식으로 해결하였다.


느낀 점

유효 재생 기준을 정의하는 것 자체는 어렵지 않았는데, 이걸 실제 집계 로직에 적용하고 테스트까지 맞추는 과정에서 생각보다 많은 부분이 함께 수정되어야 했다.

 

특히 Projection을 적용하면서 조회 구조는 더 깔끔해졌지만,ㅡ그에 맞게 테스트 코드도 같이 바꿔줘야 한다는 점이 기억에 남는다. 이번 경험을 통해 기능 하나를 변경하더라도 그 영향이 어디까지 퍼지는지 함께 고려하는 게 중요하다는 걸 알게 되었다.