Swizec Teller의 Code is the easy part, or how we refactored half the business to fix a janky script는 낡은 청구 스크립트를 고친 이야기처럼 시작합니다. 하지만 핵심은 코드가 아닙니다. 회사가 커지면서 비즈니스 개념, 데이터 모델, 운영 습관, 고객 셀프서비스, 재무팀 도구까지 함께 바꿔야 했던 리팩터링 이야기입니다.

원문: Code is the easy part, or how we refactored half the business to fix a janky script


1) 회사가 커지면 예외 처리도 제품이 된다

Swizec이 합류한 2024년 8월, 회사는 연간 반복 매출 약 2천만 달러에 가까워지고 있었습니다. 2026년 6월에는 1억 달러 ARR이 가시권에 들어왔다고 합니다. 매출은 빠르게 커졌지만, 청구 시스템은 훨씬 작은 회사였을 때의 방식에 머물러 있었습니다.

당시에는 Python 스크립트 하나가 모든 인보이스를 발행했습니다. 고객은 청구 정보 변경을 위해 지원팀에 연락했고, 지원팀과 재무팀은 고객을 만족시키기 위해 각종 예외를 받아들였습니다. 문제는 그 예외들이 문서나 코드에 명확히 남지 않았다는 점입니다. 누군가가 기억하고, 누군가가 매달 조심스럽게 처리하는 방식이었습니다.

작은 회사에서는 이런 방식이 통합니다. 고객 몇 명의 특수한 사정을 사람이 알고 있으면 됩니다. 하지만 고객 수와 주문량이 늘면 기억은 시스템이 될 수 없습니다. 사람의 머릿속에 있던 규칙은 병목이 되고, 월말 청구는 회사 전체의 스트레스가 됩니다.

2) 코드보다 데이터 모델이 먼저였다

원문의 가장 중요한 전환점은 “스크립트를 어떻게 고칠까?“가 아니라 “무엇을 청구 단위로 볼 것인가?“입니다.

기존 모델에서는 사용자, 기관, 랩, 협업 그룹, 영업 구역, 구매 부서의 요구가 한 구조 안에 뒤섞여 있었습니다. 어떤 사용자는 자기 앞으로 인보이스를 받고 싶어 하고, 어떤 사용자는 구매 부서가 처리하길 원합니다. 어떤 사용자는 프로젝트에 따라 기관을 오가기도 합니다. 영업팀은 같은 구조를 고객 구분과 보상 계산에 쓰고, 고객은 협업과 데이터 공유에 씁니다.

같은 개념을 여러 부서가 서로 다른 목적으로 보고 있었던 셈입니다. 모두의 관점이 맞지만, 모두의 요구를 한 데이터 모델로 처리하려 하니 시스템은 점점 진흙탕이 됩니다.

해법은 Billing Entities였습니다. 이 새 모델은 단순한 질문 하나에 답합니다.

이 주문을 어떻게 청구할 것인가?

사용자가 어떤 랩에 속하는지, 어떤 기관과 협업하는지, 어떤 영업 구역에 들어가는지는 별도의 문제로 분리합니다. 청구 시스템은 주문이 들어왔을 때 필요한 청구 주체와 결제 정보만 명확히 알면 됩니다. 큰 문제를 푼 비결은 더 많은 조건문이 아니라, 질문을 작게 만든 새 도메인 개념이었습니다.

3) 관찰이 자동화보다 빨랐다

흥미로운 부분은 AI와 자동화의 쓰임입니다. 팀은 구매 주문서 검증 규칙을 자동화하려고 했고, Claude에게 검증기를 써보게도 했습니다. 하지만 나온 결과는 너무 복잡한 정규식 파서였습니다.

그 다음에 팀은 재무팀이 실제로 월말 청구를 처리하는 모습을 봤습니다. 그러자 규칙은 훨씬 단순했습니다. 구매 주문서 문자열이 일정한 길이와 3~4글자 접두사 패턴으로 정렬되는지만 봐도 중요한 오류를 잡을 수 있었습니다. 실제 작업을 관찰하니, 가장 느린 수동 검증 단계를 빠르게 제거할 수 있었습니다.

