Files
2nd/10_Wiki/Topics/Coding/Feature_Flags_in_Practice.md
T
2026-05-09 21:08:02 +09:00

5.5 KiB

id, title, category, status, canonical_id, aliases, duplicate_of, source_trust_level, confidence_score, verification_status, created_at, updated_at, last_reinforced, review_reason, merge_history, tags, raw_sources, tech_stack, applied_in
id title category status canonical_id aliases duplicate_of source_trust_level confidence_score verification_status created_at updated_at last_reinforced review_reason merge_history tags raw_sources tech_stack applied_in
feature-flags-in-practice 기능 플래그 실전 (Feature Flags in Practice) Coding draft feature-flags-in-practice
feature toggle
feature gate
kill switch
dark launch
기능 플래그
null B 0.85 conceptual 2026-05-09 2026-05-09 2026-05-09
coding
deployment
release
experimentation
vibe-coding
P-Reinforce session 2026-05-09 — bulk Coding seed batch 1
language applicable_to
TypeScript / 모든 언어
Backend
Frontend
Mobile

기능 플래그 실전

배포와 릴리즈를 분리한다. 코드는 머지·배포되어도 사용자에겐 안 보일 수 있어야 한다. 플래그는 4종 — Release / Experiment / Ops / Permission — 으로 나누고 수명도 다르게.

📖 핵심 개념

기능 플래그는 "이 코드 경로를 누구에게 보일까?" 를 런타임에 결정하는 스위치. 단일 도구가 아니라 수명·평가 주기·기록 정책이 다른 4종으로 분류:

종류 목적 수명 평가 빈도
Release Toggle 배포 ≠ 릴리즈. 롤백 가능 짧음 (< 30일) 부팅 시
Experiment Toggle A/B 테스트, 트래픽 분기 중간 (실험 기간) 사용자별
Ops Toggle (kill switch) 사고 시 즉시 차단 무기한 매 요청
Permission Toggle 유료 / 베타 사용자 무기한 사용자별

💻 코드 패턴

1. 평가 추상화 (구체 도구 격리)

export interface FeatureFlags {
  isOn(flag: string, ctx?: { userId?: string }): boolean;
  variant<T extends string>(flag: string, ctx?: { userId?: string }): T | undefined;
}

LaunchDarkly / Unleash / GrowthBook / 자체 DB — 모두 위 인터페이스 뒤에. 비즈니스 코드는 도구를 모름.

2. Release Toggle — 부팅 시 평가 + 캐시

const newCheckoutEnabled = flags.isOn('release.new-checkout');

app.post('/api/checkout', async (req, res) => {
  if (newCheckoutEnabled) return newCheckoutHandler(req, res);
  return legacyCheckoutHandler(req, res);
});

부팅 시 한 번 읽는 게 일반적. 변경하려면 재배포 또는 reload.

3. Experiment Toggle — 사용자별 분기

app.get('/dashboard', async (req, res) => {
  const variant = flags.variant<'control' | 'B'>('exp.dashboard-redesign', { userId: req.user.id });
  if (variant === 'B') return renderDashboardB(res);
  return renderDashboardA(res);
});

userId hash 로 sticky 분기. 같은 사용자는 같은 variant.

4. Ops Toggle (Kill Switch) — 매 요청 평가

async function callExternalProvider(input: Input) {
  if (flags.isOn('ops.disable-provider-x')) {
    return { ok: false, reason: 'TEMPORARILY_DISABLED' };
  }
  return providerX.call(input);
}

장애 발생 시 1분 내 차단. 매 호출 평가 필수 — 부팅 캐시는 여기에 부적합.

5. Permission Toggle

function canAccessAdvancedAnalytics(user: User): boolean {
  if (user.plan === 'enterprise') return true;
  if (flags.isOn('beta.analytics', { userId: user.id })) return true;
  return false;
}

🤔 의사결정 기준

상황 플래그 종류 수명 비고
큰 리팩터 / DB 마이그레이션 Release 머지 후 1~2주 dual-write, dual-read 패턴
새 UI vs 기존 UI 비교 Experiment 실험 기간 통계 유의성 도달 후 종료
외부 API 사고 격리 Ops 무기한 매 요청 평가
유료 플랜 기능 Permission 무기한 보통 DB 기반, 플래그 시스템과 통합 가능
Frontend 기능 점진 노출 Release / Experiment 짧음 client-side 평가 시 보안 주의

안티패턴

  • 플래그 무덤: 다 끝난 Release Toggle 이 1년 넘게 코드에 남아 있음. 만료일 설정 + 정기 청소. CI 에서 expired flag 검증 도구.
  • 분기마다 새 함수 만들지 않음: if (flag) { ... 100 lines ... } else { ... 100 lines ... } 형태로 한 함수에 두 분기. 두 함수로 분리.
  • 테스트가 한 분기만 검증: 양 분기 모두 테스트. 끄고 켜고 둘 다.
  • 사용자 ID 없이 hash: 같은 사용자에게 매번 다른 variant. sticky 보장 안 됨.
  • Ops Toggle 을 부팅 캐시: 사고 시 차단이 안 됨. ops 는 매 요청 평가.
  • 민감 분기를 client-side 평가: 사용자가 토글 가능. 권한 / 보안 게이트는 server-side.
  • flag dependency hell: A 가 B 에 의존, B 가 C 에 의존 — 어떤 조합이 유효한지 모름. 평가 매트릭스 문서화.
  • flag 전파에 retry 없음: flag 시스템 장애 시 default 동작 명확히 (보통 "off" 가 안전).

🤖 LLM 활용 힌트

  • LLM에게 새 기능 코드 작성: "flag 뒤에 두고, 두 분기를 별도 함수로 분리" 명시.
  • 큰 리팩터: "release toggle + dual-read/write 단계로 점진적 전환" 패턴 요청.
  • 사고 대응 코드: "외부 의존성마다 ops kill switch 추가" 명시.
  • 청소: 코드에 flag.isOn('release.X') 검색 → 만료일 30일 지난 것 보고.

🧪 검증 상태

  • verification_status: conceptual
  • Martin Fowler "Feature Toggles" 분류, Pete Hodgson 의 4종 분류가 표준.
  • 적용 사례 발견 시 applied_in 추가.

🔗 관련 문서