Skip to content

✍️ 필사 모드: Feature Flag와 Progressive Delivery 완전 해부 — Dark Launch, Canary, Rolling, A/B, OpenFeature까지

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

들어가며 — "배포했는데 아직 기능은 없어요"

현대 소프트웨어의 이상한 원칙:

"배포 ≠ 릴리스"

새 코드를 운영 서버에 올리는 것(배포)과, 사용자에게 기능을 켜주는 것(릴리스)은 다르다. 둘을 섞으면 배포가 곧 공포다. 코드 한 줄 바꿀 때마다 "장애 나면 어쩌지"라는 마음으로 롤백 준비.

분리하면? 배포는 안전한 일상이 되고, 릴리스는 비즈니스 결정이 된다. 이걸 가능케 하는 기술이 Feature Flag다. 이 글에서는:

  • Feature Flag의 역사 — Facebook의 "Gatekeeper" 비밀
  • 4가지 플래그 타입 — release, experiment, ops, permission
  • Progressive Delivery — Canary, Rolling, Blue/Green
  • A/B 테스팅과 Feature Flag의 차이
  • Trunk-based Development — 브랜치 지옥 탈출
  • 주요 도구 비교 (LaunchDarkly, Unleash, GrowthBook, Flagsmith)
  • Open Feature — CNCF 표준화 움직임
  • 플래그 부채 관리 — "좀비 플래그"의 문제
  • 실전 롤아웃 전략 10개

이전 글 Chaos Engineering 완전 해부에서 "프로덕션에서 실패를 탐색한다"고 했다. Feature Flag는 "프로덕션에서 안전하게 신기능을 탐색"하는 반대편 짝이다.


1. 기원 — Facebook의 Gatekeeper

2004~2008 Facebook의 문제

Facebook이 기능을 매주 하나씩 내놓고 싶은데:

  • 버그 나면 수백만 사용자 영향
  • 롤백은 코드 revert + 재배포 → 수 시간
  • QA 환경이 프로덕션과 다름

내부 도구 "Gatekeeper"

엔지니어가 코드에 간단한 체크를 넣음:

if (Gatekeeper::check("new_newsfeed", user)) {
  return renderNewFeed();
} else {
  return renderOldFeed();
}

관리자가 중앙 서버에서 "new_newsfeed를 10% 사용자에게"로 바꾸면 즉시 반영. 재배포 없음.

Dark Launch

더 나아가 "Dark Launch" — 기능을 UI에 노출하지 않고 백엔드만 동작시켜 부하를 먼저 측정. 2008년 Facebook Chat 출시 때 수 주간 Dark Launch로 서버 용량 검증. 덕분에 기능 공개 첫날 문제 없음.

이 패턴이 2010년대 Etsy, Netflix, Google로 퍼지고 LaunchDarkly (2014) 같은 SaaS의 탄생으로 대중화.


2. Feature Flag의 4가지 타입 — 수명과 목적으로 분류

Pete Hodgson의 분류 (martinfowler.com 2017)가 업계 표준:

1. Release Toggle (배포 토글)

  • 수명: 단기 (며칠~몇 주)
  • 목적: 미완성 기능을 main 브랜치에 머지하되 사용자에겐 숨김
  • : 결제 시스템 개편, UI 리뉴얼

2. Experiment Toggle (실험 토글)

  • 수명: 중기 (실험 기간)
  • 목적: A/B 테스트 — 어떤 변형이 더 나은 전환율?
  • : CTA 버튼 색, 가격 표시 방식

3. Ops Toggle (운영 토글)

  • 수명: 장기 또는 영구
  • 목적: 장애 대응 — 특정 기능 긴급 비활성화
  • : 외부 API 의존 기능, 고부하 리포트

4. Permission Toggle (권한 토글)

  • 수명: 영구 (제품의 일부)
  • 목적: 플랜/역할 기반 기능 제어
  • : Premium 기능, 관리자 권한

분류가 중요한 이유

수명 관리가 다름:

  • Release Toggle은 출시 후 즉시 제거해야 함 (안 하면 부채)
  • Experiment Toggle은 결과 확정 후 한쪽 유지, 다른 쪽 제거
  • Ops Toggle은 영구 유지 (코드로 인프라 제어)
  • Permission Toggle은 영구 유지 (비즈니스 로직)

혼동하면 "무슨 플래그가 몇 개인지 모르는 정글" 탄생.


3. Progressive Delivery — 점진적 공개 전략

Big Bang Release의 위험

예전엔 배포 = "모든 사용자에게 한 번에". 버그 나면 수백만 영향.

Canary Deployment

일부 서버나 일부 사용자에게 먼저 새 버전:

100% 트래픽
 ├── 95% → 구버전 v1.0
 └── 5%  → 신버전 v1.1 (카나리)

