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

# 기능 플래그 실전

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

## 📖 핵심 개념

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

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

## 💻 코드 패턴

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

```ts
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 — 부팅 시 평가 + 캐시

```ts
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 — 사용자별 분기

```ts
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) — 매 요청 평가

```ts
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

```ts
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` 추가.

## 🔗 관련 문서

- [[Idempotent_Operations]]
- [[Optimistic_Concurrency_Control]]
- [[Backpressure_Patterns]]
