7년간 운영된 온디맨드 물류 플랫폼의 코드베이스였다. 업계 1위, 5년 연속 흑자, 매년 두 배 가까이 성장하는 서비스를 지탱하고 있던 코드.
12월 9일에 모노레포를 셋업하고, 기존 코드베이스는 packages/classic/에, 새 코드베이스는 packages/v3/에 넣어서 작업을 진행했다. 3월 13일에 packages/classic/을 삭제했다. 80,727줄이 사라졌다. 94일이 걸렸다.
왜 마이그레이션을 했는가
EOL 스택을 걷어내고 새로운 코드베이스 위에서 만들자는 논의는 3년 전부터 있었다. EOL 기술로 개발을 지속하는 건 더 큰 임팩트를 내는 데 한계가 있었고, AI를 활용하려 해도 레퍼런스가 부족한 스택 위의 코드는 활용도가 낮았다. 해볼 수 있는 기회가 생겼고, 실행에 옮겼다.
LLM이 활용할 수 있는 환경 만들기
새로운 스택, 새로운 사람들, 94일이라는 시간. 이 조건에서 속도를 내려면 사람뿐 아니라 LLM도 코드를 이해하고 활용할 수 있는 구조가 필요했다.
openapi-ts와 openapi-fetch로 API 스펙을 타입으로 만들었다. HTTP 요청의 전처리/후처리는 미들웨어로 구현했고, 백엔드도 동시에 마이그레이션을 진행하고 있었기 때문에 에러 코드 같은 것들이 실시간으로 바뀌었다. 에러 코드 API를 fetch해서 캐싱하고, TS Compiler API로 스키마를 변환하는 파이프라인을 만들었다. yarn gen-v2-api 한 줄이면 이런 변환이 일어난다.
// Before: OpenAPI가 생성한 타입
type ErrorResponse = { code: string; message: string };
// After: yarn gen-v2-api
type OrderError =
| { code: "E1001"; message: "주문을 찾을 수 없습니다." }
| { code: "E1002"; message: "이미 배차된 주문입니다." };
“이 에러 코드 뭐예요?” 같은 질문이 사라졌고, LLM도 타입 정보를 통해 도메인 맥락을 이해할 수 있게 됐다.
모달과 풀스크린을 같은 인터페이스로 만들었다. createAlgolabModal()이라는 팩토리 패턴을 만들어서, 데스크톱에서는 모달로, 모바일에서는 풀스크린으로 전환할 때도 동일한 API를 쓸 수 있게 했다. 모달에서 값을 반환받는 resolve 패턴까지 타입으로 잡아뒀기 때문에, 패턴을 한 번 익히면 사람이든 LLM이든 같은 방식으로 쓸 수 있다.
테스트 가능한 구조로 설계한 폼 시스템
퀵서비스 주문서 하나를 만들려면 누가 보내고, 누가 받고, 어떤 차량으로, 얼마에, 누가 결제하는지를 전부 다뤄야 한다. 이 각각이 독립적인 도메인이고, 서로 엮여 있다. TanStack Form 기반으로 중앙 컨텍스트를 두되, 각 도메인의 정합성은 최대한 Zod 스키마로 맞추고, Zod로 표현할 수 없는 경계는 invariant로 보완했다. 새 기능을 추가할 때 전체 폼을 이해할 필요 없이, 해당 도메인 폴더만 보면 된다.
설계 의도는 도메인별로 쪼개고, 단위 테스트와 통합 테스트로 각 도메인의 스펙을 확정한 뒤, 귀납적으로 전체를 검증하는 것이었다. 솔직히 말하면, 실제로는 구현부터 치고 들어갔다. 테스트가 구현을 뒤따랐고, 수동 QA에 의존한 기간도 있었다. 구조는 테스트를 위해 설계했지만, 순서는 반대였다. 다음에 비슷한 규모의 작업을 한다면, 테스트로 스펙을 먼저 확정하는 단계를 반드시 거칠 것이다.
기술 외적으로 가장 많이 배운 것
신뢰 자산
좋은 아키텍처를 알고 있는 것과, 팀이 그 방향으로 함께 움직이는 것은 다른 문제다. AI 시대에 구현의 품은 줄어들었다. 새로 합류한 사람이 AI를 쓰면 뭐든 빠르게 만들어낼 수 있을 것처럼 보인다. 하지만 사람 간의 관계는 그렇게 작동하지 않는다. 아무리 열려 있는 조직이라도, 본인이 하고 있는 일에 대해 — 나아가서 회사의 비즈니스를 이해하는 데 더 많은 시간을 쓰는 태도야말로 “이 사람과 제대로 일해볼 수 있겠다”는 감정을 만들어낸다.
도메인을 이해하려는 태도, 낯선 영역도 직접 해결하는 모습, 작은 성공을 같이 나누는 과정 — 이런 것들이 쌓여서 “해볼 만하겠다”는 신뢰가 된다. 그 신뢰가 없으면 마이그레이션 같은 큰 변화는 시작되지 않는다.
말로 설득하지 않았다. 먼저 보여줬다. 프론트엔드끼리 스몰톡으로 패턴에 대해 이야기를 나누고, 어떤 멘탈 모델인지, DX상 불편한 게 뭔지를 같이 고민했다. 이 과정이 결국 마이그레이션을 가능하게 만든 토대였다.
531개의 커밋, 80,727줄의 삭제, 122K 라인의 새 코드. 숫자로 보면 그렇다. 하지만 이 94일이 나에게 남긴 건 숫자가 아니다. 작은 것부터 보여주고, 신뢰를 쌓고, 그 위에서 함께 큰 것을 만드는 과정 자체가 남았다.