가상 대기열 트래픽 제어 — 크로스 도메인 통합 DAG

TPM 산출물. 세 도메인(INFRA·BE·FE)의 이미 확정된 도메인 내 DAG를 재분해하지 않고, 도메인 사이의 의존만 엮은 통합 계획이다. 도메인 내 티켓 분해·순서는 시니어 소관(불변).

  • 근거 설계: 20260709-가상대기열-tdd.md(BE), 20260709-가상대기열-design-fe-app.md(FE), 20260709-redis-contract.md(Redis), 20260709-가상대기열-prd.md
  • 도메인 내 DAG(입력, 불변):
    • INFRA: iW0 = INFRA-01 (의존 없음)
    • BE: bW1=BE-01 → bW2=BE-02·03·04·05 → bW3=BE-06·07 → bW4=BE-08·09·10 → bW5=BE-11
    • FE: fW1=FE-01·02·03·04 → fW2=FE-05·06 → fW3=FE-07 → fW4=FE-08·09

핵심 결론 (요약)

  • 도메인 간 실질 의존 엣지는 2개뿐이다 — 나머지는 계약 확정으로 이미 충족됐다.
    1. INFRA-01 → BE prod 배포 : 배포 게이트(컴파일·dev 머지 무관).
    2. BE-08·BE-09 → FE-08·09 실검증 + BE-11 E2E : 실검증 게이트(구현·유닛 무관).
  • BE API 계약이 TDD “FE/외부 계약”에 시그니처 수준으로 동결돼 있어, FE는 BE 구현을 기다리지 않고 IW1부터 병렬 착수한다.
  • 크로스 도메인 Single Writer 위반 0docker-compose.prod.yml(INFRA) / backend/**(BE) / mobile/**(FE) 교집합 ∅.
  • 통합 wave 5개, 최대 너비 6, 평균 4.2 — 직선형 아님(분해 건전).

통합 DAG (Mermaid flowchart LR)

flowchart LR
    subgraph IW1["IW1 (너비 6)"]
        INFRA01[INFRA-01 prod redis]
        BE01[BE-01 계약·Lua·키]
        FE01[FE-01 큐 API client]
        FE02[FE-02 토큰스토어·플래그]
        FE03[FE-03 대기실 컴포넌트]
        FE04[FE-04 큐 라우트 상수]
    end
    subgraph IW2["IW2 (너비 6)"]
        BE02[BE-02~05 store·hmac·도메인]
        FE05[FE-05 useEnterQueue]
        FE06[FE-06 useQueueStatus]
    end
    subgraph IW3["IW3 (너비 3)"]
        BE06[BE-06·07 usecase·pump]
        FE07[FE-07 대기실 뷰모델·화면]
    end
    subgraph IW4["IW4 (너비 5)"]
        BE08[BE-08 Controller]
        BE09[BE-09 인터셉터]
        BE10[BE-10 관측]
        FE08[FE-08 한정판 진입·토큰헤더]
        FE09[FE-09 티케팅 진입·토큰헤더]
    end
    subgraph IW5["IW5 (너비 1)"]
        BE11[BE-11 E2E+풀부팅 크로스검증]
    end
    BE01 --> BE02
    BE02 --> BE06
    BE06 --> BE08
    BE08 --> BE11
    FE01 --> FE05
    FE05 --> FE07
    FE07 --> FE08
    FE07 --> FE09
    INFRA01 -.배포게이트.-> BE11
    BE08 -.실검증게이트.-> BE11
    BE09 -.실검증게이트.-> BE11
    BE08 -.실검증게이트.-> FE08
    BE09 -.실검증게이트.-> FE09

점선(-.->)이 도메인 간 엣지다. 실선은 도메인 내(불변, 참고용 압축 표기). 도메인 간 점선은 컴파일 의존이 아니라 배포/실검증 게이트임에 유의.

통합 wave 표

각 통합 wave에 동시에 열리는 도메인 티켓과 ready 셋 크기. 도메인 내 wave 진행은 시니어가 지휘하고, TPM은 wave 경계의 게이트만 연다.

통합 waveINFRABEFEready 셋너비
IW1INFRA-01 (iW0)BE-01 (bW1)FE-01·02·03·04 (fW1)INFRA-01, BE-01, FE-01, FE-02, FE-03, FE-046
IW2— (완료)BE-02·03·04·05 (bW2)FE-05·06 (fW2)BE-02, BE-03, BE-04, BE-05, FE-05, FE-066
IW3BE-06·07 (bW3)FE-07 (fW3)BE-06, BE-07, FE-073
IW4BE-08·09·10 (bW4)FE-08·09 (fW4)BE-08, BE-09, BE-10, FE-08, FE-095
IW5BE-11 (bW5)— (실검증 참여)BE-11 (+FE-08/09 크로스 실검증)1
  • BE가 임계 경로(5 wave). FE(4 wave)는 IW1부터 시작해 IW4에서 FE-08/09가 BE-08/09와 동시에 열린다 — FE 유닛 테스트는 BE를 mock하므로 구현·유닛은 병렬 가능, 크로스 실검증만 IW5로 미룬다.
  • INFRA-01은 IW1에 단발 완료 후 이후 wave에 관여하지 않는다(배포 게이트로만 IW5 prod 배포에 선행).

도메인 간 의존 엣지 목록 (무엇이 무엇을 왜, 게이트 유형 구분)

#엣지게이트 유형무엇을 기다리나근거
E1INFRA-01 → BE 코드 prod 배포배포 게이트 (컴파일·dev 머지 아님)prod Redis maxmemory 512mb+·noeviction 반영이 코드 prod 배포보다 선행TDD Release Scenario 0단계 “1단계 코드 배포의 필수 선행 조건”. BE 구현·테스트는 Testcontainers Redis로 자급자족 → INFRA-01 없이 dev 머지·유닛/통합 전부 가능. 자기잠식(대기열 OOM→fail-open) 차단 목적
E2BE TDD “FE/외부 계약” → FE 전체계약 게이트 (이미 충족)API 시그니처 확정 — 런타임 엣지 아님계약이 TDD에 시그니처 수준 동결(엔드포인트 5종·QueueEntryResponse·429/403 코드). FE-01이 이 계약으로 타입·client 정의. 확정됨 → FE는 IW1 병렬 착수, BE 구현 대기 불요
E3BE-08(Controller)+BE-09(인터셉터) → BE-11 크로스 E2E실검증 게이트실제 엔드포인트·인터셉터 실물BE-11이 enter→admit→토큰→POST /limited-drops/{id}/orders·/events/{id}/seats/select(X-Entry-Token) 통과를 Testcontainers 풀부팅으로 검증. 컨트롤러·인터셉터 실물 필요 (도메인 내 의존이나 IW4→IW5 경계)
E4BE-08(Controller)+BE-09(인터셉터) → FE-08·09 실검증실검증 게이트 (구현·유닛 아님)실제 큐/구매 엔드포인트·403 게이트FE-08/09 유닛(axios-mock-adapter로 X-Entry-Token 부착·403 QUEUE_BYPASS_DENIED 매핑)은 BE mock으로 IW4 병렬 통과. 그러나 앱↔실 BE 통합 확인은 BE-08/09 머지 후(IW5) 가능
  • 컴파일 의존 도메인 간 엣지: 0개. 세 도메인은 서로의 산출물을 import·컴파일 참조하지 않는다(BE=Kotlin backend, FE=RN mobile, INFRA=compose). E1은 배포, E2는 계약(충족), E3·E4는 실검증뿐.

크로스 도메인 Single Writer 검증

레포 최상위 확인: backend/, mobile/, docker-compose.prod.yml이 각각 독립 경로로 존재.

도메인수정 경로 집합타 도메인과 교집합
INFRA (INFRA-01)docker-compose.prod.yml (레포 루트 단일 파일)
BE (BE-01~11)backend/** (Kotlin src·backend/docs/redis/·backend/src/main/resources/redis/)
FE (FE-01~09)mobile/** (app/·api/·lib/·theme/·components/)
  • 교집합 ∅ — 크로스 도메인 Single Writer 위반 0. INFRA는 compose만, BE는 backend만, FE는 mobile만 수정. 세 도메인이 같은 wave에서 열려도 머지 충돌 없음.
  • 주의: BE 티켓은 dev docker-compose.yml·Testcontainers를 쓰지 프로덕션 compose를 수정하지 않는다. INFRA-01은 docker-compose.prod.yml만 — dev/base compose 불변.
  • 각 도메인 내부 Single Writer(예: BE-08만 SecurityConfig.kt, BE-09는 신규 WebMvcConfig / FE-08=한정판·FE-09=티케팅 파일 분리 / FE는 theme/tokens.ts·api/types.ts 미수정)는 시니어가 이미 보장(티켓 명시). TPM 범위 밖이나 크로스 판정에 영향 없음.

통합 wave 게이트 — 진입·종료 조건

TPM은 아래 게이트만 연다. 게이트마다 완료 근거(리뷰 verdict·머지·검증 아티팩트)를 확인한 뒤 다음 wave를 연다 — 시니어의 “완료” 단언만으로 열지 않는다.

G-IW1 (착수)

  • 진입: senior-pm 정합 검증 PASS + 사용자 구현 승인. (선행 게이트 — PASS 기록 없으면 열지 않음.)
  • 동시 스폰: INFRA(private-redis-implementer for INFRA-01) + BE senior(BE-01 wave 지휘) + FE senior(FE-01·02·03·04 wave 지휘)를 한 메시지에서 동시 스폰.
  • 종료:
    • INFRA-01: private-infra-reviewer verdict APPROVED/COMMENT + prod compose CONFIG GET maxmemory≥536870912·maxmemory-policy=noeviction 확인 아티팩트. (REQUEST_CHANGES면 재수정 후 재리뷰.)
    • BE-01: 리뷰 통과·머지 (Lua EVAL raw 로그·VO 단위 테스트).
    • FE-01~04: 리뷰 통과·머지 (client 유닛·타입 tsc --noEmit 0).

G-IW2 → G-IW3 (도메인 내 진행, TPM 관망)

  • IW2·IW3는 도메인 내 wave 전진이라 시니어가 지휘. TPM은 각 도메인 wave 종료(머지·검증)만 추적표에 반영.
  • BE bW2(store·hmac·도메인)·bW3(usecase·pump), FE fW2(훅)·fW3(뷰모델·화면) 각각 리뷰 통과·머지로 닫힌다.
  • 크로스 게이트 없음 — 두 도메인이 서로를 기다리지 않는다(계약 동결).

G-INFRA (배포 게이트, E1)

  • 진입: IW1에서 INFRA-01 종료(위 G-IW1) — 이르게 닫힘.
  • 종료·효과: prod 코드 배포(IW5 이후 릴리즈) 선행 조건 충족. dev 배포·BE 구현은 이 게이트와 무관하게 진행. QA PASS 후 prod 배포 시 INFRA-01 반영 상태를 재확인.

G-INTEGRATION (실검증 게이트, E3·E4) — 핵심 크로스 게이트

  • 진입: BE-08·BE-09 리뷰 통과·main 머지 확인 (IW4 BE 종료) + FE-08·09 구현·유닛 통과(mock 기반).
  • 종료:
    • BE-11 실행: @SpringBootTest 풀부팅 성공 + 시나리오 통과 아티팩트 — 양 경로(한정판 reserve.lua·티케팅 seat:lock) admitted→토큰→구매 통과, 플래그 OFF directEntry, Redis 장애 fail-open(오버셀 0), 우회 403, §0-1 연쇄 admission 회귀, 이탈 방출.
    • FE-08/09 크로스 실검증: 실 BE 엔드포인트 대상 X-Entry-Token 부착·403 재대기 흐름 확인(dev 구동).
  • REQUEST_CHANGES·실패 시 담당 도메인 시니어에게 반려, 재검증 전 wave 미종료.

병렬 처리량 요약

  • 팀 = 에이전트 병렬 스폰. wave 너비 = 동시 스폰 implementer 수.
  • 통합 wave 5개, 티켓 총 21개(INFRA 1·BE 11·FE 9).
  • 너비 분포: IW1=6, IW2=6, IW3=3, IW4=5, IW5=1 → 최대 6, 평균 4.2.
  • 직선형(모든 wave 1~2) 아님 — 계약 동결로 BE·FE·INFRA를 IW1부터 병렬화해 fan-out을 넓혔다. 임계 경로는 BE 5 wave, FE는 그 안에 완전히 포개진다.

리스크

리스크영향완화
INFRA-01 prod 반영 누락 후 prod 배포마케팅 피크에 대기열 자기 OOM→자기 fail-open(정합성은 MySQL이 지키나 유입 제어 목적 상실)G-INFRA를 prod 배포 체크리스트에 강제 — QA PASS와 별개로 CONFIG GET maxmemory 재확인
FE-08/09를 BE-08/09 머지 전 mock으로만 검증하고 종료계약 동결이라 위험 낮으나, 실 BE와 헤더 케이스({type} kebab 파싱 등) 미세 불일치 가능G-INTEGRATION에서 실 BE 대상 크로스 실검증 필수 — 유닛 통과만으로 IW5 닫지 않음
{type} 와이어 값 정합(limited-drop/ticketing-eventQueueTargetType)BE 파싱 실패 시 400, FE 전 큐 호출 실패계약 정합 확인됨(아래) — BE-08 파싱 테스트 “잘못된 {type} 400”으로 회귀 방어
BE 임계 경로 지연(bW2 store·Lua 결함)후행 BE 전체·IW5 E2E 지연. FE는 영향 없음(계약 병렬)BE 지연은 FE 흐름을 막지 않음 — FE-08/09까지 독립 완주 가능, E2E만 대기

계약 정합 확인 (API·토큰·타입·Redis)

BE TDD “FE/외부 계약 — API 명세” ↔ FE 설계 “API 연동 표”/QueueEntryResponse 대조 — 불일치 0.

항목BE 계약FE 소비정합
엔드포인트POST/GET/DELETE /virtual-queues/{type}/{targetId}/entries[/me], /statsenterQueue·getQueueStatus·leaveQueue 동일 경로
status enumWAITING|ADMITTED|DIRECT_ADMITTEDQueueEntryStatus 동일 3값
position/aheadCount/etaSecondsLong?number | null
entryToken/tokenExpiresAtString?(ISO-8601)string | null
포화429 QUEUE_FULL429→FULL 판별 유니온
미진입404404→NOT_IN_QUEUE 판별
구매 게이트 헤더X-Entry-Token, 403 QUEUE_BYPASS_DENIED동일 헤더·403 매핑
{type} 와이어값limited-drop|ticketing-event동일 리터럴 유니온
인증X-User-Id 헤더동일
  • stats(FR-11): BE-08 제공, FE 미소비(운영자 전용) — FE 의존 없음, 정합 이슈 아님.
  • Redis 계약: BE 내부(BE-01/02) + INFRA-01 사이징(§4)만 참조. FE 비노출. INFRA↔BE는 동일 20260709-redis-contract.md 참조로 정합.

Document History

날짜변경 내용
2026-07-09최초 작성 — INFRA·BE·FE 3도메인 통합 DAG. 통합 wave 5개(너비 6/6/3/5/1). 도메인 간 엣지 4개(배포 E1·계약 E2 충족·실검증 E3·E4). 크로스 Single Writer 위반 0. 계약 정합 9항목 일치