[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
---
|
||||
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]]
|
||||
Reference in New Issue
Block a user