연휴를 맞이하여 시간적 여유가 생겼기에, 이번 기회에 모노레포 구축에 도전하기로 결정했습니다.
본 글에서는 TurboRepo의 설치 과정과 작동 원리를 탐색하고, 실제로 적용해보는 과정을 단계별로 소개할 예정입니다.
- TurboRepo 설치하기
- ESLint config를 통해 모노레포 동작 방식 이해하기
- tailwindCSS 공통으로 적용해보기
모노레포 구축을 위해 공식 문서를 참고하여 TurboRepo 설치를 시작했습니다.
가장 먼저, 모노레포를 전역 환경에 설치하는 것으로 출발했습니다.
npm install turbo --global
전역 설치를 완료 한 후, 새로운 모노레포 프로젝트 생성을 위해 아래 명령어를 실행했습니다.
npx create-turbo@latest
이 과정을 통해 turborepo에서 권장하는 기본 세팅을 할 수 있습니다.
실행이 끝이 나면 apps
와 packages
두 개의 폴더를 생성합니다.
이 폴더에는 개별 프로젝트들이 위치합니다. 초기 설정으로 docs
와 web
이라는 두 개의 Next.js 최신 버전 프로젝트가 생성됩니다.
모노레포 내에서 공통적으로 사용될 설정 파일이나 UI 컴포넌트를 담는 곳입니다.
초기 상태에는 eslint-config
와 ts-config
가 포함되어 있으며, 공용 UI 컴포넌트도 여기에 위치합니다.
설치를 완료하면, 다양한 스크립트 명령어가 package.json 파일에 추가됩니다. 이를 통해 최상위 폴더에서 여러 앱을 통합적으로 실행하거나 빌드할 수 있습니다.
Prettier, ESLint, 그리고 Turbo를 활용해 여러 스크립트를 실행할 수 있으며, 필요에 따라 추가적인 스크립트를 구성할 수 있습니다. 전역적으로 작업을 진행하고자 한다면, 이곳에 필요한 도구를 설치하고 Turbo를 통해 관리하면 됩니다.
이제 TurboRepo가 어떤 방식으로 작동하는지 packages에 eslint 설정을 살펴보며 알아보겠습니다.
모노레포 구조에서 packages
디렉토리는 프로젝트 전반에 걸쳐 공통적으로 사용될 설정, 라이브러리, 컴포넌트 등을 관리하는 공간입니다.
이번 섹션에서는 packages
내의 eslint-config
설정을 예로 들어, 모노레포 내에서 공통 설정을 어떻게 적용하고 사용하는지 설명하겠습니다.
eslint-config
폴더는 ESLint 설정을 위한 공간으로, 다양한 프로젝트 유형(예: 라이브러리용, Next.js용 등)에 맞춰 ESLint 설정을 제공합니다. 현재 구조는 다음과 같습니다:
library.js next.js node_modules/ package.json react-internal.js README.md
위와 같은 구조를 이룹니다.
이름을 보면 라이브러리용, next 용 이렇게 보입니다.
먼저 eslint-config에 package.json을 살펴보겠습니다.
{
"name": "@repo/eslint-config",
"version": "0.0.0",
"private": true,
"files": [
"library.js",
"next.js",
"react-internal.js"
],
"devDependencies": {
"@vercel/style-guide": "^5.1.0",
"eslint-config-turbo": "^1.11.3",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-only-warn": "^1.1.0",
"@typescript-eslint/parser": "^6.17.0",
"@typescript-eslint/eslint-plugin": "^6.17.0",
"typescript": "^5.3.3"
}
}
이 구조에서 주목해야 할 주요 요소는 다음과 같습니다.
패키지 이름: package.json
에서 정의한 패키지 이름(@repo/eslint-config
)을 통해, 다른 프로젝트에서 이 ESLint 설정을 npm install @repo/eslint-config
명령어로 설치할 수 있습니다.
files 필드: package.json
의 files
필드에는 library.js
, next.js
, react-internal.js
와 같이 공개하고자 하는 설정 파일들이 명시되어 있습니다. 이는 패키지를 설치할 때 이 파일들만이 포함됨을 의미합니다.
extends: ["@repo/eslint-config/next.js"]
이와 같은 방식으로 files에 있는 파일들을 불러와 .eslintrc.js
에서 사용합니다.
설치된 의존성: devDependencies
에는 ESLint 설정에 필요한 다양한 플러그인과 설정 패키지가 포함되어 있습니다.
중요한 점은 이곳에 ESLint 자체가 포함되어 있지 않다는 것입니다.
이는 eslint-config
가 실제 ESLint 실행을 위한 것이 아니라, ESLint를 위한 설정을 제공하기 위한 것임을 의미하고, 여기서 사용할 의존성만을 설치한다는 것입니다.
이제는 esconfig 파일을 어떤 식으로 작성했는지 살펴보겠습니다.
eslint-config/next.js
살펴보기우리는 eslint 설정을 할 때, tsconfig를 불러오는 경우가 있습니다.
각 프로젝트별로 이를 불러와서 사용하는 방법이 필요할 것입니다.
그래서 최상단을 살펴보면 이 문제를 해결한 코드가 있습니다.
tsconfig.json
로드: next.js
설정 파일을 살펴보면 상단에 위치한 코드는 현재 프로젝트의 tsconfig.json
파일을 동적으로 찾아와서 ESLint에 연결합니다. 이는 ESLint가 TypeScript 설정을 인식하게 하여, 프로젝트에 맞는 타입 체크와 규칙 적용이 가능하게 합니다.
const { resolve } = require("node:path");
const project = resolve(process.cwd(), "tsconfig.json");
settings: {
"import/resolver": {
typescript: {
project,
},
},
},
extends
구성: ESLint의 extends
옵션을 통해 다양한 ESLint 규칙 및 설정을 상속받습니다. extends: [
"eslint:recommended",
"prettier",
require.resolve("@vercel/style-guide/eslint/next"),
"eslint-config-turbo",
],
이를 통해 packages/eslint-config에서 어떤식으로 준비하였는지 살펴보았고,
이제 apps에서 어떻게 적용하는지 보겠습니다.
apps
내 프로젝트 설정: 우리는 apps/web
프로젝트를 살펴보겠습니다.apps/web/package.json
에는 @repo/eslint-config
가 의존성으로 추가합니다. 이는 eslint-config
에 설정한 이름과 동일하며, 이를 가져와 프로젝트에 설치하고 사용할 수 있음을 의미합니다.
"@repo/eslint-config": "*"
web/.eslintrc.js
와 같은 ESLint 설정 파일에서 @repo/eslint-config/next.js
를 extends
에 추가함으로써, 공통 ESLint 설정을 해당 프로젝트에 적용할 수 있습니다.이는 eslint-config
에 설정한 파일 이름을 불러와 사용하는 것입니다.
module.exports = {
root: true,
extends: ["@repo/eslint-config/next.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
},
};
package.json
과 .eslintrc.js
에서도 @repo/eslint-config
와 같은 공통 패키지를 참조하여 설정을 적용할 수 있습니다. 최상위 package.json
{
"name": "my-turborepo",
"devDependencies": {
"@repo/eslint-config": "*",
"@repo/typescript-config": "*",
"prettier": "^3.1.1",
"turbo": "latest"
},
}
최상위 .esconfigrc.js
// This configuration only applies to the package manager root.
/** @type {import("eslint").Linter.Config} */
module.exports = {
ignorePatterns: ["apps/**", "packages/**"],
extends: ["@repo/eslint-config/library.js"],
parser: "@typescript-eslint/parser",
parserOptions: {
project: true,
},
};
실제로 어떻게 적용하였는지 살펴보았습니다.
이제는 우리가 packages에 폴더를 만들고 이를 프로젝트에 적용해보고
동작 방식을 올바르게 이해했는지 확인해보겠습니다.
모노레포 구조에서 packages
디렉토리를 활용해 공통 설정을 관리하는 방법을 탐구하여, 모노레포에 대한 이해를 높혀보겠습니다.
packages
에 tailwind-config
폴더를 생성하고, 필요한 package.json
및 설정 파일들을 작성합니다. 이 폴더 내에서 Tailwind CSS 관련 설정을 중앙에서 관리할 수 있습니다. {
"name": "@repo/tailwind-config",
"version": "0.0.0",
"private": true,
"exports": { ".": "./tailwind.config.ts" },
"devDependencies": {
"@repo/typescript-config": "*",
"tailwindcss": "^3.4.0"
}
}
name
먼저 이름은 기존 방식에 맞추어 @repo/tailwind-config로 설정해주었습니다.
이름에 맞추어 설치하여서 설정을 가져올 수 있게 할 것입니다.
private
private은 이를 외부에 공개하지않고 내부적으로 사용하기 위해 작성해주었습니다.
exports
exports에 작성하면 이를 import 해서 사용할 수 있습니다.
import sharedConfig from "@repo/tailwind-config";
파일 이름 tailwind.config.ts로 작성할 것이므로 미리 작성해주었습니다.
devDependencies
우리는 ts를 통해 tailwindCSS를 작성할 것이고,
tailwindCSS가 가지고 있는 type이 필요하므로 이를 설치하였습니다.
TypeScript 설정 연결: tailwind-config
내에 tsconfig.json
파일을 생성하여, @repo/typescript-config
의 기본 설정을 확장합니다. 이는 TypeScript를 사용하는 Tailwind 설정 파일의 타입 체킹을 지원합니다.
{
"extends": "@repo/typescript-config/base.json",
"include": ["."],
"exclude": ["dist", "build", "node_modules"]
}
Tailwind 설정 파일 작성: Tailwind CSS 설정을 정의하는 tailwind.config.ts
파일을 작성합니다. 이 설정 파일에서는 프로젝트별로 다르게 설정될 수 있는 content
필드를 제외하고, 테마 확장, 플러그인 추가 등의 공통 설정을 정의합니다.
import type { Config } from "tailwindcss";
const config: Omit<Config, "content"> = {
theme: {
extend: {
colors: {
"custom-blue": "#007bff"
}
}
},
plugins: []
};
export default config;
이제 기본적인 세팅은 끝이 났고, 이를 apps에 있는 프로젝트에 적용해보겠습니다.
apps/web
프로젝트에서 tailwindcss
, postcss
, autoprefixer
, 그리고 중앙에서 관리되는 @repo/tailwind-config
를 설치합니다. yarn add -D tailwindcss postcss autoprefixer @repo/tailwind-config
postcss.config.ts
및 tailwind.config.ts
파일을 생성하고, 공통 Tailwind 설정을 import하여 적용합니다.apps/web
프로젝트에 packages에서 만든 설정을 적용할 수 있습니다.tailwind.config.ts
import sharedConfig from "@repo/tailwind-config";
import type { Config } from "tailwindcss";
const config: Pick<Config, "content" | "presets"> = {
content: ["./app/**/*.tsx"],
presets: [sharedConfig]
};
export default config;
아래 작성도 해주셔야합니다.
postcss.config.ts
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
global.css
@tailwind base;
@tailwind components;
@tailwind utilities;
<main>
<div className="text-custom-blue">테일윈드 확인용</div>
</main>
우리가 packages에 작성했던 custom-blue가 작동하는 것을 확인하였습니다.
이를 통해 tailwind-config에서 잘 작성했는지 확인할 수 있었습니다.
위와 같은 방식으로 모노레포 구조를 만들어주고 관리한다면 효과적으로 만들 수 있을 것입니다.
이를 끝으로 간략하게 모노레포에 대해 알아보았습니다.
아쉬운 점이나 설명이 이상한 점이 있다면 댓글로 남겨주시면 감사하겠습니다.
출처 : https://turbo.build/