피처 플래그 PRD

Background

sports-application은 dev/prod 환경 분리(main 머지 시 dev 자동 배포, prod는 QA 게이트 통과 후 배포)와 무중단 배포를 전제로 한다. 이 전제 아래 레포의 여러 규칙·설계 문서가 “롤백 = 피처 플래그 OFF”를 반복적으로 참조한다 — private-db-schema-convention의 expand-contract 순서, private-tdd의 Release Scenario 롤백 플랜, [배포 파이프라인·환경 분리 TDD](../배포 파이프라인·환경 분리/TDD.md)의 “QA 게이트 오작동 시 피처 플래그 성격 워크플로 입력으로 임시 비활성” 등이 그 예다.

그러나 실제로 이 레포에는 “피처 플래그”라 부를 수 있는 런타임 토글 시스템이 없다. 존재하는 것은 두 가지뿐이다.

  1. Spring @Profilebackend/src/main/kotlin/com/sportsapp/infrastructure/booking/gateway/StubPaymentRefundGateway.kt:18@Profile("!prod")(prod에서만 실 PG 어댑터로 교체), 그리고 @Profile("!test-jpa")가 테스트 격리 목적으로 40여 곳에 사용된다. 둘 다 기동 시점에 고정되며 런타임 변경이 불가능하다(재기동 필요).
  2. application.yml boolean 값 — 예: management.datadog.metrics.export.enabled: ${DATADOG_ENABLED:false}(backend/src/main/resources/application.yml:163). 단 이 값은 앱 기동 시 Micrometer Datadog 레지스트리를 구성할지 결정하는 부팅 시점 설정이며, 런타임에 자주 바뀌어야 할 종류의 토글이 아니다 — 이 과제가 흡수해야 할 대상의 예시가 아니라, “정적 설정 방식만 존재한다”는 현재 메커니즘 자체를 보여주는 예시다.

이 두 메커니즘 모두 사용자/그룹 타게팅, 퍼센티지 롤아웃, 변경 이력, 관리 화면을 전혀 지원하지 않는다. 즉 레포의 여러 문서가 전제하는 “피처 플래그 OFF로 즉시 롤백”이라는 능력이 실제로는 존재하지 않는 상태다.

이 간극을 보여주는 또 다른 사례로 [마케팅 이벤트 고부하 대응](../마케팅 이벤트 고부하 대응) 과제의 BE-10-LimitedDropApiController-피처플래그-config.md 티켓이 있다 — 이 티켓은 limited-drop.enabled라는 ad-hoc application.yml + @ConfigurationProperties 기반 플래그를 계획하고 있으며, 이 역시 재배포가 있어야 값이 바뀌는 정적 방식이다. 단, 이 티켓은 별도 과제 소관이며 현재 코드에 구현돼 있지 않다 — 본 과제는 이 티켓·과제의 완료 여부에 의존하지 않는다. 참조하는 이유는 “이런 ad-hoc 정적 플래그가 도메인마다 개별적으로 계속 생겨날 위험”을 보여주는 사례이기 때문이다.

이 과제는 MySQL을 SSOT(Single Source of Truth)로 하고 Redis 캐시·pub/sub로 실시간 전파하는 중앙화된 피처 플래그 시스템을 구축한다. 완료 조건은 이 시스템 자체의 구축과, 이번 과제 안에서 만드는 신규 데모 기능(또는 런타임 게이팅이 의미 있는 기존 엔드포인트) 1개를 이 시스템으로 실제 게이팅해 ON/OFF·퍼센티지 롤아웃 동작을 E2E로 증명하는 것이다. limited-drop.enabled 같은 기존 ad-hoc 플래그의 흡수·마이그레이션은 “향후 신규 기능은 이 시스템을 사용한다”는 원칙과 Open Questions의 후속 과제로 남긴다.

