안녕하세요. J4J입니다.
이번 포스팅은 모노레포를 위한 turborepo 사용하는 방법에 대해 적어보는 시간을 가져보려고 합니다.
모노레포란 ?
모노레포는 하나의 저장소에 애플리케이션 서비스, 라이브러리 패키지 등을 서로 구분하지 않고 여러 개의 프로젝트를 함께 관리하는 방식을 말합니다.
보통 저장소를 관리할 때 가장 많이 접하는 구조는 멀티레포가 될 것입니다.
멀티레포는 1개의 프로젝트마다 1개의 저장소를 소유하고 있는 것으로, 말 그대로 서로 독립적인 방식으로 관리되며 각자의 프로젝트에 관여하지 않는 구조를 말합니다.
간단하게 예시를 들면 멀티레포는 다음과 같은 구조가 나올 수 있습니다.
// design-system repo
- design-system
// application service repo
- application-service
// common-module repo
- common-module
하지만 이런 서비스들을 모두 모노레포로 구성한다면 다음과 같은 구조가 나올 수 있습니다.
// 통합 repo
- apps
ㄴ application-service
- packages
ㄴ design-system
ㄴ common-module
구조를 잡을 때 모노레포를 기반으로 구성하는 이유는 무엇일까요?
재 사용 가능한 비즈니스 로직을 만들기 위해 노력하는 요즘 시대에서 더 유연하고 편리한 방식으로 관리하기 원한다면 모노레포를 하는 것이 좋은 방향이 될 수 있습니다.
왜냐하면 멀티레포로 구성했을 때는 이럼 흐름이 발생할 수 있습니다.
- 공통 모듈 개발 → 배포
- 디자인 시스템에 공통 모듈 적용 → 배포
- 애플리케이션 서비스에 공통 모듈 및 디자인 시스템 적용
이 흐름에서 관리에 대한 어려움을 느낄 수 있는 부분은 매번 변경 사항들이 발생할 때마다 배포하고, 버전 관리하는 작업들이 존재하는 것입니다.
또한, 배포되는 패키지들 간에도 의존 관계가 존재하기 때문에 의존되는 순서를 고려한 배포도 진행되어야 합니다.
하지만 모노레포를 이용한다면 흐름을 다음과 같이 변경할 수 있습니다.
- 공통 모듈 개발
- 디자인 시스템에서 공통 모듈 사용
- 애플리케이션 서비스에서 공통 모듈 및 디자인 시스템 사용
가장 큰 차이는 매번 배포를 하며 버전 관리를 하지 않아도 된다는 부분이 존재합니다.
또한 의존되는 순서도 크게 고려하지 않아도 변경된 기능들을 어렵지 않게 적용할 수 있으며, 자연스러운 흐름으로 의존 순서를 파악해 볼 수도 있게 됩니다.
실버 불렛은 없는 것처럼 이런 장점들이 존재한다고 모노레포가 항상 정답은 아닙니다.
모노레포를 사용하게 된다면 멀티레포를 사용할 때보다 다음과 같은 단점들이 발생할 수 있습니다.
- 작은 변경 사항에도 모든 서비스와 패키지들을 build 해야 할 수 있음
- repo가 쉽게 커지며, 1개의 repo에 많은 수의 개발자들이 참여하여 관리가 어려워질 수 있음
- devops 기반의 자동화 처리 속도가 느려질 수 있음
그러므로 다음과 같은 상황을 겪고 계시는 분들이라면 모노레포를 사용하는 것을 권장합니다.
- 모든 애플리케이션에 공통적으로 사용되는 유틸 기능, 비즈니스 도메인이 많이 존재함
- 1개의 repo에서 모든 서비스들을 통합 관리가 필요함
- MFE 기반으로 서비스를 제공하고 있음
- 모든 서비스들의 배포 주기가 동일하게 관리가 되어야 함
반대로 다음과 같은 상황을 겪고 계시는 분들이라면 멀티레포를 사용하는 것을 권장합니다.
- 애플리케이션의 비즈니스가 서로 완전히 독립적임
- 서비스들의 배포 주기가 동일하게 관리될 수 없음
- 공통적으로 사용되는 기능들이 존재하지 않음
Turborepo
turborepo는 모노레포 환경에 존재하는 다양한 패키지와 애플리케이션들을 위한 고성능 빌드 시스템입니다.
turborepo의 경우 javascript 및 typescript를 이용한 코드 환경 기반으로 동작하기 때문에 react, nextjs 등과 같은 frontend 프로젝트들을 관리하는데 용이하게 사용될 수 있습니다.
turborepo를 사용해야 하는 이유는 무엇일까요?
위에서도 한번 얘기를 했지만, 모노레포 환경을 기반으로 서비스들을 관리하게 된다면 소스 코드 변경에 따른 build, test 등을 하는 과정에서 굉장히 많은 시간이 소요하게 됩니다.
그래서 한 번의 소스 작업을 하는 과정에 처리되는 속도가 굉장히 느려지기 때문에 불편함이 야기됩니다.
이 상황에서 turborepo는 다음과 같은 기능들을 제공해 줍니다.
- 한 번 build 된 파일들을 캐싱한 뒤 다음 build를 수행할 때 재 사용
- 변경이 발생할 때 다시 build가 이루어져야 하는 파일들을 관리
- 패키지 간 의존 순서를 고려하여 순서대로 build 처리를 수행
- 서로 관련 없는 작업들을 병렬적으로 처리
- 로컬뿐 아니라 원격 서버 환경에 build 결과물을 공유 가능 & 캐싱 처리도 함께 제공
- 등등..
실제로 저도 실무에 turborepo를 도입을 처음 하게 된 계기가 패키지 간 의존 순서를 고려하는 기능 때문이었습니다.
구조상 개발 및 관리되는 패키지의 개수가 많아질수록 서로 의존하는 패키지가 점점 많아지게 되는데, 단순히 명령어 한 번만 실행하면 의존 순서를 고려하지 않아도 스스로 판단하여 작업들을 모두 마무리해주는 것이 굉장히 도움이 많이 되고 있습니다.
그 외에도 build 결과물에 대해 캐싱 전략을 세울 수 있기 때문에 패키지가 커지고 많아지더라도 빠른 속도로 작업들을 처리하는 것을 확인할 수 있었습니다.
그래서 모노레포 기반으로 서비스를 관리하게 된다면 turborepo의 도입은 필수라고 생각합니다.
하지만 한 가지 알고 가야 될 점은 같이 모노레포 환경을 효율적으로 사용할 수 있도록 도와주는 것이 turborepo만 있는 것은 아닙니다.
nx, bazel 등 상황에 따라 적용해 볼 수 있는 것들이 더 다양하게 존재합니다.
그러므로 각각의 기술들을 사용할 때 얻을 수 있는 이점과, 얻고자 하는 이점이 무엇인지를 명확히 고민해 볼 필요가 있습니다.
다만, 본인의 프로젝트가 다음과 같은 구조를 가지고 있다면 turborepo를 선택하는 것은 현명한 방법이 될 수 있다고 생각합니다.
- 모든 프로젝트가 javascript/typescript 기반으로 관리
- react/nextjs 등을 이용하여 라이브러리를 구성하거나 애플리케이션을 개발하고 있는 서비스
- 간편한 설정을 통해 모노레포의 이점을 얻고자 하는 서비스
- 모노레포 내부에서 패키지 간 제약/경계 설정 등의 구조 강제화를 적용하지 않아도 되는 서비스
주요 Task 설정 방법
Turborepo Configuring 문서를 확인하면 다양한 task 설정 방법에 대해 확인할 수 있습니다.
Configuring tasks
Learn how to describe the workflows in your repository to get them done as fast as possible.
turborepo.dev
해당 문서에 대해서는 정독하는 것을 권장드리고 싶을 정도로 turborepo를 사용하기 전 한번 봐보시면 좋을 것 같다고 생각하고, 문서 내부의 내용 중 가장 기초적인 설정 방법들에 대해서만 간단하게 소개해 보려고 합니다.
turborepo에 대한 주요 설정들은 turbo.json 파일들에 이루어집니다.
그리고 이곳에 설정되는 것들은 작업 순서와 캐싱 전략 등을 다양하게 다루게 됩니다.
[ 1. 작업 순서 ]
가장 먼저 작업 순서를 얘기할 수 있습니다.
대표적으로 build를 할 때 의존하고 있는 다른 패키지들의 build가 먼저 이루어지는 설정을 얘기할 수 있고, 목표를 달성하기 위해 다음과 같이 turbo.json 파일을 구성해볼 수 있습니다.
// turbo.json
{
"tasks": {
"build": {
"dependsOn": ["^build"]
}
}
}
이 설정의 핵심은 "^" 연산자입니다.
"^" 연산자가 포함되어 있다면 task에 정의된 명령어를 실행하기 전 dependsOn에 설정된 명령어들의 의존 순서를 고려하여 실행되도록 도와줍니다.
또한 서로 다른 명령어들 간 의존 순서를 고려해 볼 수 있습니다.
대표적으로 test를 수행하기 전 build가 먼저 이루어져야 한다면 다음과 같이 설정해볼 수 있습니다.
// turbo.json
{
"tasks": {
"test": {
"dependsOn": ["build"]
}
}
}
이 외에도 필요하다면 특정 패키지의 명령어를 더 세심하게 설정하여 적용해볼 수 있습니다.
[ 2. 캐싱 설정 ]
캐싱 설정은 가장 먼저 inputs 설정에 대해 알아야 합니다.
inputs 설정은 다음과 같은 방식으로 적용할 수 있고, inputs 설정에 포함된 파일이 변경되는 경우에만 cache miss가 되어 명령어가 새롭게 동작되는 결과를 만듭니다.
{
"tasks": {
"build": {
"inputs": ["**/*.ts", "**/*.tsx"]
}
}
}
위와 같이 설정을 한다면 ts 및 tsx 파일들 중 변경되는 파일이 있을 때만 build 명령어가 캐싱되지 않고 동작하게 됩니다.
하지만 그 외의 파일들이 변경된다면 캐싱되어 있는 정보를 그대로 사용하게 됩니다.
다음으로 outputs 설정에 대해 알아야 합니다.
outputs의 설정은 어떤 결과물을 캐시에 저장하는 것인지를 판단하며, 다음과 같은 방식으로 설정해볼 수 있습니다.
{
"tasks": {
"build": {
"outputs": [".next/**", "dist/**", "build/**"]
}
}
}
위와 같이 설정을 하게 되면 build 명령어의 캐싱 정보로 outputs에 나열한 결과물들을 사용하게 됩니다.
그리고 어떠한 변화 없이 한번 더 build 명령어를 수행하게 되면 캐싱된 정보를 이용하여 outputs에 해당되는 파일들을 다시 구성해 줍니다.
최종적으로 캐싱 기능을 올바르게 사용하고자 하면 inputs와 outputs 설정을 올바르게 설정해야 합니다.
cache hit인지 cache miss인지를 판단하는 것은 inputs가 될 것이고 어떤 결과물을 캐싱해 둘지 판단하는 것은 outputs가 담당하게 되는 것입니다.
Turborepo 구성하기
turborepo를 구성하는 방법은 Turborepo Installation 문서를 참고해 보셔도 도움이 됩니다.
Installation
Learn how to get started with Turborepo.
turborepo.dev
[ 1. turborepo 설치하기 ]
turborepo는 다음 명령어 한 번으로 turborepo를 사용할 수 있는 기본 구조가 바로 구성됩니다.
명령어를 입력하고 각자가 원하는 환경을 선택만 해주시면 됩니다.
$ npx create-turbo@latest
[ 2. turborepo 구조 확인하기 ]
설치가 완료되면 다음과 같은 샘플 구조를 확인할 수 있습니다.
apps의 경우 개발되어야 하는 애플리케이션 서비스들을 작업하는 공간이 됩니다.
packages의 경우는 모든 서비스에 공통적으로 사용되어야 하는 패키지들과 publish 처리가 이루어져야 하는 패키지들을 관리하는 곳이 됩니다.

