콘서트 랭킹 개발을 하며 느낀 것 – UseCase를 두지 않기로 한 이유
이 글은 콘서트 예약 시스템 과제 중 Redis 기반 콘서트 랭킹 기능을 직접 구현하면서 설계 과정에서 고민했던 지점과, 그 과정에서 배운 점과 느낀 점을 정리한 기록이다.
시작하며 – 이 고민이 나왔다는 것 자체가
콘서트 랭킹 기능을 구현하다가 이런 생각이 들었다.
“이 기능도 UseCase로 분리해야 하는 거 아닐까?”
예전 같았으면 그냥 구조를 하나 더 만들었을지도 모른다. 하지만 이번에는 멈춰서서 다시 생각하게 됐다.
그리고 나중에 깨달았다.
이 질문이 나온 것 자체가, 설계를 기능 단위가 아니라 책임과 성격 단위로 보기 시작했다는 증거였다.
이 글은 그 고민의 결론을 정리한 것이다.
UseCase는 언제 의미가 생기는가
UseCase 계층은 “있으면 좋은 구조”가 아니다. “필요할 때만 의미가 생기는 구조”에 가깝다.
개발을 하며 정리해본 UseCase가 진짜 힘을 발휘하는 순간은 다음과 같았다.
- 여러 도메인이 하나의 시나리오로 엮일 때
- 트랜잭션 경계가 명확하고 복잡할 때
- 하나의 “행위”가 비즈니스 개념으로 존재할 때
예를 들어 콘서트 예약은 아래와 같은 흐름을 가진다.
1
2
3
4
5
콘서트 예약
├─ 대기열 진입
├─ 좌석 홀드
├─ 결제 요청
├─ 실패 시 롤백
이건 구현을 시작하기 전부터 이미 하나의 이야기다. 그래서 ReserveConcertUseCase 같은 이름이 자연스럽다.
콘서트 랭킹을 구현하며 느낀 점
반면 콘서트 랭킹을 구현하면서 계속 들었던 생각은 이거였다.
“이건 시나리오라기보다는, 기록에 가깝다”
정리해보면 랭킹 기능은 다음 특징을 가진다.
- 점수를 증가시키는 단순한 상태 변경
- 단일 도메인(Ranking)
- 트랜잭션 없음
- 조회 비중이 높음
즉, 도메인 상태를 빠르게 기록하고 조회하는 보조 기능에 가깝다.
여기서 깨달은 점은 하나였다.
모든 기능이 UseCase가 될 필요는 없다는 것
지금 구조를 다시 바라보기
현재 랭킹 기능의 구조는 아래와 같다.
1
2
3
Controller
→ ConcertRankingService
→ RedisConcertRankingRepository
처음에는 “Service가 너무 많은 걸 하는 건 아닐까?”라는 생각도 들었다.
하지만 코드를 다시 읽어보니, 이 Service는 다음 역할만 수행하고 있었다.
- 조회 시 점수 기록
- 랭킹 조회
이건 전형적인 Application Service의 책임이다. 이름만 Service일 뿐, 이미 UseCase처럼 동작하고 있었다.
그래서 결론은 단순했다.
굳이 나누지 않아도, 이미 의도가 드러나는 구조다.
억지로 나누면 생기는 어색함
만약 형식적으로 나눈다면 이런 클래스들이 생길 수 있다.
1
2
IncreaseConcertPopularityUseCase
GetConcertRankingUseCase
하지만 이렇게 나누는 순간 오히려 이런 느낌이 들었다.
- 행위가 너무 잘게 쪼개짐
- 파일 수만 늘어남
- 읽을 때 맥락이 더 흐려짐
특히 이번 과제의 핵심은
- Redis 자료구조 선택(ZSET)
- 동시성 및 성능 고려
에 있었기 때문에, 구조를 복잡하게 만드는 것은 본질에서 벗어난다고 느꼈다.
리뷰에서 받게 될 질문을 먼저 생각해보기
개발을 하면서 항상 스스로에게 던진 질문이 있다.
“이걸 왜 이렇게 설계했는지 설명할 수 있는가?”
이번 구조에 대해서는 이렇게 설명할 수 있다.
콘서트 랭킹은 단일 도메인에서 단순 점수 증가와 조회만 수행하는 기능이기 때문에 별도의 UseCase 계층을 두기보다는 Application Service가 그 역할을 수행하도록 설계했습니다.
예약이나 대기열처럼 여러 도메인이 엮이는 시점에서는 UseCase를 분리할 계획입니다.
설계를 했다는 느낌은, 결국 설명 가능성에서 나온다고 느꼈다.
그럼 언제 바꿀까?
지금 구조가 영원히 맞는 건 아니다.
개발을 하며 자연스럽게 이런 기준이 생겼다.
- 랭킹 점수가 여러 이벤트에 의해 계산되기 시작하면
- Kafka Consumer 등 비동기 흐름이 붙으면
- DB와 Redis를 동시에 갱신해야 하면
- 실패 시 보상 로직이 필요해지면
그때는 망설임 없이
1
UpdateConcertRankingUseCase
로 승격시키는 게 맞다.
마무리하며
이번 콘서트 랭킹 개발을 통해 가장 크게 배운 건 이거다.
설계는 구조를 추가하는 능력이 아니라, 추가하지 않을 수 있는 판단력이다.
UseCase를 두지 않은 선택은 “간단히 가자”가 아니라, 문제의 성격에 맞게 간 결과였다.
그리고 다음에 더 복잡해질 준비가 되어 있다는 점에서, 지금 이 구조는 충분히 좋은 출발점이라고 느낀다.