LabHub

블로그

Feature Flag와 Progressive Delivery 완전 해부 — Dark Launch, Canary, Rolling, A/B, OpenFeature까지

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

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

"배포 ≠ 릴리스"

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

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

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


1. 기원 — Facebook의 Gatekeeper

2004~2008 Facebook의 문제

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

내부 도구 "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 (배포 토글)

2. Experiment Toggle (실험 토글)

3. Ops Toggle (운영 토글)

4. 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%로 확장

장점:


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 테스트의 통계 함정

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


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

Git Flow의 한계

2010년 Vincent Driessen의 Git Flow:

이론상 깨끗. 현실은:

Trunk-based의 제안

장점

단점 / 필수 조건

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


6. 도구 생태계 비교

LaunchDarkly (2014)

Unleash (2014, Open Source)

GrowthBook (2020)

Flagsmith (2021)

ConfigCat

자체 구현

구글/메타/넷플릭스는 자체 인프라. 수천 플래그 관리에 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 생태계

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


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

부채가 어떻게 쌓이는가

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

결과:

대응 전략

1. 플래그 Naming Convention

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

2. Expiry Dates + 알림

3. 정기 Flag Audit

4. Static Analysis

5. "플래그 예산" 개념

유명한 실패 사례

Knight Capital 2012년 4.5억 달러 손실:


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

전략 10: Kill Switch

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

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

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

Client-side vs Server-side

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

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은 "스키마 롤아웃"의 기본기다. 둘은 닮았다. 다음 글에서는:

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

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

댓글

아직 댓글이 없습니다.

로그인하면 댓글을 쓸 수 있습니다