"새로운 결제 기능을 배포했는데 치명적인 버그가 발견되었습니다. 서버를 내리고, 코드를 수정하고, 다시 빌드해서 배포하려면 최소 30분이 걸립니다. 그동안 결제는 멈춰있습니다."
만약 코드 안에 **"이 기능을 켤까요?"**를 확인하는 if 문이 하나 있고, 그 값을 외부에서 즉시 바꿀 수 있다면 어떨까요? 서버 재배포 없이 스위치만 내리면(Off) 구버전으로 돌아갈 수 있습니다.
이 스위치를 "설정 파일에 박아둘 것인가" 아니면 **"실시간으로 제어할 것인가"**에 따라 활용도가 달라집니다.
1. 방식 1: 정적 토글 (Static Toggle / Config-based) - "단순한 스위치"
애플리케이션의 설정 파일(application.yml, .env)에 기능의 ON/OFF 여부를 적어두는 방식입니다. 가장 구현하기 쉽습니다.
특징
- 배포 의존: 값을 바꾸려면 애플리케이션을 **재시작(Restart)**하거나 설정을 리로드해야 합니다.
- 유지보수 모드: 전체 시스템의 특정 기능을 아예 막아둘 때 유용합니다.
코드 예시 (Spring Boot)
# application.yml
features:
new-billing-system: false # 아직 개발 중이라 꺼둠
@Service
public class BillingService {
@Value("${features.new-billing-system}")
private boolean isNewSystemEnabled;
public void processPayment() {
if (isNewSystemEnabled) {
// 새로운 결제 로직 (개발 중)
processNewPayment();
} else {
// 기존 결제 로직 (안전함)
processOldPayment();
}
}
}
장단점
- 장점:
- 구현 용이: 별도의 DB나 서버가 필요 없습니다. if 문 하나면 끝납니다.
- 트렁크 기반 개발: 완성되지 않은 코드도 false로 설정해두면 메인 브랜치(Trunk)에 병합할 수 있어, 머지 지옥(Merge Hell)을 피할 수 있습니다.
- 단점:
- 대응 속도: 값을 바꾸려면 서버를 재시작해야 하므로, 긴급 상황(Kill Switch)에서 반응이 느립니다.
- 타겟팅 불가: "사내 직원에게만 ON", "1% 유저에게만 ON" 같은 정교한 제어가 불가능합니다. 무조건 전체 ON 아니면 전체 OFF입니다.
2. 방식 2: 동적 토글 (Dynamic Toggle / Service-based) - "스마트 컨트롤러"
DB나 별도의 토글 관리 서버(Unleash, LaunchDarkly 등)에서 실시간으로 상태 값을 가져오는 방식입니다. A/B 테스트나 **카나리 배포(Canary Release)**의 핵심 기술입니다.
특징
- 실시간 제어: 서버 재배포 없이 런타임에 기능을 켜고 끌 수 있습니다.
- 컨텍스트 인식: "User ID가 짝수인 사람만", "한국 IP만" 등 요청 정보(Context)에 따라 다르게 동작합니다.
코드 예시
@Service
public class ProductService {
private final FeatureManager featureManager; // 토글 관리자 (DB or 외부 서비스 연동)
public ProductDto getProduct(User user) {
// "new-ui" 기능을 이 유저(user)에게 보여줄지 물어봄
if (featureManager.isActive("new-ui-feature", user)) {
return getNewProductUi(); // A그룹 (실험군)
} else {
return getOldProductUi(); // B그룹 (대조군)
}
}
}
활용 시나리오 (Canary Release)
- 새 기능을 배포하되, 토글은 OFF 상태로 둠. (사용자는 모름)
- 동적 설정에서 "사내 IP 대역"만 ON으로 변경 -> 직원들만 테스트.
- "전체 유저의 1%"만 ON으로 변경 -> 에러 로그 모니터링.
- 문제없으면 10% -> 50% -> 100%로 점진적 확대.
장단점
- 장점:
- Kill Switch: 치명적 버그 발견 시, 관리자 페이지에서 버튼 하나만 누르면 1초 만에 롤백 됩니다.
- 데이터 기반 의사결정: A/B 테스트를 통해 어떤 UI가 매출이 더 좋은지 실험할 수 있습니다.
- 단점:
- 복잡도: 외부 서비스 호출로 인한 지연(Latency)이 발생할 수 있어 캐싱 전략이 필요합니다.
- 기술 부채: 실험이 끝난 토글(if-else)을 제때 지우지 않으면, 코드가 스파게티가 됩니다. (Flag Cleanup 필수)
3. 실무 비교: 언제 무엇을 쓰는가?
| 구분 | Static Toggle (Config) | Dynamic Toggle (Service/DB) |
| 제어 위치 | 설정 파일 (yml, env) | DB, Redis, 외부 SaaS |
| 변경 적용 | 재시작/재배포 필요 | 즉시 반영 (Runtime) |
| 적용 범위 | 시스템 전체 일괄 적용 | 사용자별/조건별 타겟팅 가능 |
| 주 목적 | 개발 중인 코드 숨기기, 유지보수 | A/B 테스트, 긴급 차단(Kill Switch) |
| 구현 난이도 | 하 | 상 (인프라 구축 필요) |
결론
"아직 덜 만든 기능을 메인 브랜치에 합치고 싶다"면 간단한 **정적 토글(Config)**을 사용하세요. 이것만으로도 장기 실행 브랜치(Long-lived branch)의 고통에서 해방됩니다.
하지만 **"장애 발생 시 1초 만에 끄고 싶다"**거나 **"특정 사용자층을 대상으로 신기능을 실험하고 싶다"**면 반드시 동적 토글(Dynamic) 시스템을 구축해야 합니다.
Tip: 피처 토글은 영원히 두는 게 아닙니다. 기능이 안정화되면 반드시 if 문을 지우고 코드를 정리해야 기술 부채가 쌓이지 않습니다.
'프로그래밍 > 디자인패턴' 카테고리의 다른 글
| 스트랭글러 패턴(Strangler Fig Pattern) 완벽 정리 (0) | 2025.12.07 |
|---|---|
| 메시징 패턴(Messaging Pattern) 완벽 정리 (0) | 2025.12.07 |
| 이벤트 소싱 패턴(Event Sourcing Pattern) 완벽 정리 (0) | 2025.12.07 |
| 멀티테넌시 패턴(Multi-tenancy Pattern) 완벽 정리 (0) | 2025.12.07 |
| API 버저닝 패턴(API Versioning Pattern) 완벽 정리 (0) | 2025.12.07 |