
배포와 출시를 분리하라: Feature Flag (기능 플래그)
금요일 오후 5시에 배포하는 것이 두려운가요? 기능을 코드에 포함시켜 배포하되, 실제 사용자에게는 보이지 않게 만드는 Feature Flag 기술. 롤백 없이 기능을 끄는 킬 스위치부터, A/B 테스트, 카나리 배포, 그리고 기술 부채 관리까지 안전한 DevOps를 위한 필수 전략을 정리합니다.

금요일 오후 5시에 배포하는 것이 두려운가요? 기능을 코드에 포함시켜 배포하되, 실제 사용자에게는 보이지 않게 만드는 Feature Flag 기술. 롤백 없이 기능을 끄는 킬 스위치부터, A/B 테스트, 카나리 배포, 그리고 기술 부채 관리까지 안전한 DevOps를 위한 필수 전략을 정리합니다.
서버를 끄지 않고 배포하는 법. 롤링, 카나리, 블루-그린의 차이점과 실제 구축 전략. DB 마이그레이션의 난제(팽창-수축 패턴)와 AWS CodeDeploy 활용법까지 심층 분석합니다.

새벽엔 낭비하고 점심엔 터지는 서버 문제 해결기. '택시 배차'와 '피자 배달' 비유로 알아보는 오토 스케일링과 서버리스의 차이, 그리고 Spot Instance를 활용한 비용 절감 꿀팁.

내 서버가 해킹당하지 않는 이유. 포트와 IP를 검사하는 '패킷 필터링'부터 AWS Security Group까지, 방화벽의 진화 과정.

왜 넷플릭스는 멀쩡한 서버를 랜덤하게 꺼버릴까요? 시스템의 약점을 찾기 위해 고의로 장애를 주입하는 카오스 엔지니어링의 철학과 실천 방법(GameDay)을 소개합니다.

