Feature Flag와 점진적 배포 운영법: Dark Launch, Kill Switch, 실험, Flag Debt 정리까지

배포와 출시를 분리한다는 것
Feature flag의 가장 큰 가치는 배포(deploy)와 출시(release)를 분리하는 것입니다. 코드는 production에 배포하되, 기능은 특정 사용자에게만 켜거나 아예 꺼둘 수 있습니다. 이 단순한 분리가 배포 리스크를 크게 낮춥니다. 문제가 생기면 rollback 대신 flag를 끄고, 새 기능은 내부 사용자부터 천천히 열 수 있습니다.
하지만 feature flag는 잘못 운영하면 또 다른 복잡도가 됩니다. 오래된 flag가 코드 곳곳에 남고, 어떤 조합이 실제 production 상태인지 알 수 없고, 실험 flag와 권한 flag와 kill switch가 섞입니다. 결국 "flag를 켜면 무슨 일이 생기는지" 아무도 확신하지 못하는 상태가 됩니다. Feature flag는 기능이 아니라 운영 시스템으로 다뤄야 합니다.
이 글에서는 dark launch, percentage rollout, cohort targeting, kill switch, 실험 지표, flag cleanup까지 실무 운영 패턴을 정리합니다. 목표는 더 많은 flag를 만드는 것이 아니라, 안전하게 켜고 빠르게 지우는 것입니다.