Problem Definition

  • 런타임 토글 불가: @Profile·application.yml boolean 모두 값을 바꾸려면 재기동/재배포가 필요하다. “배포 후 이상 감지 시 플래그 OFF로 즉시 롤백”이 문서상으로만 존재하고 실행 수단이 없다.
  • 타게팅 불가: 전역 ON/OFF조차 코드/설정 배포 없이는 못 바꾸므로, 사용자 일부에게만 노출하는 점진적 롤아웃·A/B 실험·플랜별 기능 노출(엔타이틀먼트)은 원천적으로 불가능하다.
  • 이력 부재: 언제 누가 무엇을 바꿨는지 기록이 없다 — application.yml 값 변경은 git 커밋 이력에만 남고, 배포 없이 발생하는 변경(런타임 토글이 생기면 필연적으로 발생)은 추적 수단이 없다.
  • 분산된 ad-hoc 패턴 확산 위험: limited-drop.enabled처럼 도메인마다 각자의 @ConfigurationProperties 플래그를 만들면, 동일한 문제(재배포 필요·이력 없음·타게팅 불가)가 계속 반복되고 플래그 종류·명명·수명 관리가 도메인마다 제각각이 된다.
  • 트래픽 규모 대비 위험한 확장 경로: b2c 3000TPS, 마케팅 이벤트 20000TPS 트래픽 전제 하에서, 향후 플래그 평가를 도입한다면 매 요청 MySQL 직접 조회는 명백한 병목이 된다 — 캐시 계층 없이 설계하면 이후 되돌리기 어렵다.

Goals / Non-Goals

Goals

  • FeatureFlag 도메인을 신설하고 MySQL을 SSOT로 하는 CRUD·평가 체계를 구축한다.
  • 플래그 종류 4종을 지원한다: RELEASE(킬스위치·롤백용), OPERATIONAL(운영 중 부하 대응 등 토글), EXPERIMENT(A/B 실험), ENTITLEMENT(플랜·속성 기반 기능 노출).
  • 평가 전략은 아래 3종으로 한정한다(범용 규칙 엔진을 만들지 않는다):
    • 전역 ON/OFF
    • 퍼센티지(%) 점진 롤아웃 — 안정 키(userId) 해시 기반 버케팅. EXPERIMENT variant 배정도 동일 메커니즘(안정 키 해시)을 재사용한다.
    • 속성 매칭 1종(단순 equals) — ENTITLEMENT가 평가 컨텍스트의 속성과 매칭한다. 속성 매칭은 호출부가 평가 컨텍스트로 주입하는 범용 메커니즘이다 — 아래 User Scenarios·FR의 plan=PREMIUM은 설명을 위한 예시일 뿐, 이 레포에 실재하는 사용자 등급·구독 플랜 필드가 아니다(코드 확인 결과 Plan·subscriptionPlan 등 관련 개념 0건).
  • MySQL 변경을 Redis 캐시에 반영하고, pub/sub로 변경을 전 서버 인스턴스에 실시간 전파한다(허용 지연: 수 초 이내).
  • 관리자용 REST API(생성/조회/수정/아카이브)와 FE 관리 화면(CRUD·퍼센티지 조정·이력 조회)을 제공한다.
  • 모든 변경에 대해 감사 로그(누가·언제·무엇을·이전값→이후값)를 남기고 조회 API를 제공한다.
  • 이번 과제 안에서 만드는 신규 데모 기능(또는 런타임 게이팅이 의미 있는 기존 엔드포인트) 1개를 이 시스템의 RELEASE 플래그로 게이팅해, ON/OFF·퍼센티지 롤아웃 동작을 E2E로 실제 증명하는 것을 완료 조건으로 삼는다. 향후 신규 기능은 원칙적으로 이 시스템을 통해 게이팅한다(기존 ad-hoc 플래그의 소급 마이그레이션은 Non-Goal — 아래 참조).
  • 도메인 서비스가 반복 보일러플레이트 없이 평가를 호출할 수 있는 내부 공용 평가 클라이언트(라이브러리 모듈)를 제공한다.

