가상 대기열(Virtual Queue) 트래픽 제어 PRD
Background
전체 시스템 아키텍처 진단이 정의한 진화 2단계다. 1단계([상품·주문 공유 상위 컨텍스트](../도메인 경계 재설계/20260708-상품주문-공유상위컨텍스트-prd.md))가 모놀리스 내부 정리라면, 2단계는 마케팅 이벤트 20,000 TPS 폭주를 앱 앞단에서 흡수하는 유입 제어다.
현재 마케팅 폭주 방어는 [마케팅 이벤트 고부하 대응](../마케팅 이벤트 고부하 대응/PRD.md) 과제로 이미 상당 부분 구축됐다.
LoadSheddingFilter.kt(backend/src/main/kotlin/com/sportsapp/infrastructure/loadshedding/LoadSheddingFilter.kt) — 세마포어(Semaphore) 기반 동시 처리 상한(기본 200건,load-shedding.max-concurrent-requests)을 넘는 요청을 인증·비즈니스 로직 진입 전에 즉시 503으로 거부하는 fast-fail 필터다. 큐잉하지 않는다.DropReservationStoreImpl.kt(backend/src/main/kotlin/com/sportsapp/infrastructure/goods/redis/DropReservationStoreImpl.kt) — 한정판(LimitedDrop) 재고를reserve.lua로 원자 차감하는 Redis Lua 게이트 + 인프로세스 완충 세마포어(기본 200 permit).PurchaseLimitedDropUseCase가 이 위에서Stock.@Version낙관적 락 재시도(maxAttempts=200)까지 결합해 오버셀 0건을 보장한다.SeatLockStoreImpl(backend/src/main/kotlin/com/sportsapp/infrastructure/lock/SeatLockStoreImpl.kt) — 티케팅 좌석을SET NX PX분산락으로 개별 선점(SelectSeatsUseCase, TTL 300초).- 주문 확정은
event.payment.payment.v1Kafka 토픽으로 비동기화돼 있어(각 주문 컨텍스트가 자기*PaymentEventWorker로 확정), 결제 완료 후 DB 쓰기가 동기 경로에 몰리지 않는다.
이 모든 방어는 **“들어온 요청을 어떻게 안전하게 처리할 것인가”**에 집중돼 있다. grep으로 확인한 결과 가상 대기열(입장 인원 자체를 배치로 제어하는 메커니즘) 관련 코드는 0건이다 — 유입 총량을 줄이는 계층이 없어, 판매 시작 시각에 몰리는 20,000 TPS가 인증·필터 체인·JWT 파싱까지 포함해 앱에 그대로 도달한 뒤에야 세마포어·Lua 게이트가 처리한다. LoadSheddingFilter는 넘치는 요청을 “버리는” 최후 방어선이지, 사용자에게 “당신 차례가 언제 오는지”를 알려주며 유입을 관리하는 대기열이 아니다. DropReservationStoreImpl의 완충 세마포어(200 permit)도 대기열이 아니라 오버셀 방어(FR-7, 마케팅 이벤트 고부하 대응 PRD) 소관이며, 클래스 주석이 “단일 인스턴스 전제”임을 명시한다 — 인스턴스마다 독립된 로컬 세마포어이지 인스턴스 간 공유되는 카운터가 아니다.
두 대상 경로의 기존 게이팅은 비대칭이다. LimitedDropApiController.kt:29는 @ConditionalOnProperty(name=["limited-drop.enabled"], havingValue="true", matchIfMissing=false)로 빈 등록 자체를 부팅 시점에 토글한다 — limited-drop.enabled=false면 컨트롤러 빈이 아예 등록되지 않아 전 엔드포인트가 404를 반환한다(클래스 주석 “Release Scenario 2단계·롤백”). 이는 런타임에 값을 조회해 분기하는 FeatureFlagEvaluator와 다른 별개 축이며, private-be-code-convention의 no-conditional-on-property(빈 등록 자체를 토글하면 재기동 없이 무중단 롤백 불가) 위반 패턴이다. 반면 티케팅 EventApiController.kt(좌석 선택 진입점)에는 이런 부팅 토글이 없다 — 좌석 선택·구매 엔드포인트는 항상 빈으로 등록돼 있다. 이번 과제가 신설하는 “가상 대기열 경유 여부” 런타임 플래그는 이 기존 축과 독립적이다 — 한정판은 “기능 자체 존재 여부”(limited-drop.enabled, 부팅 토글)와 “대기열 경유 여부”(신규 런타임 플래그) 두 축이 함께 있고, 티케팅은 “대기열 경유 여부” 한 축만 있다.
이 레포에는 이미 완성된 피처 플래그 런타임 평가 메커니즘이 존재한다 — domain/common/FeatureFlagEvaluator.kt(isEnabled(key, context, default))를 도메인 서비스가 주입해 평가하고, 구현체(infrastructure/featureflag/evaluator/FeatureFlagEvaluatorImpl.kt)가 로컬 스냅샷 → Redis → MySQL → 기본값 순으로 폴백한다([피처 플래그](../피처 플래그/PRD.md) 과제 산출물). 이번 과제는 이 기존 메커니즘을 재사용하며, 새로운 토글 시스템을 만들지 않는다.
Problem Definition
- 판매 시작 시각에 발생하는 20,000 TPS 유입이 대기열 없이 앱에 그대로 도달한다 —
LoadSheddingFilter가 초과분을 503으로 fast-fail 시키지만, 이는 “누구를 먼저 들여보낼지”를 통제하지 못하고 단순히 넘치는 요청을 버릴 뿐이다. - 사용자는 자신이 재고 소진 전에 구매 기회를 얻을 수 있는지 판단할 방법이 없다 — 503을 받으면 무한 재시도(새로고침 폭격)로 대응하게 되고, 이는 유입을 더 키우는 악순환이다.
- 재고·좌석이 이미 소진된 이후에도 동일한 강도의 요청이 계속 앱에 도달해, 소진 후 거부(FR-8, 마케팅 이벤트 고부하 대응 PRD)가 처리해야 할 요청량 자체가 줄지 않는다.
- 한정판 구매(
PurchaseLimitedDropUseCase)와 티케팅 좌석 선택(SelectSeatsUseCase)/구매(PurchaseTicketsUseCase) 두 경로 모두 동일한 문제를 겪지만, 유입 제어 계층이 두 경로에 공통으로 없다. - 이 문제를 해결할 유입 제어를 도입하더라도 즉시 전량 적용이 위험하다 — 무중단으로 켜고 끌 수 있는 안전장치 없이는 배포 자체가 새로운 장애 지점이 된다.
Goals / Non-Goals
Goals
- 한정판(
LimitedDrop) 구매와 티케팅(Ticketing) 좌석 선택·구매, 두 폭주 지점 앞단에 가상 대기열 게이트를 도입한다. - 대기열은 순번을 부여하고, 배치 단위(주기·인원)로 입장(admission)을 허용해 다운스트림(기존 Lua 재고 게이트·좌석 락)에 도달하는 트래픽 총량을 제어한다.
- 입장이 허용된 사용자에게만 유효한 입장 토큰을 발급하고, 기존 구매 API 앞단에서 토큰을 검증해 대기열 우회를 차단한다.
- RN 앱에 대기실 화면을 제공한다 — 순번·예상 대기시간·진행바를 표시하고, 입장 시 구매 화면으로 자동 전환한다.
- 기존 피처 플래그 메커니즘으로 대기열 경유 여부를 런타임에 켜고 끌 수 있게 해, 무중단으로 롤백 가능하게 한다.
LoadSheddingFilter·Lua 원자 재고 게이트·좌석 락·PaymentEvent 비동기 확정은 그대로 유지한다 — 대기열은 이 방어선들을 대체하지 않고 그 앞단에 추가된다.
Non-Goals
- 한정판·티케팅 서비스 분리(MSA) — 아키텍처 진단 3단계(지속적 20,000 TPS가 지표로 증명될 때) 트리거 도달 전에는 범위 밖이다. 이번 과제는 단일 모놀리스 안에서 유입 제어만 추가한다.
- 마케팅 전용 스레드풀 bulkhead·읽기 전용 replica 라우팅 — 아키텍처 진단이 2단계 안에서 대기열과 함께 언급하지만, 이번 PRD는 대기열 자체만 다룬다. bulkhead·replica 라우팅은 별도 후속 과제로 분리한다.
- 추첨(Draw) 방식 대기열 — [마케팅 이벤트 고부하 대응](../마케팅 이벤트 고부하 대응/PRD.md) Non-Goals와 동일하게, 1차 범위는 선착순(순번 기반) 대기열만 다룬다.
- CDN 정적 랜딩·엣지 rate-limit — 아키텍처 진단이 “부분 채택(프론트 정적화만)“으로 분류한 인프라 레벨 대응이며, [배포 파이프라인·환경 분리](../배포 파이프라인·환경 분리/PRD.md) 과제 소관이다. 이번 과제는 다루지 않는다.
LoadSheddingFilter교체·제거 — 최후 방어선으로 그대로 유지한다. 대기열 게이트를 통과한 트래픽이라도 순간적으로 처리 한계를 넘으면 여전히 503으로 보호받아야 한다.- 봇·매크로 탐지, 디바이스 핑거프린팅, 캡차 — 인터파크 등 실제 티케팅 서비스가 갖춘 부정 예매 방지 고급 보안은 범위 밖이다(Open Questions).
- 한정판·티케팅 외 다른 엔드포인트로의 대기열 확장 — 이번 범위는 확정된 두 구매 경로에 한정한다.
User Scenarios
페르소나: 구매자(B2C, RN 앱으로 한정판/티케팅을 구매하려는 일반 사용자), 운영자(대기열 상태를 모니터링하고 플래그를 관리하는 개발자).
- 해피 패스 — 정상 입장·구매: 판매 시작 시각에 대량 사용자가 몰리면, 구매자는 대기실 화면에서 자신의 순번·예상 대기시간·진행바를 확인한다. 배치 admission으로 자신의 차례가 오면 입장 토큰이 발급되고, 화면이 구매(한정판 구매 또는 좌석 선택) 화면으로 자동 전환된다. 이후 흐름은 기존 Lua 재고 게이트·좌석 락을 그대로 통과한다.
- 이탈: 대기 중 앱을 종료하거나 장시간 백그라운드로 전환한 사용자는 대기열에서 제거되고 순번을 잃는다. 재진입 시 새 순번을 받는다(대기열 맨 뒤).
- 토큰 만료: 입장 토큰을 발급받았지만 유효시간 내에 구매(재고 차감 또는 좌석 선택)를 완료하지 못한 사용자는 토큰이 만료돼 구매 API 접근이 거부된다. 재진입해 새로 대기해야 한다.
- 엣지 — 중복 진입: 동일 사용자가 같은 상품/이벤트 대기열에 중복 진입을 시도하면 기존 순번을 그대로 반환한다(새 순번을 추가로 발급하지 않는다 — 멱등).
- 엣지 — 대기열 포화: 대기열이 최대 수용 규모를 초과한 상태에서 신규 진입 요청이 오면, 진입을 거부하고 “잠시 후 다시 시도” 안내를 반환한다.
- 예외 — 대기열 우회 차단: 입장 토큰 없이 구매 API를 직접 호출하면(대기실을 건너뛰려는 시도 포함) 토큰 게이트가 거부한다.
- 플래그 OFF — 대기열 우회: 운영자가 대기열 피처 플래그를 OFF로 전환한 상태에서는 구매자가 대기실 화면 없이 기존 직접 구매 경로로 즉시 진입한다. 배포 없이 수 초 내 전환된다.
- 운영자 — 실시간 모니터링: 운영자는 대기열 길이·admission rate·평균 대기시간을 대시보드에서 실시간으로 확인하고, 이상 징후(대기열 정체, 우회 시도 급증) 발생 시 알림을 받는다.
Benchmarking
| 제품명 | 카테고리 | 참조 패턴 | URL |
|---|---|---|---|
| Trip.com Flash Sale | 이커머스 플래시 세일 플랫폼 | CDN 정적 랜딩 → 가상 대기실이 배치(2초당 1,000명) 단위로 입장 허용 → 재고당 토큰 1개인 토큰 게이트 → Redis Lua 원자 차감 → Kafka로 결제/이행 분리. 이번 과제의 “배치 admission + 토큰 게이트 + 기존 Lua 원자 게이트” 구조가 이 패턴을 그대로 참조한다 | Trip.com Flash Sale System |
| Queue-it | 가상 대기실 SaaS(글로벌 티케팅·이커머스 다수 사용) | Redis Sorted Set으로 순번(ZADD/ZRANK, O(log N) 조회)을 관리하고, 리더 선출된 Admission Controller가 5~10초 주기로 다운스트림 충돌률(conflict rate)을 읽어 배치 admission 크기를 동적으로 조정한다. Controller가 죽어도 최근 값으로 폴백해 큐를 막지 않는다(fail-safe). 이번 과제의 “순번=Sorted Set, 배치 admission 주기 조정” 설계가 이 구조를 참조한다 | Queue-it’s Virtual Waiting Room System Design |
| 인터파크 NOL 티켓 | 국내 공연·스포츠 티케팅 플랫폼 | 계정 1개당 대기열 순번 1개만 발급(중복 진입 차단), 대기 중 다른 화면·앱 전환 시 오류 처리(이탈 감지), 순번이 줄어들 때까지 대기 후 예매·좌석 선택으로 자동 진행. 이번 과제의 “1인 1순번 멱등”·“이탈 시 순번 소실”·“입장 시 구매 화면 자동 전환” 시나리오가 이 운영 정책을 참조한다 | NOL 티켓 고객센터 — 예매대기 안내 |
Functional Requirements
| ID | 요구사항 | 우선순위 |
|---|---|---|
| FR-1 | 한정판 구매(PurchaseLimitedDropUseCase 진입점)와 티케팅 좌석 선택(SelectSeatsUseCase 진입점) 두 경로에 대기열 게이트를 적용한다. 피처 플래그 ON 상태에서만 대기열을 경유한다 | P0 |
| FR-2 | 대기열 진입 요청에 사용자별 고유 순번을 부여한다. 동일 사용자가 동일 대상(상품/이벤트)에 중복 진입을 요청하면 새 순번을 발급하지 않고 기존 순번을 멱등하게 반환한다 | P0 |
| FR-3 | 배치 단위(주기·인원)로 대기열 선두 사용자에게 입장(admission)을 허용하고, 입장이 허용된 사용자에게 입장 토큰을 발급한다 | P0 |
| FR-4 | 입장 토큰은 유효시간(TTL)을 가진다. TTL 내에 구매를 완료하지 못하면 토큰이 만료되어 재사용할 수 없다 | P0 |
| FR-5 | 기존 구매 API(한정판 구매·좌석 선택) 앞단에서 유효한 입장 토큰을 검증한다. 토큰이 없거나 만료·위조된 요청은 대기열 우회로 간주해 거부한다 | P0 |
| FR-6 | 사용자가 자신의 대기 순번·앞선 대기 인원·예상 대기시간을 조회할 수 있는 API를 제공하며, 대기 중에는 실시간에 가깝게 값이 갱신된다(폴링·SSE 등 구체 갱신 방식은 TDD에서 결정) | P0 |
| FR-7 | 대기열이 사전에 정의된 최대 수용 규모를 초과하면 신규 진입 요청을 거부하고 사유를 안내한다 | P0 |
| FR-8 | 대기 중 이탈(연결 종료·순번 조회 미갱신)한 사용자를 대기열에서 제거해, 뒤에 있는 사용자의 순번이 자연스럽게 앞당겨지도록 한다 | P0 |
| FR-9 | 기존 피처 플래그 메커니즘(FeatureFlagEvaluator)으로 대기열 경유 여부를 런타임에 전환한다. 플래그 OFF 시 요청은 대기열을 거치지 않고 기존 직접 구매 경로로 즉시 처리된다. 두 토글의 조합: 한정판은 기존 부팅 토글 limited-drop.enabled가 ON(기능 자체가 존재)인 상태에서만 이 대기열 플래그가 “대기열 경유 vs 직접 구매”를 가른다 — limited-drop.enabled=OFF면 컨트롤러 빈 자체가 없어 404이고 대기열 플래그와 무관하다. 티케팅은 그런 부팅 토글이 없으므로 대기열 플래그가 유일한 축이다 | P0 |
| FR-10 | RN 앱에 대기실 화면을 제공한다 — 순번, 앞선 대기 인원, 예상 대기시간, 진행바를 표시하고, 입장 허용 시 자동으로 구매 화면(한정판 구매 또는 좌석 선택)으로 전환한다 | P0 |
| FR-11 | 대기열 상태(현재 대기 인원, admission rate, 평균/구간별 대기시간)를 운영자가 조회할 수 있는 API를 제공한다 | P1 |
| FR-12 | 대기열 진입·이탈·admission·토큰 만료 이벤트를 집계해 대기 이탈률 등 지표를 API로 제공한다 | P2 |
Non-Functional Requirements
- 대기열 게이트 통과 후 다운스트림(한정판 구매·좌석 선택 API)에 도달하는 트래픽은 배치 admission 목표치 이내로 유지한다. 초기 목표치는 **클러스터 전체 기준 2초당 100명(=50 TPS)**으로 잡는다 — 기존 한정판 완충 세마포어(200 permit)·목표 응답시간(P95 800ms, 마케팅 이벤트 고부하 대응 PRD NFR)을 기준으로 계산한 다운스트림 최대 처리량(약 250 TPS, 200 permit ÷ 0.8초)의 20% 수준으로, 대기열 도입 초기에는 보수적으로 시작해 상시 트래픽 시뮬레이터 결과로 재조정한다. 레포에
docker-compose.lb.yml(backend 3 replica + nginx-lb)이 이미 존재해 마케팅 폭주 시 다중 인스턴스 투입이 가능하므로, 이 수치는 인스턴스 1대가 아니라 클러스터 전체 admission 합계 기준이다. - 배치 admission 카운터·대기 순번 큐는 Redis 등 중앙 저장소로 설계한다(원칙) —
DropReservationStoreImpl의 완충 세마포어(200 permit, 단일 인스턴스 전제, 인스턴스 간 미공유)와 달리, 대기열은 인스턴스 수가 늘거나 줄어도 순번·admission 판정이 흔들리지 않아야 한다. 인스턴스 로컬 카운터로 admission을 판정하면 인스턴스마다 독립적으로 배치를 허용해 클러스터 전체 admission이 목표치를 초과할 위험이 있다. 정확한 배치 주기·인원 튜닝은 TDD 부하 테스트로 확정한다(Open Questions). - 대기열 최대 동시 수용 인원은 100,000명으로 잡는다 — 목표 스파이크(20,000 TPS)가 5초 지속돼도 유입 전체를 대기열이 흡수할 수 있는 규모(20,000 × 5)를 기준으로 한다.
- 순번·예상 대기시간 조회 API의 P95 응답 시간은 300ms 이내다.
- 입장 토큰 유효시간(TTL)은 5분으로 잡는다 — 기존 좌석 락 TTL(300초,
SelectSeatsUseCase)과 동일하게 맞춰, 좌석을 선점한 채 입장 토큰만 만료되는 불일치를 방지한다. - 이탈 판정 기준은 마지막 순번 조회(heartbeat) 후 60초 이내 미갱신이다.
- 대기열 게이트 자체의 장애(Redis 등 인프라 장애)가 전체 구매 흐름을 막지 않도록, 장애 시 폴백 정책(대기열 없이 직접 통과 또는 전체 거부)을 TDD에서 결정하되 5xx 에러율 1% 미만을 유지한다(마케팅 이벤트 고부하 대응 PRD NFR과 동일 기준).
Operations
- 대기열 길이(현재 대기 인원), admission rate(초당 실제 입장 허용 수), 입장 토큰 발급·소진·만료 건수, 대기시간 분포(P50/P95/P99)를 [옵저버빌리티 스택 도입](../옵저버빌리티 스택 도입/PRD.md) 대시보드에 노출한다.
- 다음 조건에서 [지능형 장애 알림](../지능형 장애 알림/PRD.md) 채널로 통지한다(알림 소스 태그:
virtual-queue): 대기열 길이가 최대 수용 규모의 90%를 초과할 때, admission rate가 목표치 대비 50% 이하로 3분 이상 지속될 때, 입장 토큰 없이 구매 API를 직접 호출하는 우회 시도가 임계치를 초과할 때. - 오버셀이 1건이라도 발생하면 기존 [마케팅 이벤트 고부하 대응](../마케팅 이벤트 고부하 대응/PRD.md) Operations 기준(critical, 소스 태그
oversell)을 그대로 따른다 — 대기열 도입이 기존 오버셀 방지 게이트의 감시 기준을 낮추지 않는다. - 피처 플래그 ON/OFF 전환 이력은 기존 [피처 플래그](../피처 플래그/PRD.md) 감사 로그로 추적한다(별도 감사 로그를 신설하지 않는다).
Success Metrics
- 앱 도달 TPS 감소율: [상시 트래픽 시뮬레이터](../상시 트래픽 시뮬레이터/PRD.md) 마케팅 이벤트 스파이크 시나리오(20,000 TPS 목표)로 재현했을 때, 대기열 게이트 통과 후 다운스트림 도달 TPS가 배치 admission 목표치(초기 50 TPS) 이내로 유지되는지 실측한다. 측정 방법: 옵저버빌리티 스택의 API 레이턴시·처리량 지표에서 대기열 도입 전/후 다운스트림 TPS를 비교.
- 오버셀 0건 유지: 대기열 도입 후에도 한정판·티케팅 모든 판매 회차에서 오버셀 0건을 유지한다(마케팅 이벤트 고부하 대응 PRD Success Metrics와 동일 기준 재확인).
- 대기 이탈률: 최초 마케팅 이벤트 시뮬레이션에서 대기열 진입 후 미구매 이탈 비율을 실측하고 결과를 Document History에 기록한다. 목표 상한은 최초 계측치를 기준으로 다음 개정에서 확정한다(상시 트래픽 시뮬레이터 PRD의 “목표 미달 시 실패로 간주하지 않고 병목을 기록” 선례를 따른다).
- 롤백 반영 시간: 대기열 피처 플래그를 OFF로 전환한 후 3초 이내 전 인스턴스에 반영되어 직접 구매 경로로 전환되는지 확인한다(피처 플래그 PRD NFR·Success Metrics와 동일 기준 재사용). 측정 방법: 다중 인스턴스 환경에서 플래그 변경 시각과 각 인스턴스의 대기열 우회 전환 시각 차이를 로그로 대조.
Milestones
- M1: 대기열 진입·순번 부여·중복 진입 멱등·최대 수용 규모 초과 거부(FR-2, FR-7).
- M2: 배치 admission·입장 토큰 발급·TTL 만료(FR-3, FR-4).
- M3: 입장 토큰 게이트를 한정판 구매·티케팅 좌석 선택 두 경로에 적용(FR-1, FR-5).
- M4: 순번·예상 대기시간 조회 API·이탈 감지(FR-6, FR-8).
- M5: 피처 플래그 연동 — 대기열 우회 무중단 전환(FR-9).
- M6: RN 대기실 화면 — 순번·진행바·자동 전환(FR-10).
- M7: 운영 대시보드 연동·집계 지표(FR-11, FR-12).
의존: [마케팅 이벤트 고부하 대응](../마케팅 이벤트 고부하 대응/PRD.md)의 LimitedDrop 판매 시작 게이트·Lua 재고 게이트가 선행돼 있어야 한정판 경로의 대기열 게이트가 의미를 가진다(이미 구현됨). [피처 플래그](../피처 플래그/PRD.md) 평가 메커니즘이 선행돼 있어야 FR-9가 성립한다(이미 구현됨).
Open Questions
- 배치 admission의 정확한 주기·인원(이 PRD의 초기 제안: 2초당 100명)은 상시 트래픽 시뮬레이터의 부하 테스트 결과로 TDD 단계에서 재조정이 필요하다.
- 순번·예상 대기시간 조회를 폴링과 SSE 중 무엇으로 구현할지는 TDD 단계에서 인프라 비용·구현 복잡도로 결정한다(SSE 채택 시 옵저버빌리티 스택 도입 과제와 리소스 사용량을 함께 검토).
- 대기열 게이트를 티케팅의 “좌석 선택(
SelectSeatsUseCase)” 단계 앞에 걸지, “구매 확정(PurchaseTicketsUseCase)” 단계 앞에도 별도로 걸지 — 사용자 시나리오상 좌석 선택 진입 전이 자연스러우나, 좌석 선택 후 결제까지 시간이 걸리는 경우의 재대기 정책과 함께 TDD에서 확정한다. - 대기열 인프라 장애(Redis 등) 시 폴백 정책(대기열 없이 전원 통과 vs 전체 거부)을 어느 쪽으로 할지 — 전자는 오버셀 방지 게이트가 있어 안전하나 유입 제어 목적을 상실하고, 후자는 안전하나 가용성을 해친다. TDD에서 트레이드오프를 확정한다.
- 대기열 우회 방지를 위한 토큰 위변조 방지 방식(서명 강도 등)의 보안 요구 수준 — TDD Security Information에서 결정한다.
- 봇·매크로 대응(캡차 등)의 필요성 — 이번 범위는 Non-Goal이나, 인터파크 사례처럼 실제 마케팅 이벤트 운영 중 필요성이 확인되면 별도 후속 과제로 검토한다.
- 한정판·티케팅 외 다른 폭주 가능 지점(예: 인기 상품 조회 급증)까지 대기열을 확장할지 — 이번 범위 밖이며, 트래픽 지표로 필요성이 확인되면 별도 후속 과제로 검토한다.
LimitedDropApiController의@ConditionalOnProperty("limited-drop.enabled")부팅 토글을 이번 2단계에서 런타임FeatureFlagEvaluator로 이관할지 여부 — private-be-code-conventionno-conditional-on-property위반 패턴이며 향후 런타임 플래그로 이관을 권장하나, 이번 범위 밖(Non-Goal) 으로 둔다. 2단계 핵심은 가상 대기열 신설이므로 기존 토글 리팩터로 범위를 넓히지 않는다. 이관 여부·시점은 별도 후속 과제에서 결정한다.
Document History
| 날짜 | 변경 내용 |
|---|---|
| 2026-07-09 | 최초 작성 — 전체 시스템 아키텍처 진단 문제 B2·채택안 A를 근거로 가상 대기열 PRD 작성. 대상 경로 2개(한정판·티케팅), RN 대기실 화면, 기존 피처 플래그 메커니즘 재사용 확정 |
| 2026-07-09 | 재검수 1차 반영(NEEDS_REVISION 해소): (1) Background에 LimitedDropApiController의 @ConditionalOnProperty 부팅 토글과 EventApiController의 부재로 인한 두 경로 게이팅 비대칭 명시, 완충 세마포어(200 permit)가 대기열이 아닌 오버셀 방어·단일 인스턴스 전제임을 구분 (2) FR-9에 한정판(limited-drop.enabled × 대기열 플래그 두 축 조합)과 티케팅(대기열 플래그 단일 축)의 관계 명시 (3) 부팅 토글의 런타임 플래그 이관 여부를 Open Questions에 Non-Goal로 추가 (4) NFR의 배치 admission 수치(2초당 100명)가 docker-compose.lb.yml 3 replica 전제의 클러스터 전체 기준임을 명시하고, admission 카운터·순번 큐를 Redis 등 중앙 저장소로 설계하는 원칙을 추가 |