React

TurboRepo로 monorepo 환경 구성하기

하늘을난모기 2021. 12. 25. 19:38

글보다 그냥 코드를 보고 이해하길 원한다면 하단 레포를 참고하면 된다.

https://github.com/KimHunJin/turbo-repo-test

 

GitHub - KimHunJin/turbo-repo-test

Contribute to KimHunJin/turbo-repo-test development by creating an account on GitHub.

github.com

 

 

최근에 vercel에서 turborepo를 인수하면서 공식적으로 turborepo를 지원하기 시작했다.

아직 vercel에 바로 연결은 안되지만, 로컬환경에서 멀티모듈 환경을 구성하기는 확실히 간편해졌다.

배포를 하기 위한 작업은 조금 더 시간이 필요할거 같다만..

https://turborepo.org/docs/getting-started

 

Getting Started | Turborepo

 

turborepo.org

위 페이지를 참고하여 시작할 수 있다.

npx create-turbo@latest

위 명령어를 치면 간편하게 vercel 환경을 구성할 수 있다.

하지만, 위 명령어를 사용하지 않고 yarn으로 시작하는 방법을 다뤄보도록 한다.

 

1. 프로젝트 폴더를 생성

yarn init 으로 프로젝트 초기 설정을 진행한다.

 

2. vercel을 추가

yarn add turbo -W --dev

이제 최소한의 vercel을 다룰 준비가 됐다.

 

3. 필요한 폴더 구축하기

mkdir apps
mkdir packages

apps에는 보통 실제로 화면을 구성하기 위한 페이지들의 프로젝트 폴더가 된다.

예를들어 admin페이지와 client페이지를 구분하며 다른 독립적인 폴더로 구성하고 싶다면

apps폴더 안에 admin과 client폴더를 만들고 각각 다른 환경을 구성하면 된다.

packages에는 각종 util이나 config 파일 그리고 library처럼 쓸 수 있는 컴포넌트들로 구성된다.

cd apps
mkdir admin
mkdir client

cd packages
mkdir components
mkdir tsconfig

4. next 및 typescript 환경 구성하기

apps에 사용할 프레임워크로 next.js를 택했기 때문에 next.js에 사용할 tsconfig에 대한 환경을 구성해준다.

cd tsconfig
vi base.json

{
  "display": "Default",
  "compilerOptions": {
    "composite": false,
    "declaration": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "inlineSources": false,
    "isolatedModules": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "preserveWatchOutput": true,
    "skipLibCheck": true,
    "strict": true
  },
  "exclude": ["node_modules"]
}
 

 

vi nextjs.json

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "display": "Next.js",
  "extends": "./base.json",
  "compilerOptions": {
    "target": "es5", 
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "incremental": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "include": ["src", "next-env.d.ts"],
  "exclude": ["node_modules"]
}

 

vi react-library.json

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "display": "React Library",
  "extends": "./base.json",
  "compilerOptions": {
    "lib": ["ES2015"],
    "module": "ESNext",
    "target": "ES6",
    "jsx": "react-jsx"
  }
}

 

 

vi package.json

{
  "name": "tsconfig",
  "version": "0.0.0",
  "private": true,
  "main": "index.js",
  "files": [
    "base.json",
    "nextjs.json",
    "react-library.json"
  ]
}

package.json 파일에서 가장 중요한 부분은 private: true 이다.
이 한줄이 추가돼야 turbo레포가 인식하고 폴더간 링크를 해주는거 같다.

 

tsconfig의 설정이 끝났다.

이제 이 설정한 파일을 다른 쪽에서 사용할 수 있다.

 

5. components 구성하기

cd components
vi package.json

{
  "name": "components",
  "version": "0.0.0",
  "main": "./index.tsx",
  "types": "./index.tsx",
  "license": "MIT",
  "devDependencies": {
    "@types/react": "^17.0.37",
    "@types/react-dom": "^17.0.11",
    "tsconfig": "*",
    "typescript": "^4.5.2"
  }
}

react를 사용하기 위해 react에 대한 작업과 앞서 만들었던 tsconfig를 불러온다.

가볍게 샘플용 버튼을 하나 만든다.
mkdir Button
vi Button.tsx

export const Button = () => {
  return (
    <button> Button </button>
  )
}

 

components 루트 페이지로 돌아와서 index.ts 파일을 만든다.

vi index.ts

export * from "./Button/Button";

 

6. 페이지 구축하기

위에서 만들었던 apps안에 admin과 client 페이지를 구축하기 위한 작업을 진행한다.

next.js 기반으로 만들 예정이다.

next.js가 아니더라도 각각 프로젝트별로 원하는 구성으롤 만들 수 있다. (ex, admin은 vue.js, client는 svelt.js 등)

 

cd client
yarn init

vi package.json

