들어가며
이전에 PlaylistTrack 순서 변경 구조를 아래와 같이 개선하였다.
전체 재정렬 방식 → 영향 범위만 갱신하는 부분 재정렬 방식
2026.04.23 - [Fivefy 프로젝트/트러블슈팅] - PlaylistTrack 순서 변경 최적화 전체 재정렬을 부분 재정렬로 개선
PlaylistTrack 순서 변경 최적화 전체 재정렬을 부분 재정렬로 개선
문제 상황PlaylistTrack의 순서 변경 기능을 구현하면서, 초기에는 트랙 순서를 변경할 때마다 전체 트랙의 position을 재정렬하는 방식을 사용하였다.private void reorderPositions(List playlistTracks) { for (int i
sudaruuu.tistory.com
당시에는 전체 재정렬로 인해 발생하던 불필요한 update 문제를 줄이고, 영향 범위만 갱신하는 구조로 변경하는 데 집중했다.
하지만 구조 개선 이후에도 실제로 다음과 같은 부분은 검증하지 못한 상태였다.
- 트랙 수 증가에 따라 응답 시간이 어떻게 변화하는지
- 실제 update 범위가 어느 정도 발생하는지
- worst-case 상황에서는 어떤 특성을 가지는지
- 동시 reorder 요청에서도 안정적으로 동작하는지
이번에는 단순히 “구조를 개선했다”에서 끝나는 것이 아니라, 실제 성능과 동작을 직접 측정하면서 구조를 검증해보았다.
검증이 필요했던 이유
부분 재정렬 방식은 전체 재정렬 대비 불필요한 update를 줄일 수 있다는 장점이 있다.
하지만 실제로는 다음과 같은 특성이 존재한다.
- 이동 범위에 따라 update 수가 달라질 수 있음
- 데이터 규모가 커질수록 응답 시간 증가 가능
- 동시 reorder 요청 시 충돌 가능성 존재
특히 PlaylistTrack은 (playlist_id, position) 유니크 제약을 사용하고 있기 때문에, 순서 변경 과정에서 데이터 정합성과 동시성 안정성이 중요했다.
따라서 단순 구현 수준이 아니라 실제 수치 기반으로 구조를 검증해보기로 했다.
측정 기준
다음 기준으로 reorder 성능을 측정하였다.
측정 항목
- 트랙 수 100 / 1,000 / 10,000 기준
- 평균 응답 시간
- p95 응답 시간
- 실행 update 쿼리 범위
- 동시 reorder 요청 충돌 여부
테스트 방식
순서 변경 시나리오
worst-case 상황을 기준으로 테스트를 진행하였다.
마지막 트랙 → 가장 앞 순서로 이동
예시
10000 → 1 이동
이 경우 부분 재정렬 구조에서도 영향 범위가 가장 크게 발생한다.
즉, 가장 많은 update가 발생할 수 있는 상황이다.
성능 테스트 코드
테스트는 실제 DB 환경 기반으로 @SpringBootTest에서 진행하였다.
for (int i = 0; i < MEASURE_COUNT; i++) {
PlaylistTrackOrderUpdateRequest request =
new PlaylistTrackOrderUpdateRequest(targetTrackId, 1);
long start = System.nanoTime();
playlistTrackService.updateTrackOrder(
USER_ID,
playlist.getId(),
request
);
long end = System.nanoTime();
times.add((end - start) / 1_000_000);
}
반복 수행 후 다음 값을 계산하도록 구성하였다.
- 평균 응답 시간
- p95 응답 시간
또한 동시에 reorder 요청을 수행하여 충돌 여부도 함께 검증하였다.
성능 측정 결과
트랙 수 100
- 평균 응답 시간: 5.7ms
- p95 응답 시간: 5ms
트랙 수 1,000
- 평균 응답 시간: 19.7ms
- p95 응답 시간: 17ms
트랙 수 10,000
- 평균 응답 시간: 191.4ms
- p95 응답 시간: 79ms

또한 동시 reorder 요청 테스트에서도 예외 없이 정상 동작하는 것을 확인하였다.

결과 분석
트랙 수가 증가할수록 응답 시간도 함께 증가하는 것을 확인할 수 있었다.
특히 다음과 같은 worst-case 상황에서는 영향 범위 자체가 매우 넓기 때문에 응답 시간이 증가하였다.
10000 → 1 이동
다만 중요한 점은 부분 재정렬 구조가 “항상 update가 적은 구조”가 아니라,
"영향받는 데이터만 update하는 구조"라는 점이었다.
즉, 이동 범위가 작은 일반적인 reorder 상황에서는 불필요한 update를 줄일 수 있지만, worst-case에서는 영향 범위가 커지면서 update 수도 함께 증가할 수 있다.
실행 쿼리 분석
SQL 로그를 통해 실제 update 범위도 확인하였다.
update playlist_tracks
set position=?
where id=?
로그 분석 결과 이동 범위에 비례하여 update 쿼리 수가 증가하는 것을 확인할 수 있었다.
즉 현재 구조는 “전체 재정렬 제거” 자체가 목적이 아니라, 영향받는 범위만 최소화하는 방향에 가까웠다.
동시성 테스트
동시에 reorder 요청을 수행하는 테스트도 함께 진행하였다.
ExecutorService executorService = Executors.newFixedThreadPool(2);
Runnable task1 = () -> {
playlistTrackService.updateTrackOrder(...);
};
Runnable task2 = () -> {
playlistTrackService.updateTrackOrder(...);
};
동시 reorder 요청 상황에서도 충돌 없이 안정적으로 동작하는 것을 확인하였다.
느낀 점
이번 테스트를 통해 부분 재정렬 구조를 단순 구현 수준이 아니라 실제 수치 기반으로 검증해볼 수 있었다.
특히 다음과 같은 부분을 직접 확인할 수 있었다.
- 트랙 수 증가에 따른 응답 시간 변화
- 이동 범위에 따른 update 범위 변화
- p95 응답 시간 측정
- 동시 요청 상황에서의 안정성
또한 이번 검증을 통해 부분 재정렬 구조의 핵심은 "전체 재정렬 제거"가 아니라, "영향받는 데이터만 갱신"하는 구조라는 점을 다시 확인할 수 있었다. 단순히 기능이 동작하는 수준에서 끝나는 것이 아니라, 실제 데이터 흐름과 성능 특성을 함께 검증해보면서 구조를 바라보는 관점도 조금 더 넓어질 수 있었다.
'Fivefy 프로젝트 > 성능 개선' 카테고리의 다른 글
| 기능 구현에서 설계까지 음악 플랫폼 도메인 설계 경험 정리 (6) | 2026.04.23 |
|---|---|
| 최신 snapshot 조회 로직 개선과 DB 정렬 기반 조회 (1) | 2026.04.16 |