Non-Goals

  • 명시적 사용자/그룹 ID 리스트 타게팅 — “이 userId 리스트에게만 노출” 같은 명시적 대상 지정은 범위 밖이다. 퍼센티지 롤아웃(안정 키 해시)과 속성 매칭(엔타이틀먼트)으로 충족 가능한 범위까지만 다룬다. 필요성이 확인되면 Open Questions의 후속 과제로 검토한다.
  • FE 클라이언트사이드 평가 — 웹/앱이 직접 SDK로 플래그를 평가하는 기능은 범위 밖이다. 모든 평가는 백엔드 서버사이드에서만 이뤄지며, FE는 서버 응답(API 결과·기능 노출 여부)을 통해서만 플래그의 영향을 받는다.
  • 실험 결과 통계 분석 — variant 배정 메커니즘까지만 제공하며, 전환율 유의성 검정·실험 대시보드 등 A/B 테스트 결과 분석 기능은 범위 밖이다.
  • 변경 승인 워크플로우 — 변경 요청 → 승인자 승인 같은 별도 승인 절차는 만들지 않는다. 감사 로그로 사후 추적만 제공한다.
  • 다중 리전/다중 클러스터 pub/sub — 로컬 docker compose 단일 Redis 브로커 전제이며, 리전 간 동기화는 다루지 않는다.
  • 레거시 @Profile 전면 교체@Profile("!prod")·@Profile("!test-jpa")처럼 기동 시점에 고정돼도 무방한(테스트 격리, 컴파일 타임 어댑터 교체) 용도는 이번 시스템으로 옮기지 않는다.
  • 부팅 시점 설정값·숫자 튜닝값의 흡수management.datadog.metrics.export.enabled처럼 앱 기동 시 Micrometer 레지스트리 구성을 결정하는 값, 세마포어 permit 수·acquire timeout 같은 숫자 튜닝값은 이번 시스템의 이관 대상이 아니다. 이런 값은 계속 @ConfigurationProperties로 남는다 — “런타임에 자주 바뀌어야 하고, 그 변경이 즉시 서비스 동작에 반영돼야 하는” boolean 토글만 이관 후보다.
  • 기존 ad-hoc 플래그(limited-drop.enabled 등)의 소급 마이그레이션 — 이 값은 [마케팅 이벤트 고부하 대응](../마케팅 이벤트 고부하 대응) 과제 소관이며 현재 코드에 구현돼 있지 않다. 본 과제는 그 과제의 완료 여부와 무관하게 독립적으로 완료 가능해야 하므로, 소급 마이그레이션은 이번 범위에 포함하지 않는다 — “향후 신규 기능부터 이 시스템을 사용한다”는 원칙과 Open Questions의 후속 과제로 남긴다.

User Scenarios

