기능 플래그 (Datadog Feature Flags)
Datadog Feature Flags 로 기능을 원격 on/off 하고, 점진적 롤아웃과 A/B 실험(Experiments)의 기반을 만듭니다. OpenFeature 표준 위에 구현돼 있습니다.
웹 앱에서는
@datadog/openfeature-browser나@openfeature/*를 직접 호출하지 말고, 반드시@howmuchhome-web/feature-flags를 통해 사용하세요.
RUM 과 동일한 credential·env 태그를 쓰도록 패키지가 보장하기 때문입니다. 직접 호출하면 exposure 이벤트가
RUM 이벤트와 조인되지 않아 코호트 분석이 깨집니다.
native(Expo) 예외: provider 가 네이티브 모듈(
@datadog/mobile-react-native-openfeature)이라 공유 패키지 facade 로 감싸지 않고, sentry/datadog 와 동일하게 앱(apps/native-app/src/lib/featureFlags)에서 주입합니다 — 이 배선 파일에 한해@openfeature/*·provider 직접 사용이 허용됩니다. 평가(값 읽기)는 공유 훅·레지스트리(@howmuchhome-web/feature-flags/{hooks,evaluate,flags})를 그대로 재사용하므로useFeatureFlag('키')호출부 API 는 web 과 동일합니다. 네이티브 모듈이라 신규 배선은 OTA 불가(스토어 재빌드 필요).
왜 쓰는가
플래그를 평가하면 그 값이 RUM 이벤트·Error Tracking·Session Replay 에 자동으로 태깅됩니다. “이 기능을 켠 사용자만 에러율이 오르는가”를 배포 없이 바로 확인할 수 있고, 문제가 생기면 코드 롤백 없이 Datadog UI 에서 끌 수 있습니다.
지원 범위
| 대상 | 상태 |
|---|---|
apps/app (웹뷰), apps/dashboard, apps/binzip, apps/binzip-admin | ✅ 사용 가능 |
클라이언트 컴포넌트 ('use client') | ✅ 사용 가능 |
| RSC / 서버 액션 / route handler | ❌ 미지원 — 서버 SDK 는 dd-trace 5.116+ 가 필요하고, agentless 모드에서 exposure/experiment 를 지원하지 않음 |
apps/native-app (Expo) | ✅ 사용 가능 — @datadog/mobile-react-native-openfeature(네이티브 모듈) 배선됨. useFeatureFlag('키') 호출부 API는 웹과 동일(배선: apps/native-app/src/lib/featureFlags). ⚠️ 네이티브 모듈이라 OTA 불가 — 스토어 재빌드 후에만 반영 |
서버에서 분기해야 하면 기존 방식(Vercel Edge Config, env 스위치)을 쓰세요.
새 플래그 추가하기
1. Datadog 에서 플래그 생성
Feature Flags 에서 생성합니다. Claude Code 를 쓰면 Datadog MCP 의
create-feature-flag 로도 만들 수 있습니다.
- 키는 snake_case 만 씁니다. Feature Flag Tracking 이 지원하지 않는 문자가 많습니다
(
.:+-=&&||><!(){}[]^"~*?\). kebab-case 의-도 그 목록에 있으므로new-checkout대신new_checkout으로 만드세요. - 환경은
prod/dev두 개가 있습니다. 항상dev에서 먼저 켜서 확인한 뒤prod로 올립니다.
2. 코드 레지스트리에 등록
packages/feature-flags/flags.ts 의 FEATURE_FLAGS 에 한 줄 추가합니다.
export const FEATURE_FLAGS = {
feature_flags_smoke_test: {type: 'boolean', defaultValue: false},
my_new_feature: {type: 'boolean', defaultValue: false}, // ← 추가
} satisfies Record<string, FlagDefinition>;defaultValue 는 플래그가 없거나 · SDK 초기화 전이거나 · credential 미설정일 때 반환되는 값입니다.
반드시 기존 동작(플래그 도입 전 동작) 을 default 로 두세요. 이 값이 곧 장애 시 fallback 입니다.
3. 코드에서 읽기
'use client';
import {useFeatureFlag} from '@howmuchhome-web/feature-flags';
export function MyScreen() {
const isNewFlowEnabled = useFeatureFlag('my_new_feature');
return isNewFlowEnabled ? <NewFlow /> : <LegacyFlow />;
}키·타입·기본값이 모두 레지스트리에서 추론되므로, 없는 키나 타입이 안 맞는 훅을 쓰면 컴파일 에러가 납니다.
API
| 대상 | 용도 |
|---|---|
useFeatureFlag(key) | boolean 플래그 읽기. 대부분 이것만 씁니다 |
useStringFeatureFlag(key) | string(멀티 variant) 플래그 |
useNumberFeatureFlag(key) | number 플래그 |
useFeatureFlagsReady() | provider 초기화 완료 여부 |
getFeatureFlag(key) | 훅을 못 쓰는 곳(이벤트 핸들러·유틸 함수)에서 동기 조회 |
FeatureFlagProvider | 앱 루트 배선용. 신규 앱을 추가할 때만 씀 |
FeatureFlagUserSync | provider 보다 하위에서 사용자가 밝혀질 때 컨텍스트 갱신 |
깜빡임(flash) 다루기
provider 는 렌더를 블로킹하지 않습니다. 초기화 전에는 defaultValue 가 반환되므로, 값이 true 로 바뀔 때
UI 가 한 번 바뀝니다. 대부분 문제가 안 되지만(default = 기존 동작이므로) 전환이 눈에 거슬리는 화면에서는
useFeatureFlagsReady() 로 게이팅하세요.
const ready = useFeatureFlagsReady();
const enabled = useFeatureFlag('my_new_feature');
if (!ready) return <Skeleton />;
return enabled ? <NewFlow /> : <LegacyFlow />;⚠️ credential 이 없는 환경(로컬 등)에서는 ready 가 계속 false 입니다. 이걸로 콘텐츠를 완전히 감추면
안 됩니다 — 스켈레톤이나 기존 UI 유지 용도로만 쓰세요.
타게팅 컨텍스트
평가 컨텍스트는 packages/feature-flags/context.ts 가 만듭니다.
| 속성 | 값 |
|---|---|
targetingKey | 로그인 사용자는 user id, 비로그인은 localStorage 에 영속되는 익명 id — 버킷팅 단위 |
is_signed_in | boolean |
service | howmuchhome-web / howmuchhome-web-dashboard / howmuchhome-binzip / howmuchhome-binzip-admin |
user_id | 로그인 시에만 |
제약:
- 속성은 flat primitive(string/number/boolean)만 넣습니다. 중첩 객체나 배열을 넣으면 Datadog 이 exposure 이벤트를 조용히 버립니다(에러도 안 남습니다).
- PII 를 넣지 않습니다(email·이름 등). exposure 이벤트는 분석 데이터로 장기 보존됩니다.
- 익명 id 는
packages/logger의generateAnonymousId()와 다릅니다. 후자는 호출마다 새 값을 만들어 저장하지 않으므로 버킷팅 키로 쓰면 페이지 이동마다 variant 가 튑니다. 반드시 패키지가 만드는 키를 쓰세요.
환경 매핑
코드 (FLAG_ENV) | Datadog flagging environment | 조건 (web) | 조건 (native) |
|---|---|---|---|
prod | prod (is_production) | NEXT_PUBLIC_ENV === 'production' | APP_ENV === 'production' (production 빌드) |
dev | dev | 그 외 전부 (로컬·프리뷰) | 그 외 전부 (local·canary·qa 등) |
native 는
NEXT_PUBLIC_ENV가 없고APP_ENV(EXPO_PUBLIC_APP_ENV)로 판별합니다. production 빌드(yarn *:production·*:local:prod)만prod, 나머지 빌드는dev로 평가되므로 Datadog 플래그 targeting 환경을 빌드에 맞춰야 합니다.
prod / dev 표기는 기존 RUM/Logs 의 env 태그와 맞춘 것입니다. exposure 이벤트와 RUM 이벤트를
Experiments 에서 조인할 때 env 태그가 어긋나면 붙지 않기 때문에 production 같은 다른 표기를 쓰면 안 됩니다.
알려진 한계: RUM 의
env는 현재 모든 웹 환경에서'prod'로 하드코딩돼 있습니다 (packages/logger/provider/client/ClientLogger.tsx). 따라서 dev 트래픽은 exposure 가env:dev, RUM 은env:prod로 남습니다. dev 에서 실험을 정확히 분석해야 할 때 RUM env 를 파라미터화하면 되지만, 기존 대시보드·모니터·Error Tracking 필터가env:prod기준이라 재검증이 필요합니다.
필요한 환경변수
RUM 과 동일한 값을 재사용하므로 신규 시크릿은 없습니다.
NEXT_PUBLIC_DATADOG_RUM_APPLICATION_ID=...
NEXT_PUBLIC_DATADOG_RUM_CLIENT_TOKEN=...
NEXT_PUBLIC_ENV=production # prod 환경에서만Vercel 프로젝트 설정에 있습니다(저장소의 .env 파일에는 없습니다). 로컬에서 플래그를 확인하려면
.env.shared 나 앱별 .env.local 에 앞의 두 개를 넣으세요. 없으면 provider 가 등록되지 않고
모든 훅이 defaultValue 를 반환합니다(에러 없음).
비용 주의
enableRumFeatureFlagTracking 이 기본 true 입니다. 플래그 평가가 RUM 이벤트에 붙는 대신
RUM 청구 이벤트 수가 늘어날 수 있습니다. 이게 RUM 상관관계 분석의 핵심 기능이라 켜 두었지만,
플래그를 아주 많이 쓰게 되면 packages/feature-flags/provider.tsx 에서 조정하세요.
플래그 정리 (중요)
플래그는 기술 부채입니다. 롤아웃이 100% 로 끝나면:
- 코드에서 분기를 걷어내고 승자 경로만 남깁니다.
flags.ts의 레지스트리에서 키를 지웁니다.- Datadog 에서 플래그를 archive 합니다.
Datadog 이 오래된 플래그를 stale 로 표시해 주고(list-stale-feature-flags), MCP 의 clean-up-flag 로
정리 작업을 도울 수 있습니다.
실험(Experiments)으로 확장하기
지금 세팅은 실험의 전제조건(플래그 + exposure 파이프라인)까지입니다. 실제 실험을 돌리려면 Datadog UI 에서 추가로 필요합니다:
- 실험 지표(experiment metric) 정의 — 무엇을 성공으로 볼지
- subject type 설정 — 무작위 배정 단위 (우리
targetingKey와 맞춰야 함) - Product Analytics + Feature Flags 권한
그 후 플래그 상세 페이지에서 바로 실험을 만들 수 있습니다.