여기서 교훈은 명확합니다. 자동화는 현장을 이해한 뒤에 해야 합니다. 사람의 작업을 보지 않고 규칙만 물어보면, 누구나 예외를 먼저 떠올립니다. 실제로 옆에서 보면 의사결정 기준은 더 작고 선명할 때가 많습니다.

4) 리팩터링은 프로젝트가 아니라 로드맵이었다

이 작업은 스크립트 하나를 고치는 것으로 끝나지 않았습니다. 사용자가 직접 랩을 만들고, 청구 정보를 관리하고, 결제 수단을 공유하고, 구매 주문서를 등록할 수 있는 기능이 필요했습니다. 기존 청구 정보는 새 모델로 이관해야 했고, 인보이스 발행 스크립트는 다시 실행 가능하고 멱등적으로 바뀌어야 했습니다. 재무팀에는 청구 예외, 미청구 항목, 수동 검토 대상을 관리하는 도구가 필요했습니다.

Swizec은 이 일을 6개월 이상, 최소 6명이 참여한 작업으로 설명합니다. 중요한 점은 모든 것을 멈추고 새 시스템을 만든 것이 아니라는 점입니다. 비즈니스는 계속 커지고 있었고, 하루 수천 건의 주문을 처리해야 했습니다.

그래서 팀은 한동안 이중 모드로 운영했습니다. Billing Entity가 있으면 새 방식으로 처리하고, 없으면 기존 방식으로 되돌아갔습니다. 이 방식은 재무팀에 혼란을 주기도 했지만, 한 달에 한 번만 드러나는 치명적인 예외를 찾는 데 도움이 됐습니다. 큰 리팩터링은 실험실에서 완성한 뒤 한 번에 교체하는 일이 아니라, 실제 운영 속에서 위험을 줄여 가는 일입니다.

5) 결과: 청구는 빨라졌고 제품은 강해졌다

최종 결과는 명확했습니다. 월말 청구는 일주일짜리 작업에서 2일짜리 작업으로 줄었습니다. 재무팀은 덜 불안해졌고, 고객은 직접 청구 정보를 관리하는 새 권한을 얻었습니다. 수백 개의 랩이 만들어졌고, 수십 개의 고객 제공 Billing Entity가 생겼습니다.

이 변화의 본질은 비용 절감보다 큽니다. 청구는 회사가 고객과 돈을 주고받는 핵심 인터페이스입니다. 이 영역이 사람이 기억하는 예외와 불안정한 스크립트에 의존하면, 성장은 곧 운영 리스크가 됩니다. 반대로 도메인 모델이 정리되고 고객이 직접 관리할 수 있게 되면, 회사는 더 큰 매출을 감당할 수 있습니다.

코드는 쉬운 부분이라는 말은 코드가 중요하지 않다는 뜻이 아닙니다. 코드가 진짜 문제를 표현할 수 있을 때 쉬워진다는 뜻입니다. 잘못된 모델 위에서는 스크립트가 계속 커지고, 올바른 모델 위에서는 코드가 작아집니다.

결론 및 시사점

한 줄 결론: 낡은 스크립트를 고치는 가장 빠른 길은 스크립트에 조건문을 더하는 것이 아니라, 비즈니스가 실제로 구분하는 개념을 데이터 모델에 반영하는 것이다.

이 글은 리팩터링을 기술 부채 청소로만 보면 놓치는 점을 보여줍니다. 진짜 부채는 코드 안에만 있지 않습니다. 운영팀의 기억, 고객 지원팀의 수동 처리, 재무팀의 예외 목록, 고객이 직접 바꿀 수 없는 정보에도 쌓입니다.

그래서 큰 리팩터링을 시작할 때 첫 질문은 “어느 파일을 고칠까?“가 아니라 “우리가 한 단어로 부르는 이 개념을 모든 부서가 같은 뜻으로 쓰고 있는가?“여야 합니다. 그 답이 아니면, 코드 수정은 잠깐의 진통제일 뿐입니다.