페르소나: 운영자(플래그를 관리하는 개발자/운영 담당자), BE 서비스 개발자(평가 클라이언트를 코드에 통합하는 소비자), 최종 사용자(B2C/B2B, 플래그 평가 결과의 간접 영향을 받음).

  1. 해피 패스 — 킬스위치 롤백: prod 배포 후 이상이 감지되면 운영자가 관리 화면에서 해당 RELEASE 플래그를 OFF로 전환한다 → 수 초 이내 전 서버 인스턴스에 반영돼, 그 플래그로 게이팅된 엔드포인트가 즉시 503 Service Unavailable(응답 바디에 사유·재시도 안내 없음, 단순 비활성 표시)을 반환하기 시작한다.
  2. 해피 패스 — 점진 롤아웃: 운영자가 OPERATIONAL 플래그의 롤아웃 비율을 10% → 50% → 100%로 단계적으로 올린다 → 안정 키(userId) 해시 기반이라 동일 사용자는 단계마다 일관되게 노출 여부가 유지된다(한번 노출된 사용자가 다음 단계에서 다시 비노출로 바뀌지 않는다).
  3. 해피 패스 — A/B 실험: 운영자가 EXPERIMENT 플래그에 variant A/B를 50:50 비율로 설정한다 → 동일 userId는 항상 동일 variant를 받는다(sticky).
  4. 해피 패스 — 엔타이틀먼트: 운영자가 ENTITLEMENT 플래그에 매칭 규칙(예: plan=PREMIUM)을 설정한다 → 평가 컨텍스트로 전달된 속성이 그 규칙과 일치할 때만 기능이 노출된다. (plan 속성은 설명용 예시이며, 실제 평가 컨텍스트에 어떤 속성을 담을지는 호출부·TDD 단계에서 정의한다 — 이 레포에는 아직 사용자 등급·구독 플랜 개념이 없다.)
  5. 예외 — Redis 장애: 캐시·pub/sub 연결이 끊기면 평가 클라이언트는 마지막으로 성공한 로컬 스냅샷 값으로 계속 평가한다 — 캐시 접근 실패가 요청 실패로 전파되지 않는다.
  6. 예외 — 캐시·스냅샷 모두 없음: 최초 기동 등으로 로컬 스냅샷조차 없는 상태에서 평가 요청이 오면, 호출부가 지정한 기본값(default)을 반환한다.
  7. 예외 — 잘못된 관리 API 요청: 롤아웃 퍼센티지에 100 초과 값을 입력하거나, 이미 존재하는 플래그 키로 생성을 요청하면 400을 반환한다.
  8. 빈 상태: 신규 배포 직후 등록된 플래그가 하나도 없을 때 관리 화면은 빈 목록과 “플래그 추가” CTA를 보여준다.
  9. 상태 보호 — 아카이브된 플래그 재평가 차단: ARCHIVED 상태로 전환된 플래그는 평가 요청 시 기본값만 반환하고 활성 평가 대상에서 제외되며, 관리 화면에서도 활성 목록에 노출되지 않는다.
  10. 해피 패스 — 데모 기능 게이팅 증명: 이번 과제 안에서 만든 신규 데모 기능(또는 런타임 게이팅이 의미 있는 기존 엔드포인트) 1개가 이 시스템의 RELEASE 플래그로 게이팅된 상태에서, 운영자가 관리 화면에서 이 플래그를 OFF로 바꾸면 해당 엔드포인트가 재배포 없이 즉시 503 Service Unavailable을 반환하기 시작하고, 다시 ON으로 바꾸면 수 초 이내 정상 응답으로 복구된다 — E2E 테스트로 이 전체 흐름을 검증한다.

Benchmarking

제품명카테고리참조 패턴URL
Unleash오픈소스 피처 플래그 플랫폼(self-hosted)퍼센티지 롤아웃(gradualRollout*/flexibleRollout 전략)에서 userId를 안정 키(stickiness)로 MurmurHash 해싱해 0~100 사이 결정론적 숫자를 산출, 동일 사용자는 항상 동일 결과를 받는 방식을 그대로 참조 — 본 과제의 “안정 키 해시 기반 퍼센티지 버케팅”이 이 패턴Unleash — Stickiness, Unleash — Gradual Rollout
LaunchDarklySaaS 피처 플래그 플랫폼서버사이드 SDK가 Redis 등 외부 저장소를 백엔드로 한 인메모리 read-through 캐시에서 즉시 평가하고, 스트리밍 연결로 변경을 각 SDK 인스턴스에 실시간 push하는 구조 — 본 과제의 “MySQL SSOT + Redis 캐시 + pub/sub 실시간 전파” 구조가 이 캐시·전파 패턴을 참조LaunchDarkly — Redis 연동, LaunchDarkly — 아키텍처 딥다이브

Functional Requirements

