문제 상황
리뷰 기능을 구현하면서
식당별 총 리뷰 개수를 함께 조회해야 하는 요구사항이 있었다.
초기에는 리뷰 목록을 조회하면서
리뷰 개수를 함께 계산하는 방식으로 처리할 수 있었다.
하지만 이 방식은 다음과 같은 문제가 있었다.
- 리뷰 목록 조회 시마다 리뷰 개수를 다시 계산해야 함
- 리뷰 데이터가 많아질수록 처리 비용이 증가
- 동일한 요청이 반복될 경우 불필요한 DB 접근 발생
또한 리뷰 작성이 동시에 발생하는 경우
- 여러 요청이 같은 시점에 처리되면서
- 리뷰 개수 값이 정확하게 증가하지 않을 가능성도 존재했다.
즉,
조회 성능 문제 + 동시성 문제가 동시에 존재하는 상황이었다.
원인 분석
문제의 핵심 원인은 두 가지였다.
1. 조회 시마다 계산하는 구조
리뷰 개수를 매번 계산하는 방식은
- 요청이 들어올 때마다 추가 연산이 발생하고
- 데이터가 많아질수록 비용이 계속 증가한다.
조회가 많아질수록 성능에 부담이 되는 구조였다.
2. 동시성 제어가 없는 증가 로직
리뷰 생성 시 단순히
totalReviewCount++;
와 같이 처리할 경우
- 동시에 여러 요청이 들어오면
- 일부 증가 값이 반영되지 않을 수 있다.
즉, 데이터 값의 정확성이 깨질 수 있는 구조였다.
해결 방법
다음과 같은 방식으로 구조를 개선하였다.
1. 집계 컬럼 도입
store 테이블에 totalReviewCount 컬럼을 추가하여
리뷰 개수를 별도로 저장하도록 변경하였다.
조회 시 계산 없이 바로 값을 사용할 수 있도록 개선
2. 리뷰 생성 시 값 직접 반영
Store store = storeRepository.findByIdWithPessimisticLock(storeId)
.orElseThrow(...);
store.increaseReviewCount();
public void increaseReviewCount() {
this.totalReviewCount++;
}
3. 비관적 락 적용
동시성 문제를 해결하기 위해
- 식당 조회 시 비관적 락을 적용하여
- 동시에 하나의 요청만 해당 데이터를 수정할 수 있도록 제한하였다.
이를 통해 리뷰 개수 값이 정확하게 반영되도록 보장하였다.
결과
구조 변경 이후 다음과 같은 개선 효과를 얻을 수 있었다.
- 리뷰 개수를 조회할 때 추가 계산이 필요 없어짐
- 불필요한 DB 접근 감소
- 조회 성능 개선
또한
- 동시성 문제를 방지하여
- 리뷰 개수 값이 정확하게 유지되도록 개선하였다.
느낀 점
이번 작업을 통해
- 단순 조회 로직도 데이터가 많아지면 성능 문제가 될 수 있다는 점
- 동시에 데이터를 수정하는 경우 반드시 동시성 제어가 필요하다는 점
을 이해할 수 있었다.
특히
조회 성능 개선과 데이터 정확성을 함께 고려하는 설계가 중요하다는 것을 배웠다.
'트러블슈팅' 카테고리의 다른 글
| Redis write-back 구조에서 스케줄러와 벌크 처리 개념 혼동 (0) | 2026.03.20 |
|---|---|
| Redis write-back 구조에서 데이터 유실 문제 (0) | 2026.03.19 |
| Redis write-back 구조에서 스케줄러 주기와 벌크 처리 고민 (0) | 2026.03.18 |
| 캐시 적용 후 데이터 정합성 문제 해결 (CacheEvict 적용) (0) | 2026.03.18 |
| 캐시를 적용했는데 왜 첫 조회는 느릴까? (0) | 2026.03.18 |