광산의 카나리처럼 소수가 먼저 위험을 감지. 1% → 5% → 25% → 100%로 점진.

Rolling Deployment

서버 인스턴스를 한 번에 몇 대씩 교체. Kubernetes의 기본 전략.

strategy:
  type: RollingUpdate
  rollingUpdate:
    maxUnavailable: 25%
    maxSurge: 25%

장점: 단순, 추가 자원 적음. 단점: 롤백이 느림 (다시 롤링).

Blue/Green Deployment

완전히 같은 구성 두 세트(blue=현재, green=새).

  1. Green에 새 버전 배포
  2. 테스트
  3. 트래픽을 한 번에 blue → green 전환
  4. 문제 없으면 blue 폐기, 문제 있으면 blue로 복귀

장점: 즉각 롤백. 단점: 자원 2배, DB 스키마 호환성 관리.

Feature Flag 기반 배포

위의 "서버 수준" 전략과 달리 사용자 수준 제어:

같은 코드 배포 후
 → Flag: new_checkout
 → 사용자 IDs 1~1000에게만 켬
 → 전환율 측정
 → 좋으면 100%로 확장

장점:

  • 서버 재배포 없이 제어
  • 사용자 단위 정교한 타겟팅
  • 순간 롤백 (플래그 OFF)

4. A/B 테스트 vs Feature Flag

겹치는 부분

둘 다 "일부 사용자에게만 다른 동작 보여주기".

다른 부분

측면Feature FlagA/B 테스트
목적배포 안전성, 권한 제어통계적 실험
측정기본 오류율비즈니스 지표 (전환율, LTV)
기간가변유의성 도달까지
설계단순 ON/OFF통제된 실험 (random, 표본 수)
도구LaunchDarkly, UnleashOptimizely, VWO, GrowthBook

현실 — 통합 플랫폼

LaunchDarkly, GrowthBook은 Feature Flag + A/B를 하나의 플랫폼에서 제공. 같은 "규칙 엔진" 기반이라 자연스러움.

A/B 테스트의 통계 함정

  • P-hacking — 지표 여러 개 놓고 우연히 유의한 것만 보고
  • Peeking — 실험 중간에 결과 보고 "통계적 유의성 달성 시 즉시 종료" → 실제론 유의 X
  • Sample Ratio Mismatch (SRM) — A:B가 50:50으로 나누려 했는데 48:52면 랜덤화 버그 의심
  • Novelty Effect — 새 UI가 처음엔 호기심으로 클릭, 2주 후엔 평범

통계학자 협업 권장. Sequential Testing (CUPED, mSPRT) 같은 기법이 peeking 문제 해결.


5. Trunk-based Development — 긴 브랜치의 종말

Git Flow의 한계

2010년 Vincent Driessen의 Git Flow:

  • feature/xxx 브랜치
  • develop 브랜치
  • release/xxx 브랜치
  • hotfix 브랜치
  • main 브랜치

이론상 깨끗. 현실은:

  • 머지 지옥 — 브랜치 오래 살면 충돌 폭증
  • 통합 지연 — 기능이 통합되지 않은 채 2주 이상
  • "크리스마스 트리" 배포 — 일주일치 변경을 한 번에 올림, 장애 나면 원인 불명

Trunk-based의 제안

  • 모든 개발자가 main(trunk)에 직접(또는 1일 수명 브랜치에서) 커밋
  • 배포는 매일 또는 매시간
  • 미완성 기능은 Feature Flag로 숨김
  • 주요 기업: Google, Facebook, Netflix, Amazon

장점

  • 통합이 매일 → 충돌 최소
  • 린 딜리버리
  • 모든 커밋이 잠재적 배포

단점 / 필수 조건

  • 강력한 CI 파이프라인
  • 자동화된 테스트
  • Feature Flag 인프라
  • 팀의 숙련도

"우리 팀에서는 안 통해요"는 대부분 CI/CD 성숙도가 원인.


6. 도구 생태계 비교

LaunchDarkly (2014)

  • 업계 리더, 엔터프라이즈 중심
  • 거의 모든 언어 SDK (20+)
  • 강력한 experimentation
  • 가격: 사용자 수 기반, 비싼 편

Unleash (2014, Open Source)

  • 오픈소스 (Apache 2.0)
  • 셀프호스팅 가능 (데이터 주권)
  • Slack/GitHub/Jira 통합
  • Enterprise 버전 유료

GrowthBook (2020)

  • 오픈소스
  • Experimentation first (A/B 중심)
  • BigQuery/Snowflake 직접 연결해서 지표 계산
  • Data team 친화적

Flagsmith (2021)

  • 오픈소스
  • Remote config 기능 강조
  • 모바일 앱 친화적

ConfigCat

  • 중소 SaaS 친화 가격
  • 다국가 엣지 배포

자체 구현