ID요구사항우선순위
FR-1FeatureFlag 도메인(key, type(RELEASE/OPERATIONAL/EXPERIMENT/ENTITLEMENT), status(ACTIVE/ARCHIVED), 평가 전략 설정)을 신설하고 MySQL을 SSOT로 저장한다P0
FR-2서버사이드 평가 진입점 — 플래그 key와 평가 컨텍스트(userId, plan 등 속성)를 받아 평가 결과(boolean 또는 variant)를 반환한다. 정의되지 않은 키·아카이브된 키는 호출부 지정 기본값을 반환한다P0
FR-3안정 키(userId) 해시 기반 퍼센티지 버케팅을 구현한다 — 동일 사용자는 항상 동일한 롤아웃 판정/variant 배정을 받는다. OPERATIONAL의 퍼센티지 롤아웃과 EXPERIMENT의 variant 배정이 동일 메커니즘을 공유한다P0
FR-4엔타이틀먼트 평가 — 평가 컨텍스트의 속성 값과 플래그에 설정된 매칭 규칙(단순 equals 1종)을 비교해 결과를 반환한다. 속성 매칭은 호출부가 주입하는 범용 메커니즘이며, 어떤 속성(예: plan)을 실제로 사용할지는 이 레포에 실재하는 개념에 한정하지 않고 TDD·적용 시점에 정의한다P0
FR-5Redis 캐시 계층을 둔다 — MySQL 변경 시 캐시를 갱신하고, 평가 요청은 캐시를 우선 조회한다(MySQL 직접 조회는 캐시 미스 시에만)P0
FR-6Redis pub/sub 기반 변경 전파 — 플래그 생성/수정/아카이브 시 전 서버 인스턴스가 구독 중인 채널에 이벤트를 발행하고, 각 인스턴스는 수신 즉시 로컬 캐시를 갱신한다P0
FR-7관리자용 REST API — 플래그 생성/조회/수정/아카이브, 상태 전이(ACTIVE↔ARCHIVED)를 제공한다P0
FR-8플래그 변경 이력(감사 로그: 변경자, 변경 시각, 변경 대상, 이전값→이후값)을 저장하고 조회 API를 제공한다P0
FR-9이번 과제 안에서 만드는 신규 데모 기능(또는 런타임 게이팅이 의미 있는 기존 엔드포인트) 1개를 본 시스템의 RELEASE 플래그로 게이팅한다 — 플래그 OFF 시 해당 엔드포인트가 503 Service Unavailable을 반환하며 즉시 비활성화됨을, ON 복구 시 수 초 내 정상화됨을 실제 코드·E2E 테스트로 증명한다P0
FR-10관리자용 FE 웹 CRUD 화면 — 플래그 목록/생성/수정, 퍼센티지 롤아웃 조정 UI(슬라이더 등), 변경 이력 조회 화면을 제공한다P1
FR-11캐시·MySQL 모두 접근 불가 시 평가 클라이언트는 마지막 성공 스냅샷(로컬 인메모리) 값으로 폴백하고, 스냅샷조차 없으면 호출부 기본값을 반환한다 — 평가 실패가 서비스 장애로 전파되지 않는다P1
FR-12도메인 서비스가 반복 보일러플레이트 없이 평가를 호출할 수 있는 내부 공용 평가 클라이언트 모듈을 제공한다P1
FR-13플래그별 평가 호출 횟수를 최소 집계(노출 카운트)해 노출한다(상세 통계 분석은 Non-Goal)P2
FR-14장기간(예: 90일) 변경 없이 ACTIVE 상태로 남아있는 RELEASE 플래그를 정리 후보로 식별해 알린다P2

