팀이 커져도 배포를 늦추지 않는 코드베이스 운영법 (토스 모노리포)

토스는 모노리포를 쪼개는 대신 핵심 의존성을 카탈로그로 표준화해, 100명 넘는 프론트엔드 조직의 검증·이전·배포 경로를 관리했습니다.

토스 프론트엔드 모노리포에서 100명 넘는 조직이 React 19, Next.js 15, TypeScript 7 카탈로그로 버전을 표준화한다는 것을 보여주는 표지
토스는 핵심 의존성 버전을 카탈로그로 묶어 팀이 커져도 배포 속도를 지키는 방법을 택했습니다.

팀이 커져도 배포를 늦추지 않는 코드베이스 운영법: 토스 모노리포

프론트엔드 인력이 100명을 넘고 하루 배포가 수백 번이면, 공통 라이브러리 하나의 업데이트도 곧 운영 문제가 된다. 토스는 모바일 제품 코드를 하나의 모노리포로 유지하면서 React 19, Next.js 15, TypeScript 7처럼 핵심 개발환경을 가깝게 맞추는 방식을 택했다.

핵심은 리포지터리의 개수가 아니라 의존성 변경을 어느 범위까지 예측하고 전파할 수 있는가에 있다. 토스 프론트엔드 플랫폼 팀은 서비스마다 달랐던 핵심 라이브러리 버전을 카탈로그로 묶고, 새 버전을 별도로 발행해 검증과 이전의 순서를 만들었다.

모노리포가 커질수록 왜 업데이트가 비싸지는가

한 리포지터리에 코드가 모여 있어도 서비스가 제각각의 React, 상태 관리 도구, 빌드 도구 버전을 고르면 공통 코드는 사실상 여러 환경을 동시에 지원해야 한다. 설치할 패키지 종류가 늘고, 어떤 서비스에서 통과한 변경이 다른 서비스에서도 통과하는지 장담하기 어렵다. 이 상태에서 플랫폼 팀은 테스트 범위가 너무 넓어지고, 서비스 팀은 업데이트를 미루게 된다.

서비스별로 서로 다른 핵심 의존성 버전을 고르는 구조와 카탈로그가 표준 버전을 참조하게 만드는 구조를 비교한 도식
같은 리포지터리 안에서도 의존성 선택이 흩어지면 변경 경로가 늘어난다. 카탈로그는 서비스의 버전 선택을 공통 참조로 바꾼다.

토스가 찾은 원인은 모노리포 자체가 아니라 버전 정책의 부재였다. 리포를 여러 개로 쪼개면 개별 설치 범위는 작아질 수 있지만, 개발환경이 갈라지고 공통 패키지를 바꾸는 비용은 남는다. 그래서 팀은 핵심 의존성의 버전과 조합을 플랫폼이 관리하는 표준으로 만들었다.

카탈로그는 버전을 고정하는 대신 변경 경로를 만든다

카탈로그는 pnpm-workspace.yml 또는 .yarnrc.yml에 정의한 의존성 세트를 서비스가 catalog: 프로토콜로 참조하는 방식이다. 새 서비스는 처음부터 최신 카탈로그를 쓰고, 직접 설치할 때도 현재 서비스의 카탈로그를 참조하게 하며, CI는 카탈로그에 든 패키지를 제각각 버전으로 쓰는 실수를 잡는다.

표준 버전, 릴리스 전 검증, 단계적 이전이 배포 속도를 지키는 세 축이라는 대표 도식
카탈로그의 역할은 단순한 잠금 파일이 아니다. 표준 버전, 사전 검증, 단계적 이전을 한 운영 경로로 연결한다.

이 구조에서 이미 발행한 카탈로그는 파괴적 변경을 몰래 담지 않는다. 토스는 메이저 변경이 필요하면 새 카탈로그를 만들고 일부 서비스에서 먼저 동작을 확인한 뒤 이전한다. 이전에는 코드 변환 스크립트와 AI Skill을 제공하고, 서비스 팀은 자신이 쓰는 카탈로그 이름을 바꾸는 방식으로 변경 범위를 알아차린다.