{
  "name": "client",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
  },
  "dependencies": {
    "next": "latest",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "components": "*"
  },
  "devDependencies": {
    "@types/react": "^17.0.37",
    "@types/react-dom": "^17.0.11",
    "config": "*",
    "next-transpile-modules": "^9.0.0",
    "tsconfig": "*",
    "typescript": "^4.5.3"
  }
}

 

vi next.config.js

const withTM = require("next-transpile-modules")(["components"]);
module.exports = withTM({
  reactStrictMode: true,
});

 

vi tsconfig.json

{
  "extends": "tsconfig/nextjs.json",
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

 

mkdir pages
cd pages
vi index.tsx

import { Button } from "components";

export default function Main() {
  return (
    <div>
      <h1>Client Main</h1>
      <Button />
    </div>
  );
}

 

기본적인 client 파일 구성이 완료됐다.

admin도 위와 같이 만든다.

cd admin
yarn init

vi package.json

{
  "name": "admin",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "dev": "next dev --port 3001",   
    "build": "next build",
    "start": "next start",
  },
  "dependencies": {
    "next": "latest",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "components": "*"
  },
  "devDependencies": {
    "@types/react": "^17.0.37",
    "@types/react-dom": "^17.0.11",
    "config": "*",
    "next-transpile-modules": "^9.0.0",
    "tsconfig": "*",
    "typescript": "^4.5.3"
  }
}

여기서 한 가지 client와 다른점은, 두 프로젝트를 동시에 실행시키기 위해 next의 port를 다르게 지정한다는 점이다.

vi next.config.js

const withTM = require("next-transpile-modules")(["components"]);
module.exports = withTM({
  reactStrictMode: true,
});

 

vi tsconfig.json

{
  "extends": "tsconfig/nextjs.json",
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

 

mkdir pages
cd pages
vi index.tsx

import { Button } from "components";

export default function Main() {
  return (
    <div>
      <h1>Admin Main</h1>
      <Button />
    </div>
  );
}

페이지 구분을 위해 h1태그 안에 값을 admin이라고 바꿔주자

 

이제 마지막으로 turbo로 연결하는 작업만이 남았다.

아마 IDE에서는 모든 페이지에 빨간 줄이 좍좍 그어져 있을 거다.

각각 프로젝트 환경을 구성해 가며 yarn install을 해가며 진행해도 되지만, 전체적인 컨셉을 잡아주기 위해 일부러 진행하지 않았다.

 

프로젝트의 루트 폴더로 돌아온다.

package.json파일을 연다.

vi package.json

{
  "name": "turbo-repo-test",
  "version": "1.0.0",
  "main": "index.js",
  "repository": "https://github.com/KimHunJin/turbo-repo-test.git",
  "author": "mos",
  "license": "MIT", 
  "private": true,
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev --no-cache --parallel --continue"
  },
  "baseBranch": "origin/main",
  "workspaces": [
    "packages/*",
    "apps/*"
  ],
  "devDependencies": {
    "turbo": "^1.0.23"
  },
  "dependencies": {
    "typescript": "^4.5.2"
  },
  "turbo": {
    "pipeline": {
      "build": {
        "outputs": [
          "dist/**",
          ".next/**",
          "public/dist/**"
        ],
        "dependsOn": [
          "^build"
        ]
      },
      "test": {
        "outputs": [
          "coverage/**"
        ],
        "dependsOn": []
      },
      "lint": {
        "outputs": []
      },
      "dev": {
        "cache": false
      },
      "clean": {
        "cache": false
      }
    }
  }
}
yarn install
yarn run build
yarn run dev
 
세가지 작업이 모두 정상적으로 이뤄지면 된다.
localhost:3000 으로 들어가면 client 페이지가,
localhost:3001 으로 들어가면 admin 페이지가 실행됨을 확인할 수 있다.

 

전체 프로젝트 구성은

https://github.com/KimHunJin/turbo-repo-test

 

GitHub - KimHunJin/turbo-repo-test

Contribute to KimHunJin/turbo-repo-test development by creating an account on GitHub.

github.com

위 폴더를 참고하면 된다.

 

추가로 다른 프로젝트를 구성하고 싶다면,

turbo-repo의 공식 깃헙을 확인하자

https://github.com/vercel/turborepo/tree/main/examples

 

GitHub - vercel/turborepo: The High-performance Build System for JavaScript & TypeScript Codebases

The High-performance Build System for JavaScript & TypeScript Codebases - GitHub - vercel/turborepo: The High-performance Build System for JavaScript & TypeScript Codebases

github.com

 

아직 시도해볼게 많지만 확실히 모노레포 환경을 구성하고, 빌드하는데 드는 시간이 줄어든거 같다.

좀 더 테스트를 해 보면 생각보다 유용하게 사용해볼 수 있을거 같다.

(피드백 환영합니다.)