Non-Functional Requirements

  • 플래그 평가 API 응답 시간: P95 5ms 이내(캐시 미스 포함), 캐시 히트 시 P95 1ms 미만.
  • 정상 운영 상태에서 캐시 히트율 99% 이상을 유지한다.
  • 마케팅 이벤트 20000TPS 경로에서 평가 호출이 해당 요청 전체 처리 시간의 5% 이내 비중을 차지한다 — 평가가 병목이 되지 않는다([상시 트래픽 시뮬레이터](../상시 트래픽 시뮬레이터) 마케팅 이벤트 시나리오로 검증).
  • 플래그 변경 후 전 서버 인스턴스 캐시 반영까지 pub/sub 전파 지연 P95 3초 이내.
  • MySQL 장애 시에도 최근 캐시된 값으로 평가(읽기)는 계속 가능해야 한다 — 단 관리 API(쓰기)는 이 경우 실패를 허용한다.
  • 감사 로그는 최소 1년 보존한다.
  • Redis pub/sub는 이 레포에 신규로 추가되는 인프라다 — 코드 확인 결과 RedisMessageListenerContainer·MessageListener 사용 0건. 서버 인스턴스마다 구독 리스너 스레드·전용 Redis 커넥션이 추가되므로, 배포 파이프라인 환경 분리 과제의 스케일아웃 구성(다중 인스턴스)에서 인스턴스 수만큼 리스너·커넥션이 선형으로 늘어난다는 점을 용량 설계에 반영한다.

Operations

  • [옵저버빌리티 스택 도입](../옵저버빌리티 스택 도입) 대시보드에 플래그별 평가 횟수, 캐시 히트율, pub/sub 전파 지연을 지표로 노출한다.
  • 캐시 히트율이 임계치(예: 95%) 아래로 떨어지거나 pub/sub 전파 지연이 NFR 목표(3초)를 초과하면 [지능형 장애 알림](../지능형 장애 알림) 채널로 통지한다(알림 소스 태그: feature-flag).
  • 감사 로그 조회 API를 통해 변경 이력을 추적하며, 운영자는 관리 화면(FE, FR-10) 또는 API로 직접 조회한다.
  • 정리 후보 알림(FR-14)은 옵저버빌리티 대시보드 또는 지능형 장애 알림 채널 중 하나로 정기 통지한다(구체 채널은 TDD 단계에서 결정).
  • Redis pub/sub 리스너는 신규 인프라이므로, 인스턴스별 구독 커넥션 수·리스너 스레드 상태를 [옵저버빌리티 스택 도입](../옵저버빌리티 스택 도입) Redis 대시보드(cpu·memory 지표)에 함께 노출해 커넥션 누수·리스너 미기동을 조기에 발견한다.

Success Metrics

  • 캐시 히트율 99% 이상을 부하 테스트에서 실측(측정 방법: Redis INFO statskeyspace_hits/keyspace_misses 비율 또는 애플리케이션 메트릭 집계).
  • 평가 API P95 5ms 이내를 [상시 트래픽 시뮬레이터](../상시 트래픽 시뮬레이터)의 마케팅 이벤트 20000TPS 시나리오 실행 중 실측(측정 방법: 옵저버빌리티 스택의 API 레이턴시 히스토그램).
  • 킬스위치 변경 후 3초 이내 전 인스턴스 반영 — 측정 방법: 다중 인스턴스 환경(로컬 compose 스케일아웃)에서 플래그 변경 시각과 각 인스턴스 캐시 갱신 시각의 차이를 로그로 대조.
  • 감사 로그 커버리지 100% — 측정 방법: 일정 기간 동안의 관리 API 변경 성공 응답 건수와 감사 로그 적재 건수를 대조해 누락 0건을 확인.
  • 데모 기능 게이팅 E2E 증명 완료 — 측정 방법: 데모 기능(또는 선정된 기존 엔드포인트) 1개에 대해 (1) 플래그 OFF 시 503 응답 재현, (2) ON 복구 시 수 초 내 정상 응답 재현, (3) 퍼센티지 롤아웃 값을 바꿨을 때 안정 키 해시 기반 노출 대상이 일관되게 유지되는지를 각각 통합/E2E 테스트로 확인.

Milestones

