리포지토리 구조화하기
turbo는 JavaScript 생태계의 패키지 관리자 기능인 Workspaces 위에 구축되어 있으며, 이를 통해 하나의 리포지토리에 여러 패키지를 그룹화할 수 있습니다.
이러한 규칙을 따르는 것이 중요한 이유는 다음과 같습니다:
- 리포지토리의 모든 도구에 대해 해당 규칙을 활용할 수 있습니다
- 기존 리포지토리에 Turborepo를 빠르고 점진적으로 적용할 수 있습니다
이 가이드에서는 turbo의 기반을 마련할 수 있도록 멀티 패키지 워크스페이스(모노레포) 설정 과정을 안내합니다.
시작하기
워크스페이스 구조를 수동으로 설정하는 것은 번거로울 수 있습니다. 모노레포가 처음이라면 올바른 워크스페이스 구조로 바로 시작할 수 있도록 create-turbo 사용을 권장합니다.
그런 다음 이 가이드에 설명된 특성에 대해 리포지토리를 검토할 수 있습니다.
워크스페이스의 구조
JavaScript에서 워크스페이스는 단일 패키지 또는 패키지 모음일 수 있습니다. 이 가이드에서는 종종 "모노레포"라고 불리는 멀티 패키지 워크스페이스에 초점을 맞춥니다.
아래는 유효한 워크스페이스로 만드는 create-turbo의 구조적 요소가 강조 표시되어 있습니다.
최소 요구 사항
모노레포에서 패키지 지정하기
패키지 디렉토리 선언하기
먼저 패키지 관리자가 패키지의 위치를 설명해야 합니다. 애플리케이션과 서비스를 위한 apps/와 라이브러리 및 도구 등 그 외의 모든 것을 위한 packages/로 패키지를 나누는 것부터 시작하는 것을 권장합니다.
이 구성을 사용하면 apps 또는 packages 디렉토리에 package.json이 있는 모든 디렉토리가 패키지로 간주됩니다.
Turborepo는 JavaScript 생태계의 패키지 관리자 간에 모호한 동작으로 인해 apps/** 또는 packages/**와 같은 중첩된 패키지를 지원하지 않습니다. apps/a에 패키지를, apps/a/b에 다른 패키지를 배치하는 구조를 사용하면 오류가 발생합니다.
디렉토리별로 패키지를 그룹화하려면 packages/* 및 packages/group/*와 같은 glob을 사용하고 packages/group/package.json 파일을 생성하지 않으면 됩니다.
각 패키지의 package.json
패키지 디렉토리에는 패키지를 패키지 관리자와 turbo가 검색할 수 있도록 하는 package.json이 있어야 합니다. 패키지의 package.json 요구 사항은 아래에 있습니다.
루트 package.json
루트 package.json은 워크스페이스의 기반입니다. 아래는 루트 package.json에서 찾을 수 있는 일반적인 예입니다:
루트 turbo.json
turbo.json은 turbo의 동작을 구성하는 데 사용됩니다. 작업 구성 방법에 대한 자세한 내용은 작업 구성 페이지를 참조하세요.
패키지 관리자 lockfile
lockfile은 패키지 관리자와 turbo 모두에 대해 재현 가능한 동작의 핵심입니다. 또한 Turborepo는 lockfile을 사용하여 워크스페이스 내 내부 패키지 간의 의존성을 이해합니다.
turbo를 실행할 때 lockfile이 없으면 예측할 수 없는 동작이 나타날 수
있습니다.
패키지의 구조
워크스페이스 내에서 각 패키지를 고유한 단위로 설계하는 것부터 시작하는 것이 가장 좋습니다. 높은 수준에서 각 패키지는 자체 package.json, 도구 구성 및 소스 코드가 있는 자체 작은 "프로젝트"와 거의 같습니다. 이 아이디어에는 한계가 있지만 시작하기에 좋은 정신적 모델입니다.
또한 패키지에는 워크스페이스의 다른 패키지가 exports로 지정된 패키지에 액세스하는 데 사용할 수 있는 특정 진입점이 있습니다.
패키지의 package.json
name
name 필드는 패키지를 식별하는 데 사용됩니다. 워크스페이스 내에서 고유해야 합니다.
npm 레지스트리의 다른 패키지와의 충돌을 피하기 위해 내부 패키지에 네임스페이스 접두사를 사용하는 것이 좋습니다. 예를 들어 조직 이름이 acme인 경우 패키지 이름을 @acme/package-name으로 지정할 수 있습니다.
문서와 예제에서는 npm 레지스트리에서 사용되지 않고 클레임할 수 없는 네임스페이스이기 때문에 @repo를 사용합니다. 그대로 유지하거나 자체 접두사를 사용할 수 있습니다.
scripts
scripts 필드는 패키지의 컨텍스트에서 실행할 수 있는 스크립트를 정의하는 데 사용됩니다. Turborepo는 이러한 스크립트의 이름을 사용하여 패키지에서 실행할 스크립트(있는 경우)를 식별합니다. 이러한 스크립트에 대한 자세한 내용은 작업 실행 페이지에서 설명합니다.
exports
exports 필드는 패키지를 사용하려는 다른 패키지의 진입점을 지정하는 데 사용됩니다. 한 패키지에서 다른 패키지의 코드를 사용하려는 경우 해당 진입점에서 가져옵니다.
예를 들어 @repo/math 패키지가 있는 경우 다음과 같은 exports 필드를 가질 수 있습니다:
이 예제는 단순화를 위해 Just-in-Time 패키지 패턴을 사용합니다. TypeScript를 직접 내보내지만 대신 컴파일된 패키지 패턴을 선택할 수도 있습니다.
이 예제의 exports 필드는 최신 버전의 Node.js 및 TypeScript가 필요합니다.
이를 통해 다음과 같이 @repo/math 패키지에서 add 및 subtract 함수를 가져올 수 있습니다:
이러한 방식으로 exports를 사용하면 세 가지 주요 이점이 있습니다:
- 배럴 파일 피하기: 배럴 파일은 동일한 패키지의 다른 파일을 다시 내보내어 전체 패키지에 대한 하나의 진입점을 생성하는 파일입니다. 편리해 보일 수 있지만 컴파일러와 번들러가 처리하기 어렵고 빠르게 성능 문제로 이어질 수 있습니다.
- 더 강력한 기능:
exports는 조건부 Exports와 같은main필드에 비해 다른 강력한 기능도 있습니다. 일반적으로 가능한 한main대신exports를 사용하는 것이 좋습니다. 이는 더 현대적인 옵션이기 때문입니다. - IDE 자동 완성:
exports를 사용하여 패키지의 진입점을 지정하면 코드 편집기가 패키지의 exports에 대한 자동 완성을 제공할 수 있습니다.
imports (선택 사항)
imports 필드는 패키지 내의 다른 모듈에 대한 하위 경로를 만드는 방법을 제공합니다. 파일을 이동하는 리팩토링에 더 탄력적인 간단한 가져오기 경로를 작성하는 "바로 가기"와 같다고 생각할 수 있습니다. 방법을 알아보려면 TypeScript 페이지를 참조하세요.
유사한 목표를 달성하는 TypeScript의 compilerOptions#paths 옵션에 더 익숙할 수 있습니다. TypeScript 5.4부터 TypeScript는 imports에서 하위 경로를 추론할 수 있으므로 Node.js 규칙과 함께 작업하게 되므로 더 나은 옵션입니다. 자세한 내용은 TypeScript 가이드를 참조하세요.
소스 코드
물론 패키지에 일부 소스 코드가 필요합니다. 패키지는 일반적으로 src 디렉토리를 사용하여 소스 코드를 저장하고 dist 디렉토리(패키지 내에도 위치해야 함)로 컴파일하지만 이는 요구 사항이 아닙니다.
일반적인 함정
- TypeScript를 사용하는 경우 워크스페이스의 루트에
tsconfig.json이 필요하지 않을 수 있습니다. 패키지는 일반적으로 워크스페이스의 별도 패키지에서 공유tsconfig.json을 기반으로 자체 구성을 독립적으로 지정해야 합니다. 자세한 내용은 TypeScript 가이드를 참조하세요. - 가능한 한 패키지 경계를 넘어 파일에 액세스하는 것을 피해야 합니다. 한 패키지에서 다른 패키지로 이동하기 위해
../를 작성하는 경우 필요한 곳에 패키지를 설치하고 코드로 가져와서 접근 방식을 다시 생각할 기회가 있을 수 있습니다.
다음 단계
워크스페이스가 구성되었으므로 이제 패키지 관리자를 사용하여 패키지에 의존성을 설치할 수 있습니다.