docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거

Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
This commit is contained in:
Antigravity Agent
2026-07-05 00:33:48 +09:00
parent 1cfd3bbb56
commit 9148c358d0
6455 changed files with 1 additions and 86875 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]]