private-prd-template “1단계는 P0 전부 포함” 원칙의 예외: 본 과제의 P0 요구사항(FR-1FR-9)은 도메인→평가 전략→캐시/전파→관리 API→적용 증명 순으로 서로를 전제하는 계층 구조라(예: FR-9의 게이팅 증명은 FR-1FR-8 없이는 성립하지 않는다) 단일 마일스톤에 담을 수 없다. M1~M6 전체를 하나의 릴리스 단위로 보고, M6에서 전체 P0가 충족된다.

  • M1: FeatureFlag 도메인·MySQL 스키마·전역 ON/OFF 평가(FR-1, FR-2 일부) — 선행.
  • M2: 퍼센티지 롤아웃·안정 키 해시·엔타이틀먼트 매칭(FR-3, FR-4).
  • M3: Redis 캐시·pub/sub 실시간 전파·폴백 동작(FR-5, FR-6, FR-11).
  • M4: 관리 REST API·감사 로그(FR-7, FR-8).
  • M5: FE 관리 화면(FR-10).
  • M6: 데모 기능 게이팅·평가 클라이언트 모듈화·E2E 증명(FR-9, FR-12) — 완료 조건, 여기서 전체 P0(FR-1~FR-9) 충족.
  • M7: 평가 카운트 집계·정리 후보 알림(FR-13, FR-14).

Open Questions

  • 명시적 사용자/그룹 ID 리스트 타게팅이 실제로 필요해지면 별도 후속 과제로 검토한다 — 이번 범위는 퍼센티지 롤아웃과 속성 매칭으로 한정.
  • 감사 로그 보존 기간 1년이 적절한 기본값인지 — 스토리지 비용과 실제 조회 빈도를 본 뒤 조정 여부를 TDD 단계에서 재확인한다.
  • 관리 화면을 기존 web/portal에 통합할지, 별도 신규 화면으로 분리할지는 TDD 단계에서 결정한다(다른 과제들의 “전용 UI는 최소 범위로, 기존 포털 확장 우선” 선례를 참고할 가능성이 높다).
  • EXPERIMENT variant 개수 상한(예: 최대 4개)을 둘지 여부 — TDD 단계에서 확정.
  • limited-drop.enabled([마케팅 이벤트 고부하 대응](../마케팅 이벤트 고부하 대응) 과제가 실제로 구현한 이후)를 포함해, 향후 식별되는 런타임 boolean 토글 후보(@Profile 기반 부팅 시점 설정은 Non-Goal로 제외)는 본 시스템으로 마이그레이션할지 여부를 해당 과제 완료 시점에 별도 후속 티켓으로 결정한다 — 본 PRD는 그 마이그레이션을 완료 조건으로 삼지 않는다.
  • 데모 게이팅 대상(신규 데모 기능 vs 기존 엔드포인트 재사용)을 구체적으로 무엇으로 할지는 TDD 단계에서 확정한다.

Document History

날짜변경 내용
2026-07-03최초 작성 — 사용자 확정 답변(플래그 종류 4종 전체, 타게팅은 전역+퍼센티지, 서버사이드 평가만, MySQL SSOT+Redis 캐시/pub-sub, 관리 API+FE 화면, 감사 이력, limited-drop.enabled 마이그레이션 포함, NFR 초안) 반영
2026-07-03재검수 반영(NEEDS_REVISION 해소): (1) limited-drop.enabled가 미구현 코드임을 확인, 완료 조건을 “신규 데모 기능 게이팅 E2E 증명”으로 재정의(FR-9·M6·Success Metrics 수정), 마케팅 이벤트 고부하 대응 과제에 대한 hard dependency 제거(2) FR-9 흡수 대상을 “런타임 boolean 토글”로 한정하고 부팅 시점 설정·숫자 튜닝값은 @ConfigurationProperties 잔존으로 Non-Goals에 명시 (3) Background의 datadog 키 경로를 management.datadog.metrics.export.enabled로 정정 (4) Milestones에 “1단계 P0 전부 포함” 예외 사유 명시 (5) User Scenario 1·10에 플래그 OFF 응답 계약을 503 Service Unavailable로 통일 (6) ENTITLEMENT plan=PREMIUM 예시가 이 레포에 실재하지 않는 개념임을 Goals·FR-4·User Scenario 4에 명시 (7) Redis pub/sub가 신규 인프라(RedisMessageListenerContainer 0건)임을 NFR·Operations에 전제로 추가