구글/메타/넷플릭스는 자체 인프라. 수천 플래그 관리에 SaaS로는 부족. 하지만 초기부터 자체 구현은 99% 낭비 — 초기엔 Unleash 자체호스팅 또는 Flagsmith 권장.


7. Open Feature — 표준화의 시대

문제

앱 하나가 LaunchDarkly 쓰다 Unleash로 옮기려면? 전체 코드에서 SDK 교체. 테스트 재작성. 이주 프로젝트.

Open Feature (2022, CNCF)

표준 API를 정의하고, 각 벤더가 Provider 구현을 제공. 애플리케이션은 Open Feature API만 호출. 벤더 교체 = Provider만 교체.

import { OpenFeature } from '@openfeature/server-sdk';
import { LaunchDarklyProvider } from '@openfeature/launchdarkly-provider';

OpenFeature.setProvider(new LaunchDarklyProvider({ sdkKey: '...' }));

const client = OpenFeature.getClient();
const showNewUi = await client.getBooleanValue('new-ui', false, context);

나중에 Unleash로 바꾸려면 setProvider만 교체. 나머지 코드 그대로.

2025 생태계

  • 공식 Provider: LaunchDarkly, Flagsmith, Split, ConfigCat, GoFeatureFlag
  • SDK: Node, Java, Go, Python, .NET, Ruby, PHP, C++, Swift, Kotlin

CNCF Incubating 상태. 빠르게 표준으로 자리잡는 중.


8. 플래그 부채 관리 — 좀비 플래그의 공포

부채가 어떻게 쌓이는가

  1. 엔지니어 A가 Release Toggle 만듦
  2. 기능 100% 출시 완료
  3. A는 다른 일로 바쁨 → 플래그 코드 제거 잊음
  4. 몇 달 뒤 코드에 "만약 이거 꺼져 있으면..." 분기 남음

결과:

  • 조건 분기 폭증 — 코드 복잡도
  • 테스트 행렬 폭발 — 2^N 가지 조합
  • 이해 불가 — "이 플래그 OFF면 무슨 일이?" 아는 사람 없음

대응 전략

1. 플래그 Naming Convention

[타입]_[팀]_[기능]_[만료일]
release_checkout_new-ui_2026-06-01

2. Expiry Dates + 알림

  • 각 플래그에 만료일. 지나면 Slack 알림.
  • LaunchDarkly는 "Flag Expiration" 기능 내장.

3. 정기 Flag Audit

  • 매 분기 미팅에서 "이 플래그 아직 필요?" 점검
  • Cleanup PR 전담자 지정

4. Static Analysis

  • IDE 린트로 "사용 안 되는 플래그" 탐지
  • ESLint, Sonar 플러그인 활용

5. "플래그 예산" 개념

  • 팀당 활성 플래그 50개 이하 유지 같은 정책

유명한 실패 사례

Knight Capital 2012년 4.5억 달러 손실:

  • 8년 된 사용하지 않는 플래그를 실수로 재활성화
  • 옛 트레이딩 로직이 다시 켜지며 45분만에 회사 파산
  • 교훈: 부채 방치는 직접 비용

9. 실전 롤아웃 전략 10가지

전략 1: Percentage-based Rollout

1% → 5% → 25% → 50% → 100%
각 단계 24시간 관찰 후 진행

전략 2: Ring-based Deployment

사용자를 링으로 분류:

Ring 0: 내부 직원 (도그푸딩)
Ring 1: 얼리 어답터 (베타 참여자)
Ring 2: 무료 사용자
Ring 3: 유료 사용자
Ring 4: 엔터프라이즈

Ring 0→4로 점진 공개. Microsoft가 Windows 업데이트에 이 방식 사용.

전략 3: 지리적 롤아웃

Week 1: 뉴질랜드, 호주 (작은 시장)
Week 2: 유럽 일부
Week 3: 미국
Week 4: 전 세계

장점: 작은 시장에서 먼저 검증, 시차로 24시간 모니터링.

전략 4: 세그먼트 기반

IF user.is_premium AND user.country = 'KR'
THEN flag = ON

전략 5: Sticky Bucketing

같은 사용자는 항상 같은 결과. hash(user_id + flag_name) % 100 < rollout_percent.

전략 6: Holdout Group

장기 실험에서 소수 사용자는 영구 신기능 제외. 실제 비즈니스 임팩트 측정용.

전략 7: Automated Rollback

if error_rate > 1% OR p99 > 500ms:
  rollback_flag(immediate)

관측성과 통합된 자동화가 SRE 시대의 기본.

전략 8: Shadow Traffic

프로덕션 요청을 복제해서 새 버전에도 보냄. 응답은 버림. 실제 부하 영향 평가.

전략 9: Feature Flag + Database Migration

  • 플래그 OFF: 기존 스키마 사용
  • 플래그 50%: 이중 쓰기 — 새/옛 스키마 모두
  • 플래그 100%: 새 스키마만

