[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-09 21:08:02 +09:00
parent f0befc887a
commit 93ec7e9056
363 changed files with 68333 additions and 64 deletions
@@ -0,0 +1,192 @@
---
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]]