중복 생성 제거로 티저 성능 개선
목차
src/app/api/reading/route.ts 와 src/lib/reading.ts 를 손봤다. 사용자가 콘텐츠를 조회할 때마다 생성되던 티저(미리보기)를 백그라운드에서 선생성하도록 바꾸고, 동일 사용자가 동시에 여러 요청을 보낼 때 중복 생성이 일어나지 않도록 uid 기준의 락을 추가한 작업이다.
문제: 동시 요청의 중복 생성
한 사용자가 짧은 시간 안에 여러 콘텐츠를 연달아 본다고 생각해보자. 또는 느린 네트워크 환경에서 새로고침을 여러 번 하거나, 탭을 동시에 여는 경우. 각 요청이 독립적으로 티저를 생성하려 하면:
- 동일한 사용자 ID(uid)에 대해 동시에 여러 생성 작업이 시작됨
- 각각이 리소스(DB 쿼리, 렌더링, 캐시 갱신)를 따로따로 소비하며 중복 작업 수행
- 응답 시간도 길어지고 시스템 부하도 증가
"이미 생성 중이라면 기다리되, 같은 요청을 또 시작하지는 말자"는 동시성 제어의 기본 원칙이다. 그런데 이 부분이 빠져 있거나 불완전하면 조용히 리소스가 낭비된다. 버그처럼 눈에 띄지 않고, 모니터링을 통해서야 비로소 "뭔가 리소스가 비효율적으로 쓰인다"는 걸 포착하게 된다.
해결: 백그라운드 선생성 + uid별 동시성 제어
두 가지 개선을 합쳤다:
| 기법 | 목적 | 효과 |
|---|---|---|
| 백그라운드 선생성 | 조회 요청이 들어올 때까지 기다리지 말고 미리 준비해두기 | 응답 지연 제거, 사용자는 즉시 데이터 획득 |
| uid별 dedupe 락 | 같은 사용자 ID에 대해 동시 생성 작업 시작 방지 | 중복 리소스 소비 제거, 시스템 부하 감소 |
백그라운드 프로세스가 주기적으로(또는 이벤트 기반으로) 사용자별 티저를 미리 생성해두고, API 요청이 들어올 때는:
사용자 요청 → 락 획득(uid 기준) →
(이미 생성? → 반환) / (생성 중? → 대기) / (미생성? → 조회 후 생성)
→ 락 해제 → 응답
이렇게 하면 누가 먼저 끝내든 다른 요청들은 그 결과를 재사용한다. 같은 사용자의 10개 동시 요청도 결국 1번의 생성 작업만 수행하면 된다.
설계 선택: 왜 이 방식인가
팀에서 논의했던 트레이드오프들:
- 락 수준: 전역 락 vs. uid별 락
- 전역 락은 구현이 단순하지만 사용자 A의 생성이 사용자 B를 막는 문제
- uid별 락은 복잡도가 약간 올라가지만 각 사용자가 독립적으로 동시에 처리됨
-
선택: uid별 락. 시스템 규모가 커질수록 이 차이가 성능에 크게 영향
-
백그라운드 스케줄: 얼마나 자주?
- 너무 자주 → 리소스만 낭비
- 너무 드물게 → 선생성의 의미 없음
-
선택: 사용 패턴 분석 또는 사용자 활동 감지 시 트리거
-
락 대기 제한: 무한 대기 vs. 타임아웃
- 무한 대기는 데드락 위험
- 선택: 적절한 타임아웃 + 폴백 처리
코드 구조
src/lib/reading.ts 에 생성 로직의 핵심을, src/app/api/reading/route.ts 에서 락 획득·해제를 관리하도록 분리했다. 이렇게 하면:
- 비즈니스 로직과 동시성 제어가 명확히 구분
- 향후 다른 엔드포인트에서 같은 패턴 재사용 가능
- 락 전략을 바꿀 때도 한 곳만 수정
배운 점
-
동시성 이슈는 조용하다 — 예외가 터지거나 에러가 보이지 않고, 성능 저하나 리소스 낭비로만 드러난다. 모니터링 없이는 포착하기 어렵다.
-
단순함과 확장성의 균형 — 작은 팀이면 전역 락도 충분할 수 있지만, 사용자가 늘어나면서 이런 세밀한 동시성 제어가 전체 성능을 크게 좌우한다.
-
선생성 패턴의 재사용성 — 이 패턴은 캐싱, 미리 계산되어야 하는 데이터, 무거운 렌더링 등 다양한 곳에 적용된다. 비슷한 성능 이슈를 다음에 만나면 같은 접근법을 먼저 시도할 수 있겠다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.