창업 초기, 저에게 금요일 오후 5시는 '공포의 시간'이었습니다. 개발팀이 한 주 동안 열심히 만든 기능을 라이브 서버에 올리는 시간이었거든요.
"자, 배포 시작합니다."
엔터키를 누르고 나면 그때부터 심장이 뛰기 시작합니다. 혹시 로그인 버튼이 안 눌리면 어떡하지? 결제 페이지에서 에러가 나면? 아니나 다를까, 슬랙(Slack) 알림이 울립니다.
(유저 문의) "회원가입이 안 되는데요?"
그 순간부터 지옥이 시작됩니다. 원인을 찾으려고 로그를 뒤지고, 급하게 코드를 수정하고, 다시 빌드하고 배포합니다. 10분, 20분... 서비스가 멈춰있는 시간 동안 식은땀이 흐릅니다. 결국 안 되겠다 싶어서 "롤백(Rollback)!"을 외칩니다. 이전 버전으로 되돌리고 나면 이미 저녁 8시. 팀원들의 주말은 그렇게 날아갔습니다.
저는 이것이 개발자의 숙명인 줄 알았습니다. 배포는 원래 무섭고 위험한 것이라고요. 하지만 알고 보니 이건 제가 "배포(Deployment)"와 "출시(Release)"를 구분하지 못해서 생긴 무지함 때문이었습니다.
이 개념을 처음 접했을 때, 머리를 한 대 맞은 것 같았습니다.
저는 이 두 가지를 항상 동시에 했습니다. 코드를 서버에 올리는 순간(배포), 사용자가 바로 기능을 볼 수 있었으니까요(출시). 그러니 배포가 곧 출시였고, 배포의 실패가 곧 서비스 장애였습니다.
하지만 Feature Flag(기능 플래그)를 알게 된 후, 이 공식이 깨졌습니다.
코드는 서버에 올라가 있어도(배포 완료), 스위치를 끄면 사용자는 그 기능을 볼 수 없습니다(출시 전). 이제 저는 금요일 오후 5시에도 웃으면서 배포합니다. 어차피 기능은 꺼져 있으니까요. 그리고 월요일 아침에 출근해서 모닝커피를 마시며 스위치를 '딸깍' 켭니다. 만약 문제가 생기면? 다시 '딸깍' 끄면 그만입니다.
롤백하느라 서버를 내릴 필요도, 긴급 패치를 하느라 허둥댈 필요도 없습니다. 이것이 제가 배포의 공포에서 해방된 비결입니다.
처음엔 단순히 if (true) 문으로 기능을 끄고 켜는 게 전부인 줄 알았습니다. 하지만 공부를 하다 보니 피쳐 플래그에도 확실한 용도와 종류가 있다는 걸 알게 되었습니다. 마틴 파울러(Martin Fowler)의 블로그에서 정리한 4가지 분류가 가장 와닿았습니다.
가장 기본적인 형태입니다. 미완성 기능을 숨겨두는 용도죠.
예전에 저는 큰 기능을 개발할 때 feature/big-update 같은 브랜치를 만들어서 2주, 3주씩 작업했습니다. 그러다 메인 브랜치(main)에 합치려고 하면? 엄청난 충돌(Conflict)이 발생합니다. 일명 '머지 헬(Merge Hell)'이죠.
하지만 릴리스 토글을 쓰면, 미완성 코드라도 플래그로 감싸서 매일매일 메인 브랜치에 넣을 수 있습니다.
if (featureFlags.isOn('new-billing-system')) {
useNewBilling(); // 아직 미완성
} else {
useOldBilling(); // 현재 잘 돌아감
}
이렇게 하면 동료들의 코드와 내 코드가 매일 섞이기 때문에 나중에 큰 충돌이 날 일이 없습니다. 이것이 바로 Trunk-Based Development의 핵심이었습니다.
이건 마케터나 기획자가 더 좋아합니다. 흔히 말하는 A/B 테스트입니다.
"구매 버튼이 빨간색일 때 더 많이 누를까, 파란색일 때 더 많이 누를까?"
이걸로 회의하느라 몇 시간을 쓰는 대신, 그냥 두 개를 다 만들고 플래그로 나눕니다.
그리고 데이터를 봅니다. 빨간 버튼의 클릭률이 5% 더 높다면, 그때 모든 사용자에게 플래그를 ON으로 바꾸면 됩니다. 감이 아니라 데이터로 싸우는 문화가 생긴 것이죠.
이건 시스템의 '안전벨트'입니다. 예를 들어, 우리 서비스 메인 페이지에 '실시간 인기 검색어' 위젯이 있다고 칩시다. 평소엔 좋지만, 트래픽이 폭주하면 이 기능 때문에 DB에 부하가 걸려 전체 사이트가 느려질 수 있습니다.
이때 Ops Toggle을 미리 심어둡니다. 서버 모니터링 경보가 울리면, 개발자가 코드를 수정할 필요 없이 대시보드에서 show-realtime-search 플래그를 끕니다. 그러면 위젯만 사라지고 나머지 사이트는 정상적으로 돌아갑니다. 이걸 Kill Switch라고도 부릅니다.
특정 사용자에게만 기능을 열어주는 겁니다.
저희 팀은 이걸로 'Dogfooding(자사 제품 사용)'을 합니다. 새로운 기능은 직원들에게만 먼저 1주일간 열어두고(Permission Toggle), 버그가 없으면 전체 사용자에게 풉니다(Release Toggle).
코드야 if 문으로 가린다 쳐도, 데이터베이스(DB) 변경은 어떡할까요?
예를 들어 users 테이블의 주소 컬럼을 address 하나에서 address_city, address_detail로 나눈다고 칩시다. 코드를 배포하기 전에 DB를 바꿔버리면, 배포 전까지 구버전 코드는 에러가 날 겁니다.
여기서 Expand-Contract (확장-축소) 패턴을 배웠습니다.
address_city, address_detail)을 추가합니다. 기존 address 컬럼은 그대로 둡니다. DB 스키마만 변경하니 서비스 영향이 없습니다.address 데이터를 쪼개서 새 컬럼에 채워 넣습니다.address 컬럼을 DB에서 삭제합니다.이 과정은 귀찮고 깁니다. 하지만 무중단 배포를 위해서는 반드시 거쳐야 할 과정이더군요. "한 방에 배포"하려다 "한 방에 망하는" 것보다 백배 낫습니다.
처음에는 돈 아끼겠다고 config.json 파일에 플래그를 넣어서 관리했습니다.
{
"new-login": true,
"promo-banner": false
}
하지만 이건 배포를 다시 해야 플래그가 바뀝니다. "배포 없이 플래그 변경"이라는 핵심 가치를 잃어버린 거죠.
그래서 DB에 feature_flags 테이블을 만들고 API를 팠습니다. 근데 매번 DB를 조회하니 성능이 느려집니다. 그래서 Redis를 붙였습니다.
점점 일이 커지더군요. 플래그 관리자 페이지(Admin)도 만들어야 하고, 누가 언제 껐는지 로그(Audit Log)도 남겨야 하고... 결국 "바퀴를 다시 발명하지 말자"는 결론을 내렸습니다.
지금은 스타트업 단계라 오픈소스인 Unleash를 셀프 호스팅해서 쓰고 있습니다.
돈이 좀 벌리면 LaunchDarkly 같은 SaaS로 넘어갈 생각입니다. 관리가 필요 없고, 타겟팅 기능이 정말 강력하거든요.
Feature Flag는 '공짜 점심'이 아닙니다. 대가가 따릅니다. 바로 코드 복잡도입니다.
코드 곳곳에 if/else가 덕지덕지 붙습니다. 테스트 코드도 두 배로 짜야 합니다(ON일 때, OFF일 때).
가장 끔찍한 건 "좀비 플래그"입니다. 실험이 끝나서 A안으로 결정 났는데, 플래그 코드를 지우지 않고 그냥 둡니다. 1년 뒤, 누군가 실수로 그 플래그를 OFF로 바꿉니다. 갑자기 1년 전 구석기 시대 UI가 튀어나옵니다.
저는 이 실수를 몇 번 겪고 나서 원칙을 세웠습니다.
dev_sj_login_refactor) 메타데이터에 남깁니다. 퇴사자가 남긴 플래그만큼 무서운 게 없거든요.되돌아보면, Feature Flag 도입의 가장 큰 효과는 기술적인 것이 아니라 심리적인 것이었습니다.
"실수해도 괜찮다. 끄면 되니까."
이 믿음이 생기자 팀원들이 과감하게/빈번하게 배포하기 시작했습니다. 하루에 한 번 하던 배포를 하루 10번 하게 되었습니다. 배포 단위가 작아지니 버그를 찾기 쉬워졌고, 버그 수정도 빨라졌습니다. 선순환이 시작된 거죠.
아직도 금요일 배포가 무섭다면, 혹시 롤백 버튼에 손을 얹고 기도하고 있다면, 지금 당장 Feature Flag를 도입해 보세요. 여러분의 주말이 달라질 겁니다.