코드 배포 없이 비즈니스 룰을 바꾸는 방법
비즈니스 로직이 배포 파이프라인의 모든 무게를 함께 짊어질 이유는 없습니다. 룰을 코드에서 분리하는 세 가지 방법과 각각이 무너지는 지점을 짚어봅니다.
다섯 글자 바꾸는 데 2주 걸리는 풍경
오전 10시, PM에게서 메시지가 옵니다.
"VIP 할인을 10%에서 15%로 바꿔주세요. 마케팅팀이 금요일까지 반영되길 원해요."
파일 위치는 이미 알고 있습니다. 한두 번 만져본 상수입니다.
private static final double VIP_DISCOUNT_RATE = 0.10;
변경 자체는 4초면 끝납니다. 나머지 모든 과정이 2주입니다.
브랜치를 파고, PR을 올리고, 리뷰를 기다립니다. 동료는 회의 중이었다가, 점심 먹으러 갔다가, "진짜 일"을 마무리하는 중입니다. 퇴근 무렵에야 approve 하나가 찍힙니다.
다음 날 아침, QA가 테스트 실패를 발견합니다. 이번 변경과 관계없는 테스트지만 이제 내 문제입니다. 고치고, 푸시하고, 파이프라인이 다시 돌기를 기다립니다. 수요일이 지나갑니다.
목요일에 main 머지. 스테이징 배포는 야간에 돌아갑니다. QA 리드가 프로덕션 전에 한 번 더 눌러보겠다고 합니다. 금요일 아침에 승인. 오후 2시, 마침내 라이브.
다섯 글자 변경. 엔지니어 10시간이 5일에 흩어졌습니다. PM은 이제 속으로 이렇게 생각합니다. "간단한 변경"이라는 건 없구나.
이 장면이 익숙하다면, 이 글은 당신을 위한 것입니다. 팀이 느려서가 아닙니다. 비즈니스 로직이 애초에 배포 파이프라인에 속해 있지 않아서 그렇습니다.
왜 다섯 글자가 2주를 먹는가
애플리케이션 코드는 그 배포 파이프라인을 받을 만한 값을 합니다. 리뷰, 테스트, 카나리, 롤백 — 이 모든 의식(ceremony)은 깨진 코드를 배포하는 비용이 크기 때문에 존재합니다. 잘못된 DB 마이그레이션은 데이터를 망가뜨리고, 잘못된 API 변경은 외부 연동을 끊어놓습니다. 이 무게는 하중을 지탱하는 구조물입니다.
그런데 비즈니스 룰은 같은 종류의 코드가 아닙니다. 데이터 구조를 바꾸지 않고, DB 스키마를 건드리지 않고, 외부 계약을 변경하지 않습니다. 그저 이렇게 말할 뿐입니다. 장바구니가 10만원 이상이고 고객 등급이 VIP면 15% 할인.
이 로직이 자바 상수로 서비스 안에 박혀 있으면, 룰은 코드가 가진 모든 것을 물려받습니다. 테스트, 리뷰 프로세스, 배포 사이클, 온콜 로테이션, 롤백 도구 — 전부입니다. 인프라가 "DB 커넥션 풀 리팩토링"과 "할인율 변경"을 구분하지 못하기 때문에 그 비용을 내고 있는 겁니다.
해답은 배포를 빠르게 만드는 게 아닙니다. 해답은 둘이 다른 종류의 일이라는 걸 인정하고 다르게 다루는 것입니다.
비즈니스 룰을 코드에서 분리하는 세 가지 방법
선택지는 스펙트럼 위에 있습니다. 각각은 유연성과 복잡도를 맞바꿉니다.
Option 1 — Config 파일
가장 먼저 떠오르는 방법입니다. 값을 코드 밖의 config 파일이나 환경 변수로 옮깁니다.
# pricing-config.yaml
vip_discount_rate: 0.15
min_order_for_discount: 100000
애플리케이션은 시작할 때 또는 주기적으로 config를 읽습니다. 값을 바꾸려면 파일을 수정하고 재시작합니다 — 핫 리로드가 연결돼 있다면 재시작도 생략 가능합니다. CI/CD 의미의 애플리케이션 재배포는 필요 없습니다.
해결하는 것: 리뷰 사이클 없이 단순한 값 변경.
해결하지 못하는 것:
- 실제 로직 이 들어가는 순간 무너집니다 — "유럽 VIP는 15%, 나머지는 10%"를 config로 표현하려는 순간 구조가 꼬입니다
- 변경 이력이 없습니다. 누가, 언제, 왜 바꿨는지 알 수 없습니다
- 타입 검증이 없습니다. float 자리에 문자열을 넣으면 그대로 프로덕션에서 터집니다
- 미리 보기가 없습니다. "20%로 설정하면 어떻게 될까"를 테스트하려면 실제로 20%로 설정해야 합니다
Config 파일은 튜닝 가능한 몇 개의 파라미터에만 잘 맞습니다. 분기 조건이 있는 진짜 로직이 등장하는 순간 역부족입니다.
Option 2 — DB 테이블 + 어드민 UI
자연스러운 다음 단계입니다. 룰을 행으로 만듭니다. 작은 어드민 UI를 만들어 엔지니어가 아닌 사람도 편집하게 합니다.
CREATE TABLE discount_rules (
id SERIAL PRIMARY KEY,
customer_tier VARCHAR(20),
region VARCHAR(20),
min_cart_total DECIMAL(10,2),
discount_rate DECIMAL(4,3),
priority INT,
active BOOLEAN
);
서비스는 이 테이블을 쿼리해서 매칭되는 룰을 고르고 할인을 적용합니다. 어드민 UI에서 PM이나 운영 담당자가 할인율을 바꾸고, 새 등급을 추가하고, 룰을 on/off 할 수 있습니다.
해결하는 것: 엔지니어가 아닌 사람이 직접 변경. 감사 컬럼을 추가하면 DB가 이력을 남겨줍니다. 스키마 안에서 표현 가능한 분기 로직은 다룰 수 있습니다.
해결하지 못하는 것 — 그리고 시간이 지나면 터지는 것들:
- 스키마가 곧 룰 언어입니다. "시간대" 조건을 추가하고 싶다면? 스키마 마이그레이션 + 이 테이블을 읽는 모든 서비스의 코드 변경입니다. 배포 문제를 룰 구조 레벨에서 그대로 재현한 꼴이 됩니다
- 우선순위와 충돌 해소가 당신 책임이 됩니다. 두 룰이 동시에 매칭되면 어떻게 될까요? 결국 엔진을 직접 짜게 됩니다
- 시뮬레이션이 없습니다. 누군가
active플래그를 토글하면 룰은 즉시 라이브 트래픽에 적용됩니다. 잘못됐을 때의 피드백 채널은 분노한 고객입니다 - 이 모든 걸 당신이 소유합니다. 어드민 UI, 감사 로그, 권한 시스템, API, 테스트 하네스. 세 번째 프로덕트 팀이 "작은 기능 하나만 추가해달라"고 말하는 순간 무너집니다
저는 예전 회사에서 이 경로를 두 번 만들어봤습니다. 두 번 다 아무도 유지보수하고 싶지 않은 시스템으로 자랐습니다. 그걸 짓는 게 본업이 아니었기 때문입니다. 본업은 제품을 출시하는 것이었습니다.
Option 3 — 외부 의사결정 플랫폼
이 문제만을 위한 전용 인프라입니다. 룰은 애플리케이션 밖, 룰을 저장·버전 관리·시뮬레이션·실행하도록 설계된 시스템에 삽니다.
패턴은 이런 모양입니다.
# 서비스가 플랫폼을 호출합니다
curl -X POST https://api.lexq.io/api/v1/execution/groups/{groupId} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"facts": {"customer_tier": "VIP", "payment_amount": 150000}}'
플랫폼은 전달받은 facts에 대해 룰을 실행하고 응답을 돌려줍니다.
{
"result": "SUCCESS",
"data": {
"outputVariables": {
"customer_tier": "VIP",
"payment_amount": 135000,
"last_discount_amount": 15000
},
"executionTraces": [
{
"traceId": "34d91d2e-...",
"ruleId": "d91d98c1-...",
"ruleName": "VIP 10% Discount",
"executedAt": "2026-04-21T09:36:48Z",
"matched": true,
"matchExpression": "(customer_tier == 'VIP') && (payment_amount >= 100000)",
"inputFacts": {
"customer_tier": "VIP",
"payment_amount": 150000
},
"generatedActions": [
{
"type": "DISCOUNT",
"parameters": {
"rate": 10,
"method": "PERCENTAGE",
"refVar": "payment_amount"
}
}
]
}
],
"decisionTraces": [
{
"ruleId": "d91d98c1-...",
"ruleName": "VIP 10% Discount",
"status": "SELECTED",
"reasonCode": "FINAL_WINNER"
}
]
}
}
애플리케이션은 질문을 던집니다. 플랫폼은 결정 그리고 설명을 함께 돌려줍니다. executionTraces 블록은 어느 룰이 발화했는지, 어떤 표현식이 정확히 평가됐는지, 어떤 input facts가 엔진에 도달했는지를 알려줍니다. decisionTraces 블록은 최종적으로 어느 룰이 선택됐고 왜 선택됐는지를 알려줍니다. 두 블록을 합치면 결정이 어떻게 내려졌는지에 대한 변조 불가능한 기록이 됩니다 — 6개월 뒤 누군가 "왜 이 고객이 15% 할인을 받았는가"라고 물을 때 당신이 꺼낼 수 있는 아티팩트입니다.
해결하는 것:
- 룰 구조가 바뀌어도 코드 변경이나 DB 마이그레이션이 필요 없습니다
- 모든 변경은 버전이 붙고, 감사 가능하고, 되돌릴 수 있습니다
- 변경이 프로덕션에 닿기 전에 시뮬레이션이 먼저 돌아갑니다 — 새 룰을 과거 데이터에 돌려서 어떤 결과가 나왔을지 정확히 확인합니다
- 엔지니어가 아닌 사람이 초안을 작성하고, 엔지니어가 리뷰 후 퍼블리시하는 워크플로우가 가능합니다
- 실행 추적은 규제 감사에 제출 가능한 수준의 아티팩트가 됩니다
비용:
- 핫패스에 네트워크 홉이 하나 추가됩니다 (밀리초 단위지만 0은 아닙니다)
- 이해하고, 신뢰하고, 모니터링해야 할 새 시스템이 생깁니다
- 외부 서비스에 대한 의존성 — 또는 self-host를 택한다면 운영 비용
나란히 놓고 비교
| 항목 | Config 파일 | DB 테이블 + UI | 의사결정 플랫폼 |
|---|---|---|---|
| 애플리케이션 재배포 없이 변경 | ✅ | ✅ | ✅ |
| 분기 로직 표현 | ❌ | 제한적 | ✅ |
| 내장된 감사 추적 | ❌ | 직접 구현 | ✅ |
| 롤아웃 전 시뮬레이션 | ❌ | ❌ | ✅ |
| 초 단위 롤백 | ❌ | 직접 구현 | ✅ |
| 스키마 없이 룰 구조 확장 | ❌ | ❌ | ✅ |
| 구축 비용 | 낮음 | 높음 | 0 (관리형) 또는 중간 (self-host) |
| 런타임 비용 | 0 | 0 | 네트워크 홉 +1 |
정답은 룰이 얼마나 자주 바뀌는지, 그리고 그 룰에 실제로 얼마나 많은 로직이 들어있는지에 따라 달라집니다. 튜닝 가능한 상수 몇 개? Config 파일. 할인율 필드 다섯 개짜리 어드민 패널? DB 테이블. 세 팀이 주 단위로 편집하고, 분기 조건이 실존하고, 감사 요구사항이 있고, 가끔은 프로모션 오작동으로 롤백이 필요한 가격 룰? 의사결정 플랫폼.
이 패턴이 필요 없는 경우
모든 팀이 룰을 코드베이스에서 빼야 하는 건 아닙니다. 다음 경우라면 이 글의 조언은 무시해도 됩니다.
- 룰이 분기에 한 번도 안 바뀐다면. 2주짜리 배포 비용이 몇 달에 걸쳐 상각됩니다. 별도 시스템을 도입하는 오버헤드가 수지타산이 맞지 않습니다
- 모든 룰 변경이 정당하게 엔지니어 리뷰를 요구한다면. 일부 규제 도메인 — 특정 헬스케어 워크플로우, 특정 금융 컴플라이언스 로직 — 은 모든 변경에 코드 리뷰, 테스트 커버리지, 완전한 배포 규율이 정당하게 필요합니다. 이걸 건너뛰면 감사 요건을 위반합니다. 이때 그 마찰은 마찰이 아니라 통제 그 자체입니다
- 룰이 딱 하나라면. 단일 세율, 단일 피처 플래그. 코드 안 상수면 충분합니다. 플랫폼으로 옮기는 건 기껏해야 가로 이동입니다
질문은 "내 코드가 완벽한가"가 아닙니다. 질문은 이것입니다. 모든 비즈니스 변경이 배포 파이프라인의 전체 무게를 물려받아야 하는가? "아니오"라고 답할 일이 충분히 자주 있다면 분리하세요. "예"라면 지금 구조를 지키세요 — 그 자체로 합리적인 선택입니다.
LexQ가 놓이는 자리
LexQ는 그런 의사결정 플랫폼 중 하나입니다 — 비즈니스 결정을 배포 파이프라인 밖으로 꺼내고 싶은 팀을 위해 만들어졌습니다. 룰은 버전이 붙은 정책 그룹 안에 살고, 모든 변경은 퍼블리시 전에 과거 또는 업로드된 데이터에 대해 시뮬레이션을 거칩니다. 모든 실행은 감사용으로 구조화된 trace를 반환합니다 — matched, matchExpression, inputFacts, 그리고 reasonCode가 붙은 최종 status까지. 63개 도구 MCP 서버가 있어 AI 에이전트가 기존 워크플로우 안에서 룰을 조회하거나 변경을 제안할 수 있습니다.
모든 팀에게 맞는 건 아닙니다. 가격 로직을 DB 마이그레이션과 같은 파이프라인에 실어 나르는 것에 지친 엔지니어링 팀에게 맞습니다.
→ LexQ 플레이그라운드에서 바로 실행해보기 — 가입 없이
관련 글