새 버전은 한 번에 밀어 넣지 않고 통과 경로를 남긴다

토스의 카탈로그 이름에는 발행 연월이 들어간다. 새 카탈로그는 한 달에 최대 한 번 발행하며, 월초에 패키지 메인테이너가 예정 변경을 함께 논의한다. 발행 전 검증된 서비스, 자동 변환 도구, CI 규칙을 차례로 두면 플랫폼 팀은 변경을 전파하고 서비스 팀은 필요한 시점에 이전할 수 있다.

새 카탈로그 발행부터 일부 서비스 검증, 변환 도구 제공, 서비스 이전과 CI 점검까지의 세로형 운영 순서도
버전 표준화는 새 버전의 속도를 늦추는 일이 아니라, 검증과 이전을 반복할 수 있는 경로를 만드는 일이다.

이전이 끝난 뒤에도 정책은 코드로 남는다. 토스는 Yarn 플러그인과 CLI로 카탈로그 적용, 특정 워크스페이스의 사용 강제 또는 제한을 관리한다. 실험용 버전은 기존 안정 카탈로그의 이름 아래 별도 경로로 두어 일부 서비스에서 먼저 시험할 수 있게 했다. 플랫폼 팀의 판단을 문서 공지에만 두지 않고, 새 프로젝트 생성과 의존성 설치, CI 검사에 녹인 셈이다.

측정값이 보여 주는 것은 설치 시간만이 아니다

토스가 공개한 적용 전후 수치에서 Yarn PnP의 .pnp.cjs 파일은 96MB에서 15MB로 줄었다. 개발 서버 실행은 26.7초에서 20.3초로, 모노리포 전체 의존성 설치는 528.4초에서 249.9초로 감소했다. 각각 약 84%, 23%, 52%의 변화다. 이 숫자는 토스가 같은 모노리포에서 카탈로그 적용 효과로 제시한 측정이며, 중요한 변화는 패키지 수 감소와 함께 어떤 서비스가 어떤 버전을 쓰는지 추적할 수 있게 된 점이다.

토스가 공개한 카탈로그 적용 전후 Yarn PnP 파일 크기, 개발 서버 실행 시간, 전체 의존성 설치 시간 비교 차트
토스 원문 차트. 카탈로그 적용 뒤 Yarn PnP 파일 크기와 개발 서버·의존성 설치 시간이 함께 감소했다.

가시성이 생기면 패키지 개발자는 구조적 변경의 파급 범위를 찾고, 서비스 개발자는 검증된 세트를 바탕으로 업데이트할 수 있다. 토스는 이 기반 위에서 RSC, TypeScript 7, Rspack, E2E 테스트 같은 변화를 빠른 기간에 시도했다고 설명한다. 보안 패치도 카탈로그 버전 갱신을 통해 전파 경로를 확보한다.

다른 팀이 먼저 정할 것은 리포 구조가 아니라 세 가지다

첫째, 모든 서비스가 반드시 함께 쓸 핵심 의존성을 좁혀야 한다. 모든 패키지를 중앙화할 필요는 없지만, 런타임·프레임워크·빌드와 검사 도구처럼 공통 코드의 호환성을 좌우하는 층은 표준이 필요하다. 둘째, 이미 발행한 표준에 어떤 변경을 허용할지 정해야 한다. 호환성이 깨지는 변경은 새 버전으로 분리해야 서비스 팀의 예상이 맞는다. 셋째, 새 버전으로 가는 수동 작업을 코드 변환과 CI 규칙으로 줄여야 한다.

모노리포는 코드를 한곳에 둔다는 사실만으로 배포 속도를 보장하지 않는다. 표준 버전, 사전 검증, 되돌릴 수 있는 이전 경로를 함께 운영할 때 공통 플랫폼의 변경이 여러 팀의 대기열이 아니라 다음 배포로 이어진다.

출처: 토스테크, 「모노리포 희망편, 절망의 리포가 희망의 리포로 부활하기까지 걸린 1년」(2026-08-10)