전략 10: Kill Switch

외부 API 고장 시 기능 자체를 비활성화. Ops Toggle의 전형.

if (externalAPI.isDown()) {
  flag.force("use-fallback", ON);
}

10. 플래그 평가의 아키텍처

Client-side vs Server-side

Client-side (브라우저/모바일)

  • 장점: 서버 왕복 없음, 빠름
  • 단점: 규칙 엔진이 클라이언트에 노출 (보안), SDK 키 유출 위험
  • 민감 플래그 X

Server-side

  • 장점: 규칙 보호, 개인정보 서버에 머무름
  • 단점: 매 요청 또는 주기적 동기화 필요

평가 지연 최적화

Streaming Updates: SDK가 SSE/Polling으로 실시간 업데이트. 몇 초 이내 전파.

Local Evaluation: 규칙 전체를 SDK에 다운로드 → 로컬 평가. 네트워크 왕복 0.

Edge Evaluation: Cloudflare Workers 같은 엣지에서 평가. 글로벌 저지연.


11. 실무 함정과 안티패턴

함정 1: 플래그가 비즈니스 로직에 스며듦

// 나쁜 예
if (flag("feature-a")) {
  if (flag("feature-b")) {
    if (user.country === 'KR' && flag("feature-c")) {
      // ...
    }
  }
}

플래그 트리 × 사용자 속성 × 여러 언어 = 유지보수 재앙.

해결: 플래그는 "기능 추가/제거" 수준으로 얇게. 복잡한 분기는 상위 설계로.

함정 2: 실험 결과를 제대로 분석 안 함

"플래그 켰더니 지표 좋아진 것 같음" → 과학이 아닌 추측. 통계 엄격함 필수.

함정 3: 테스트 환경이 모든 플래그 조합 커버 X

"플래그 A=ON, B=OFF, C=?" 조합마다 다른 동작. 모든 조합 테스트는 불가능 → 최상위 경로 통합 테스트로 한정.

함정 4: 플래그가 캐시됨

CDN이 응답을 캐시했는데 플래그가 바뀌면? 사용자 단위로 캐시 키 포함 또는 짧은 TTL.

함정 5: DB/외부 API에 의존하는 플래그

플래그 평가에 DB 쿼리가 붙으면? 매 요청마다 DB 접근 → 느림. 플래그는 반드시 빠른 로컬 평가.

함정 6: 플래그로 롤백 한 뒤 잊어버림

장애 대응으로 플래그 OFF → 근본 원인 미해결 → 다음에 똑같이 터짐. Ops Toggle은 반드시 후속 포스트모템.


12. 실전 체크리스트 12가지

  1. 플래그 타입을 명시 — Release/Experiment/Ops/Permission
  2. 만료일 설정 + 알림 — 좀비 플래그 방지
  3. Open Feature API 채택 — 벤더 종속 회피
  4. 플래그를 얇게 — 비즈니스 로직에 깊이 끼지 않게
  5. 관측성과 통합 — 플래그별 오류율/지연 자동 측정
  6. 자동 롤백 트리거 — 지표 이탈 시 OFF
  7. Sticky Bucketing — 같은 사용자 일관 경험
  8. Holdout Group — 장기 비즈니스 영향 측정
  9. 정기 Flag Audit — 분기 1회 최소
  10. Naming Convention — 검색성 확보
  11. SDK 평가는 로컬 — 네트워크 지연 피하기
  12. Trunk-based 전환 시 플래그가 기반 — CI/CD와 세트로

다음 글 예고 — Database Migration과 Zero-Downtime 전환

Feature Flag가 "기능 롤아웃"의 기본기라면, Zero-downtime DB Migration은 "스키마 롤아웃"의 기본기다. 둘은 닮았다. 다음 글에서는:

  • Expand-Contract 패턴 — 스키마 변경의 정석
  • Blue/Green 데이터베이스의 현실
  • Dual-Write 함정과 CDC 기반 이주
  • 외부 키 제약, 인덱스 추가의 Online 기법
  • pt-online-schema-change, gh-ost (GitHub)의 원리
  • PostgreSQL CONCURRENTLY 패턴
  • Logical Replication으로 메이저 버전 업그레이드
  • NoSQL 마이그레이션 전략 (MongoDB, DynamoDB)
  • 수십억 행 테이블 실전 사례

"스키마 바꾸면 테이블 락 걸려요 어떻게 해요?" "메이저 업그레이드 다운타임 없이 가능한가요?" 같은 질문의 답이 다음 글에 있다.

"데이터는 코드보다 무겁다. 하지만 그 무게를 분산시킬 방법은 있다."

현재 단락 (1/241)

현대 소프트웨어의 이상한 원칙:

작성 글자: 0원문 글자: 7,828작성 단락: 0/241