Turborepo 6개월 실사용 후기: 기대와 현실
모노레포를 Turborepo로 구성하고 6개월 돌린 솔직한 후기. 빌드는 빨라졌는데 다른 게 느려졌다.
왜 모노레포를 시작했나
패키지가 5개였다. 프론트엔드 2개, 백엔드 1개, 공유 라이브러리 2개. 각각 별도 레포로 관리하다 보니 공유 라이브러리를 수정할 때마다 npm에 배포하고, 다른 프로젝트에서 버전 올리고, 테스트하고... 이 과정이 한 번에 45분씩 걸렸다.
모노레포로 합치면 이 고통이 사라진다고 했다. 반은 맞고 반은 틀렸다.
Turborepo를 고른 이유
Nx도 검토했다. 솔직히 Nx가 기능이 더 많다. 근데 설정이 복잡했다. 팀원 3명 중 2명이 모노레포 경험이 없었고, 러닝 커브가 낮은 걸 원했다. Turborepo는 turbo.json 하나면 기본 설정이 끝났다.
(사실 그냥 Vercel 생태계여서 고른 것도 있다.)
빌드 캐시가 진짜 빠르다
이건 인정한다. Remote caching을 설정하고 나서 CI 빌드 시간이 평균 8분 42초에서 2분 15초로 줄었다. 변경되지 않은 패키지는 캐시에서 가져오니까 전체 빌드를 돌릴 필요가 없다.
로컬에서도 turbo run build를 연속으로 치면 두 번째부터는 거의 즉시 끝난다. 이 경험이 좋아서 초반에는 만족스러웠다.
근데 문제가 슬슬 보이기 시작했다
3개월쯤 됐을 때 패키지 간 의존성이 꼬이기 시작했다. 공유 라이브러리 A가 공유 라이브러리 B를 참조하고, 프론트엔드가 A와 B를 동시에 참조하는데 버전이 다르다든가. TypeScript 경로 설정이 패키지마다 달라서 IDE에서 자동완성이 안 된다든가.
이런 문제는 Turborepo의 문제가 아니라 모노레포 자체의 문제다. 근데 Turborepo가 이런 걸 자동으로 해결해줄 거라고 기대했던 것 자체가 실수였다.
팀원들의 반응
"왜 다른 패키지 건드렸는데 내 프로젝트가 깨지냐?" 이 질문을 4개월 동안 매주 들었다. 모노레포의 핵심이 바로 그거라고 설명해도 납득이 잘 안 되는 눈치였다.
PR 하나에 여러 패키지 변경이 섞이니까 코드 리뷰도 복잡해졌다. 리뷰어가 "이 부분은 내 담당이 아닌데?" 하는 상황이 잦아졌다. CODEOWNERS를 설정해서 좀 나아졌긴 한데 완벽하지는 않다.
(모노레포는 기술 도구가 아니라 협업 방식의 변화라는 걸 늦게 깨달았다.)
빌드는 빨라졌는데 다른 게 느려졌다
git clone이 느려졌다. 레포 크기가 1.2GB까지 불어났다. CI에서 shallow clone을 쓰면 해결되긴 하는데, 로컬에서 처음 clone할 때 3분 47초가 걸린다.
ESLint도 느려졌다. 전체 린트를 돌리면 2분 넘게 걸린다. Turborepo의 필터링을 쓰면 되는데 매번 --filter 옵션을 치는 게 귀찮다.
6개월 후 솔직한 평가
빌드 캐시와 태스크 병렬 처리는 확실히 좋다. 공유 코드 수정 후 즉시 반영되는 것도 좋다. 근데 팀이 작고 패키지가 적다면 그냥 멀티레포가 더 편할 수 있다.
우리 팀 규모(3명)에 패키지 5개는 사실 모노레포 안 해도 됐을 수도 있다. 이미 세팅해놨으니 쓰고 있긴 한데, 다음 프로젝트에서도 무조건 모노레포를 쓸지는 모르겠다.