1. Flag 유형을 구분한다
모든 flag가 같은 목적을 갖지 않습니다. Release flag는 새 기능을 점진적으로 열기 위한 임시 flag입니다. Experiment flag는 A/B 테스트와 지표 검증을 위한 flag입니다. Ops flag는 장애 대응을 위한 kill switch나 degrade switch입니다. Permission flag는 특정 고객이나 플랜에 기능을 제공하기 위한 장기 flag입니다.
유형을 구분하지 않으면 관리 기준이 흐려집니다. Release flag는 출시가 끝나면 제거되어야 합니다. Experiment flag는 실험 종료 후 winner를 반영하고 삭제해야 합니다. Ops flag는 항상 빠르게 찾고 조작할 수 있어야 하며, 변경 시 감사 로그가 필요합니다. Permission flag는 제품 권한 모델과 연결되어 장기 유지될 수 있습니다.
Flag metadata에는 owner, 생성일, 만료 예정일, 유형, 관련 티켓, 기본값, fallback 동작이 있어야 합니다. 코드에 boolean 하나만 추가하면 시작은 빠르지만, 몇 달 뒤 아무도 책임지지 않는 flag가 됩니다. Flag는 생성 순간부터 삭제 계획이 있어야 합니다.
2. Dark Launch와 점진적 Rollout
Dark launch는 기능 코드를 production에 배포하되 사용자에게 노출하지 않는 방식입니다. 내부 사용자, QA 계정, 특정 tenant에게만 켜서 실제 production 데이터와 의존성에서 동작을 확인할 수 있습니다. Staging에서 잡히지 않는 권한, 데이터 분포, 외부 API 문제를 조기에 발견할 수 있습니다.
Percentage rollout은 사용자 일부에게 기능을 여는 방식입니다. 중요한 것은 안정적인 bucketing입니다. 같은 사용자가 요청마다 켜졌다 꺼지면 경험이 깨집니다. User ID나 tenant ID를 해시해 1%, 5%, 25%, 50%, 100%처럼 점진적으로 늘립니다. 단순 random은 테스트에는 괜찮지만 production rollout에는 부적합합니다.
Rollout 단위도 선택해야 합니다. 사용자 단위 rollout은 일반 기능에 적합합니다. Tenant 단위 rollout은 B2B SaaS에서 고객별 영향 범위를 통제하기 좋습니다. Region 단위 rollout은 인프라 변경이나 latency 민감 기능에 유용합니다. 기능의 blast radius에 맞춰 rollout key를 정해야 합니다.
점진적 배포에는 지표가 따라와야 합니다. Error rate, latency, conversion, business KPI를 rollout 단계별로 비교합니다. 5%에서 문제가 보이면 10%로 올리지 않습니다. Flag 시스템은 배포 버튼이 아니라 관측과 의사결정 루프입니다.
3. Kill Switch는 빠르게 꺼져야 한다
Kill switch는 장애 시 특정 기능을 즉시 끄는 flag입니다. 외부 결제사 장애 시 결제 수단 하나를 숨기거나, 추천 시스템 장애 시 기본 목록으로 대체하거나, 무거운 리포트 생성을 막는 식입니다. Kill switch는 production 장애 중 사람이 가장 빨리 조작할 수 있어야 합니다.
Kill switch는 기본값과 실패 모드가 중요합니다. Flag provider가 장애일 때 기능을 켤지 끌지 결정해야 합니다. 결제처럼 위험한 기능은 provider 장애 시 보수적으로 끄는 것이 맞을 수 있습니다. 반대로 로그인처럼 핵심 기능은 마지막으로 알고 있던 값을 캐시해 유지해야 할 수 있습니다. 모든 flag에 같은 fallback 정책을 적용하면 안 됩니다.
Kill switch 변경은 감사 로그가 필요합니다. 누가 언제 어떤 이유로 껐고, 언제 다시 켰는지 남아야 합니다. 장애 후에는 kill switch가 얼마나 오래 켜져 있었는지, 사용자 영향은 무엇이었는지 분석해야 합니다. 임시로 끈 기능이 몇 주 동안 방치되는 경우가 흔합니다.
4. Experiment Flag와 지표 오염
A/B 테스트도 feature flag 위에서 돌아가는 경우가 많습니다. 실험에서는 사용자 배정의 안정성과 지표 정의가 중요합니다. 사용자가 A/B 그룹을 오가면 실험 결과가 오염됩니다. 로그인 전 사용자, 여러 디바이스, 쿠키 삭제, tenant 사용자 구조를 고려해야 합니다.
실험 flag는 rollout flag와 목적이 다릅니다. Rollout은 안정성 확인이 목표이고, experiment는 제품 지표 비교가 목표입니다. 둘을 같은 flag로 처리하면 분석이 어려워집니다. 예를 들어 10% rollout 중 에러가 없어 50%로 올리는 행위와, 50:50 실험으로 전환율을 비교하는 행위는 다릅니다.
Guardrail metric도 필요합니다. 전환율이 좋아 보여도 latency가 나빠지거나 고객지원 문의가 늘면 좋은 실험이 아닙니다. 실험 대시보드는 primary metric과 guardrail metric을 함께 봐야 합니다. 실험 종료 후에는 winner를 코드 기본 경로로 반영하고 flag를 제거합니다.
5. Flag Debt를 관리한다
Feature flag의 가장 큰 비용은 오래된 분기입니다. if (flag)와 else가 수개월 남아 있으면 테스트 조합이 늘고, 리팩터링이 어려워지고, 죽은 코드가 유지됩니다. 이것을 flag debt라고 볼 수 있습니다. 기술 부채와 마찬가지로 관리하지 않으면 코드베이스를 느리게 만듭니다.
Flag cleanup은 프로세스에 넣어야 합니다. Release flag에는 만료일을 두고, 만료일이 지나면 CI나 bot이 알립니다. Flag provider에는 stale flag 리포트가 있어야 하고, 코드 검색으로 참조가 사라졌는지 확인합니다. 삭제 PR은 기능 개발과 별개로 정기적으로 처리해야 합니다.
테스트 전략도 필요합니다. 모든 flag 조합을 테스트할 수는 없습니다. 대신 기본값, rollout 중 핵심 조합, kill switch off 상태를 우선합니다. Permission flag처럼 장기 유지되는 flag는 제품 권한 테스트에 포함하고, release flag는 수명 동안만 테스트합니다.
실무 체크리스트
- Flag 유형이 release, experiment, ops, permission으로 구분되어 있는가
- 모든 flag에 owner, 생성일, 만료일, 기본값, fallback 정책이 있는가
- Percentage rollout이 안정적인 hash bucketing으로 동작하는가
- Rollout 단계별 error rate, latency, business metric을 비교하는가
- Kill switch가 provider 장애 시 어떤 값을 쓸지 정의되어 있는가
- Flag 변경 감사 로그와 장애 후 리뷰가 남는가
- Experiment flag와 rollout flag를 목적에 맞게 분리하는가
- 만료된 flag를 찾고 삭제하는 정기 프로세스가 있는가
6. Flag 설정도 배포처럼 리뷰한다
Feature flag는 코드 배포 없이 production 동작을 바꿀 수 있습니다. 그만큼 flag 변경은 배포와 같은 수준의 주의가 필요합니다. 특히 결제, 권한, 가격, 알림, 데이터 삭제처럼 사용자 영향이 큰 기능의 flag는 단독 운영자가 즉흥적으로 바꾸면 안 됩니다. 변경 이유, 영향 범위, rollback 방법, 관측 지표가 있어야 합니다.
Flag 관리 도구에는 환경별 권한을 나눠야 합니다. 개발 환경 flag를 바꾸는 권한과 production kill switch를 조작하는 권한은 다릅니다. Production 변경에는 승인 워크플로우나 최소한 감사 로그가 필요합니다. 누가 언제 어떤 tenant에게 어떤 값을 적용했는지 추적할 수 있어야 장애 회고가 가능합니다.
또한 flag 기본값은 코드 리뷰에서 확인해야 합니다. 새 기능 flag의 기본값이 true로 들어가면 의도치 않은 전체 출시가 될 수 있습니다. SDK 초기화 실패 시 fallback이 무엇인지도 중요합니다. provider 장애 때 모든 기능이 켜지거나 꺼지면 장애가 확대될 수 있습니다. 각 flag의 실패 모드는 기능 위험도에 맞게 정해야 합니다.
7. Flag 조합 폭발을 줄이는 설계가 필요하다
Flag가 많아지면 가능한 상태 조합이 기하급수적으로 늘어납니다. release flag 5개만 있어도 32가지 조합이 생깁니다. 모든 조합을 테스트할 수 없으므로, flag 간 의존성을 줄이고 수명을 짧게 가져가야 합니다. 하나의 기능 출시 안에서도 서로 강하게 의존하는 flag가 있다면 별도 flag가 아니라 단계 상태로 표현하는 편이 나을 수 있습니다.
장기 flag와 단기 flag를 같은 코드 경로에 섞지 않는 것도 중요합니다. Permission flag는 제품 권한 모델의 일부라 오래 남을 수 있지만, release flag는 출시 후 삭제되어야 합니다. 둘이 한 if문 안에 섞이면 삭제가 어려워집니다. flag 조건을 도메인 정책 함수로 감싸면 코드 곳곳에 provider 호출이 퍼지는 것을 줄일 수 있습니다.
실험 flag는 분석 시스템과 연결되어야 합니다. 사용자가 어떤 variant에 배정되었는지 이벤트와 함께 남기지 않으면 나중에 지표를 해석할 수 없습니다. 실험 종료 후에도 variant 할당 로그가 남아 있어야 분석 재현이 가능합니다. Feature flag는 런타임 분기이면서 데이터 분석 계약입니다.
8. Flag SDK 장애도 테스트해야 한다
Flag provider나 SDK가 느려질 때 애플리케이션이 어떻게 동작하는지 미리 확인해야 합니다. 요청마다 원격 flag 평가를 기다리는 구조라면 provider 지연이 사용자 응답 지연으로 전파됩니다. 로컬 캐시, streaming update, timeout, fallback 값을 설정하고, provider 장애 상황에서도 핵심 기능이 예측 가능하게 동작해야 합니다.
장애 훈련에서는 flag 값을 가져오지 못하는 상황, 오래된 캐시만 남은 상황, 잘못된 flag 타입이 내려오는 상황을 테스트합니다. Boolean으로 기대한 값이 string으로 내려오거나, targeting rule이 깨졌을 때 기본값으로 안전하게 돌아가는지 봐야 합니다. Flag 시스템도 외부 의존성입니다.
결론: Feature flag는 켜는 기술보다 끄고 지우는 기술이다
Feature flag는 안전한 배포의 핵심 도구입니다. 하지만 flag가 많아질수록 시스템 상태는 복잡해집니다. 성공적인 운영은 flag를 만드는 속도가 아니라, 위험을 줄이며 켜고, 문제가 있으면 즉시 끄고, 역할이 끝나면 빠르게 지우는 능력에 달려 있습니다.
배포와 출시를 분리하면 팀은 더 자주 배포할 수 있습니다. 대신 그 자유에는 책임이 따릅니다. Flag owner, rollout 지표, kill switch 절차, cleanup 루틴이 함께 있을 때 feature flag는 부채가 아니라 운영 레버가 됩니다.