---
id: backend-feature-flags-deep
title: Feature Flags 심화 — 점진 / A/B / 격리
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [backend, feature-flags, ab-testing, vibe-coding]
tech_stack: { language: "TS / GrowthBook / LaunchDarkly / Statsig", applicable_to: ["Backend", "Frontend"] }
applied_in: []
aliases: [feature flag, ab test, gradual rollout, GrowthBook, LaunchDarkly, Unleash, kill switch]
---

# Feature Flags Deep

> 단순 on/off 가 아님. **% rollout / 사용자 segment / A/B 실험 / kill switch** 4종. SDK + dashboard. **GrowthBook (open) / LaunchDarkly / Statsig**.

## 📖 핵심 개념
- Targeting: 사용자 attribute 기반 (plan / country / cohort).
- Rollout: 0% → 10% → 100% 점진.
- A/B: variant 분배 + metric 측정.
- Kill switch: emergency off.

## 💻 코드 패턴

### GrowthBook (open source)
```ts
import { GrowthBook } from '@growthbook/growthbook';

const gb = new GrowthBook({
  apiHost: 'https://cdn.growthbook.io',
  clientKey: process.env.GB_KEY,
  attributes: {
    id: user.id,
    plan: user.plan,
    country: user.country,
  },
});

await gb.loadFeatures();

if (gb.isOn('new-checkout')) {
  // 새 flow
}

const variant = gb.getFeatureValue('button-color', 'blue'); // 'red' | 'blue'
```

### LaunchDarkly
```ts
import { init, type LDClient } from 'launchdarkly-node-server-sdk';

const ld: LDClient = init(process.env.LD_KEY);
await ld.waitForInitialization();

const flag = await ld.variation('new-checkout', { key: user.id, custom: { plan: user.plan } }, false);
```

### 점진 rollout
```yaml
# Dashboard (GrowthBook)
flag: new-checkout
rules:
  - condition: { plan: 'pro' }
    rollout: 100%
  - condition: { country: 'US' }
    rollout: 10%
  - default: 0%
```

### A/B 실험
```yaml
flag: button-color
experiment:
  hypothesis: red converts higher than blue
  variants:
    - { id: blue, weight: 50 }
    - { id: red, weight: 50 }
  metric: checkout_completed
```

```ts
const color = gb.getFeatureValue('button-color', 'blue');
analytics.track('button_seen', { variant: color });
// CTA 클릭 시
analytics.track('checkout_completed', { variant: color });
```

### Kill switch
```ts
if (gb.isOn('use-new-payment-provider')) {
  await stripeNew.charge(...);
} else {
  await stripeOld.charge(...);
}

// 새 provider 가 깨지면 → flag off → 즉시 fallback
```

### Stable hash (consistent variant)
```ts
import { hash } from '@growthbook/growthbook';

// 같은 user.id = 항상 같은 variant
const bucket = hash('exp-key', user.id, 1) * 100;
const variant = bucket < 50 ? 'a' : 'b';
```

→ 사용자가 새로고침 해도 같은 variant 받음.

### Server-side vs client-side
```ts
// Server: SDK 직접 호출 — 비밀 attribute 사용 가능
gb.isOn('admin-feature'); // 서버에서

// Client: rules 가 client 에 노출 → 비밀 정보 X
// 또는 server-rendered (Next.js)
const flags = await gb.evaluateAll();
return { props: { flags } };
```

### Type-safe flags
```ts
type Flags = {
  'new-checkout': boolean;
  'button-color': 'blue' | 'red' | 'green';
  'max-items': number;
};

function flag<K extends keyof Flags>(key: K): Flags[K] {
  return gb.getFeatureValue(key, defaults[key]) as Flags[K];
}
```

### Cleanup (가장 어려움)
```ts
// 모든 flag = TODO 폴더
// "100% rollout 후 14일 → 코드 cleanup"
// CI 가 오래된 flag 알람
```

```bash
# .github/workflows/flag-audit.yml
- run: gb-cli list-flags --age-gt 90d
```

### Local override (dev / test)
```ts
// 환경변수로 override
const overrides = JSON.parse(process.env.FLAG_OVERRIDES ?? '{}');
gb.setForcedFeatures(overrides);
```

```ts
// 테스트
gb.setForcedFeatures({ 'new-checkout': true });
```

### Audit log
- 누가 / 언제 / 무엇을 toggle 했는지.
- Rollout 변화 history.
- Compliance 요구.

## 🤔 의사결정 기준
| 규모 | 추천 |
|---|---|
| 작은 / 자체 호스트 | GrowthBook OSS / Unleash |
| 큰 / 매니지드 | LaunchDarkly / Statsig |
| 실험 자동 분석 | Statsig (자동 stats) |
| 단순 on/off | env var + ConfigMap |
| Edge runtime | Cloudflare Flags / GrowthBook Edge |

## ❌ 안티패턴
- **Flag 쌓기 + 정리 안 함**: 100+ flag = 코드 스파게티.
- **Cleanup 정책 없음**: 누구도 안 지움.
- **Random variant — sticky X**: 사용자가 매번 다른 variant.
- **Attribute 안 정의 — global 만**: targeting 못 함.
- **Flag 안에 비즈니스 결정**: dashboard 가 source of truth — 코드 의도 잃음.
- **Critical path flag check 매번 fetch**: cache.
- **Flag failure → 앱 다운**: default 명시 + 안전 fallback.

## 🤖 LLM 활용 힌트
- 새 feature = flag 부터.
- % rollout + segment + kill switch 3종.
- 정리 정책 + audit log 필수.

## 🔗 관련 문서
- [[Feature_Flags_in_Practice]]
- [[Backend_API_Gateway_BFF]]
- [[AI_LLM_Eval_Patterns]]