[ 3. 명령어 실행하기 ]
turborepo에 관리되는 모든 애플리케이션 및 패키지들을 한 번에 일괄적으로 실행하는 것을 원하면 프로젝트 가장 상위에 있는 package.json에 script 관리가 되어야 합니다.
물론, 원한다면 각 서비스 및 패키지의 구조 안으로 들어가면 존재하는 package.json을 이용하여 멀티 레포의 환경처럼 작업을 실행할 수도 있습니다.
그래서 초기 구조를 기준으로 build script 실행을 예시로 들면, turbo 명령어가 수행되고 각 애플리케이션 및 패키지의 package.json에 build 명령어를 일괄적으로 실행하도록 도와줍니다.
$ npm run build
이상으로 모노레포를 위한 turborepo 사용하는 방법에 대해 간단하게 알아보는 시간이었습니다.
읽어주셔서 감사합니다.
'SPA > React' 카테고리의 다른 글
| Turborepo 원격 캐시 설정 방법 (Vercel / Self-hosted) (0) | 2026.02.01 |
|---|---|
| Tailwind로 만드는 React 컴포넌트 라이브러리 배포 가이드, tsup + tsc 활용 (0) | 2026.01.23 |
| Tailwind className 관리 가이드, clsx + twMerge + cva 역할 정리 (0) | 2026.01.18 |
| React Typescript 컴포넌트 라이브러리 배포 가이드, tsup + tsc 활용 (0) | 2026.01.10 |
| [React] SSE (Server-Sent Events) 사용하여 실시간 통신하기 (0) | 2024.05.08 |
댓글