← 포스트 목록
TurborepoMonorepopnpm아키텍처

디자인 하나 바뀔 때마다 레포 10개를 돌아야 했던 이야기

2026-07-10 · 약 10분 소요

요약

문제
같은 피그마 디자인을 쓰는 어드민들이 레포마다 흩어져 있어, 디자인 하나가 바뀌면 레포 수만큼 같은 작업을 반복
실행
공용 코드를 흩어놓지 말고 한 저장소에서 관리하기로 하고, npm 배포·MFE와 비교 끝에 pnpm workspace 위에 Turborepo를 얹어 모노레포로 통합. 이후 부정확한 캐시 설정(inputs·dependsOn)과 누락된 의존성 명시를 바로잡고 Vitest로 공용 코드 테스트 자동화
결과
디자인이 바뀌어도 공용 패키지 한 곳만 고치면 10개 앱에 반영돼, 레포를 오가며 같은 수정을 반복하거나 동기화하는 일이 사라짐. 공용 코드 라인 커버리지 52%를 확보해 사이드 이펙트 걱정에 손대지 못하던 코드도 고칠 수 있게 됨

저희 회사는 KOKKOK이라는 플랫폼 안에서 여러 서비스를 운영하고 있었습니다. 그 서비스들의 어드민은 모두 같은 피그마 디자인을 기반으로 만들어졌는데, 정작 코드는 서비스마다 별도 레포로 나뉘어 있었습니다. 디자인이 하나 바뀌면 그 디자인을 쓰는 레포 수만큼 같은 작업을 반복해야 했고, 이 멀티 레포 구조가 업무 비효율을 계속 만들어내고 있었습니다.

업무 효율을 위해 저희 팀이 내린 결정은 공용 코드를 흩어놓지 말고 하나의 저장소에서 함께 관리하자는 것이었습니다.

무엇을 비교했고 왜 모노레포였나

공용 코드를 관리할 방법으로 네 가지를 검토했습니다.

이 중 모노레포가 가장 간단했습니다. 배포 방식들은 버전을 올리고 배포하고 재설치하는 과정이 매번 필요했고, MFE는 한 팀이 모든 프로젝트를 관리하는 저희 조직 구조와 맞지 않았습니다.

모노레포로 정한 뒤에도 구현 방법이 하나 더 남아 있었습니다. pnpm workspace만으로 갈지, 아니면 Lerna·Nx·Turborepo 같은 도구를 얹을지였습니다. pnpm workspace만으로는 패키지 간 참조는 가능해도 빌드 순서나 캐싱까지 관리해주진 않았습니다. 그래서 Turborepo를 얹기로 했습니다. Vercel이 만든 도구인 데다, 저희가 쓰고 있던 Next.js도 Vercel 소속이라 호환성이 좋았기 때문입니다.

npm install turbo --global로 최소한의 PoC부터 시작해, 기존 node_modulespackage-lock.json을 지우고 pnpm으로 재설치했습니다. 검증이 끝난 뒤 6개 프로젝트를 한 번에 옮기는 본작업으로 넘어갔습니다.

1차 문제: 캐시가 못 믿을 캐시였다

어느 날 공용 컴포넌트 하나를 고쳐서 배포했는데, 실제 화면엔 고치기 전 모습이 그대로 떠 있었습니다. 원인을 찾아보니 Turborepo가 예전 빌드 캐시를 그대로 재사용하고 있었습니다.

Turborepo는 turbo.json에 적어둔 파일들이 바뀌었는지 확인해서, 안 바뀌었으면 이전 빌드 결과를 그대로 재사용합니다. 그런데 저희는 이 "확인 대상"에 packages 폴더나 .env 파일을 제대로 적어놓지 않았습니다. 그래서 이 파일들이 바뀌어도 Turborepo는 "안 바뀌었다"고 착각하고 예전 빌드 결과를 그대로 써버린 것이었습니다. 캐시가 최신 코드인지 확신할 수 없다면, 캐시는 있으나 마나 한 정도가 아니라 오히려 없는 것보다 위험합니다. 확인해야 할 파일(inputs)과 작업 순서(dependsOn)를 turbo.json에 다시 명확하게 적어 이 문제를 없앴습니다.

여기서 더 파고들어보니, 각 패키지의 package.json에 정작 어떤 라이브러리를 쓰는지 제대로 안 적혀 있다는 것도 발견했습니다. 그 라이브러리가 루트 package.json에 이미 설치돼 있었던 덕분에 "어쩌다 보니" 잘 동작하고 있었던 겁니다. 이걸 그대로 두면 안 되는 이유는 세 가지였습니다.

전 패키지의 dependencies, devDependencies를 실제 사용 현황에 맞춰 일괄 재작성했습니다.

2차 문제: 공용 코드를 고치기가 무서웠다

모노레포로 합친 뒤에도 정작 공용 코드(컴포넌트, 커스텀 훅, 유틸 등)는 하나같이 손대기 무서운 존재였습니다. 고치고 나면 어떤 사이드 이펙트가 생길지 예상이 안 됐기 때문입니다. 기존 동작이 여전히 잘 되는지 확인하려 해도 수동으로 하나하나 확인하려니 시간이 너무 많이 들었습니다. 결국 공용 코드는 웬만하면 건드리지 않으려는 분위기가 생겼고, 이건 모노레포로 전환한 이유(한 곳을 고치면 전체에 반영되는 것) 자체를 무색하게 만드는 문제였습니다.

이 문제를 풀려면 테스트 코드 도입이 필요했습니다. 테스트 코드가 있으면 코드를 고쳐도 기존 기능이 제대로 동작하는지 자동으로 확인할 수 있고, 그만큼 더 확신을 갖고 코드를 고칠 수 있기 때문입니다. 테스트를 자동화하기로 하고 Jest와 Vitest를 두고 고민했습니다. 저희 빌드 환경이 Vite 기반이었기 때문에, 별도 설정 없이 잘 맞물리는 Vitest를 선택했습니다.

hooks 46개 중 44개, utils 40개 중 37개, components 164개 중 72개에 테스트를 적용해, 공용 코드 라인 커버리지 52%를 확보했습니다. 그중 components는 지금도 계속 커버리지를 넓혀가는 중입니다.

공용 코드를 고치거나 새로 추가할 때는 테스트 코드를 함께 작성하는 걸 컨벤션으로 정했습니다. 덕분에 공용 코드를 훨씬 더 안정적으로 고칠 수 있게 됐습니다.

종합 결과

배운 점

모노레포 전환 여정을 통해 저희가 얻은 교훈은 이랬습니다. 비슷한 전환을 앞두고 계신 분들께 도움이 되었으면 합니다.

여러분의 팀도 여러 레포에 흩어진 공용 코드 때문에 같은 버그를 몇 번씩 고치고 계신가요? 그렇다면 도구 선택보다 먼저, "왜 한 곳에서 관리해야 하는가"에 대한 팀의 합의부터 만들어두시길 권합니다.