모노레포 프로젝트 세팅 (Yarn + Vite + React + TypeScript)

Lemon·2024년 11월 13일

Monorepo

목록 보기
1/2
post-thumbnail
패키지 매니저yarn
프레임워크react+typescript
모노레포 도구yarn workspace
번들러vite
테스트jest
디자인시스템storybook

🍋디렉토리 구조 소개

monorepo-test / # 프로젝트별 앱 폴더 
├── apps/
│   ├── normal/
│   └── group/
├── packages/ # 공통 요소 관리
│   ├── common/
│   │   └── img/
│   ├── ui/
│   └── utils/
│       └── test/
├── package.json
├── yarn.lock
└── .yarnrc.yml

🍋모노레포 프로젝트 생성

최상위 디렉토리 생성

mkdir monorepo-test 
cd monorepo-test

디렉토리 구조 생성

mkdir -p apps/normal apps/group
mkdir -p packages/common/img packages/ui packages/utils/test

🍋apps 폴더 - 프로젝트 세팅

Vite + React + TypeScript

apps 폴더 안에 있는 두개의 프로젝트(normal, group)에 Vite + React + TypeScript 설치

cd apps/normal
yarn create vite ./
yarn
yarn dev

cd ../group
yarn create vite ./
yarn
yarn dev

yarn create vite ./ : 현재 파일에 vite 프로젝트 설치

개발환경 선택창 Select a framework > React, Select a variant > TypeScript 선택

🍋root/.gitignore파일 작성

git push를 하려면 미리 작성해둬야 git에 필요없는 파일이 올라가지 않는다.

# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
vscode/
node_modules/
.pnp
.pnp.js

# testing
coverage/

# production
build/
dist/

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Storybook
*storybook.log

🍋root/package.json파일 작성

workspace 설정에서 가장 핵심되는 파일이다.
root/package.json에 공통으로 공유할 패키지에 대한 정보와 workspace에 대한 설정을 한다.

{
  "name": "monorepo-test",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "normal": "yarn workspace normal",    
    "group": "yarn workspace group"
  }
}

📌private: true *필수 입력
배포 방지
모노레포는 여러 패키지를 포함하고 있기 때문에, 모든 패키지가 외부에 배포될 필요가 없을 때 private: true가 유용합니다.

📌workspaces *필수 입력
모노레포로 관리할 프로젝트 명을 입력한다.

"workspaces": [
  "apps/*",
  "packages/*"
],

📌script *필수 입력
root package.json에 스크립트를 설정해주지 않으면 프로젝트를 실행할 때마다 매번 cd ../ 으로 폴더를 옮겨다녀야한다.
(이 설정을 몰랐을 때 내가 그렇게 작업했다.)

문법

"scripts": {
  "사용할 이름": "yarn workspace {개별 프로젝트의 package.json 파일의 name 프로퍼티}",
},

실제 적용 예시

  "scripts": {
    "normal": "yarn workspace @doc/normal",
    "group": "yarn workspace @doc/group",
  },

적용 후 사용 예시

yarn normal start
yarn normal add {package}
yarn normal remove {package}
yarn normal build
yarn normal dev

yarn group start
yarn group lint
yarn group add {package}
yarn group remove {package}

이렇게 각 프로젝트의 package.json의 script를 root 경로에서 경로 이동 없이 바로 사용할 수 있다.

scripts의 key 값인 normal과 group을 그때 사용하는 용도이다.

📌dependencies 필수 아님

중복되는 패키지가 있다면 프로젝트 패키지의 중복을 막기 위해 root에 분리한다.

🍋TypeScript 전역 설치

typescipt의 tsconfig.json을 공유하기 위해 설치했다.

yarn add -D typescript -W
  • -W 명령어 : 모노레포의 모든 워크스페이스에 대해 실행된다.

🍋root/tsconfig.json 파일 작성

yarn workspace에서는 tsconfig.json 작성 방식중 2가지를 선택할 수 있다.

  1. 멀티레포 구조처럼 각 패키지에 tsconfig.json을 설정하는 방법
  2. 공통 설정사항을 root tsconfig에 두고, 개별 설정사항을 각 프로젝트의 tsconfig에 두는 방법

2번은 1번보다 설정이 복잡하지만 공통되는 설정을 root에서 관리할 수 있다는 장점이 있어서 2번 설정으로 선택했다.

{
  "compilerOptions": {
    "removeComments": true,
    "strict": true,
    "allowJs": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
	  "composite": true,
  },
  "include": ["apps/**/*", "packages/**/*"],
  "references": [
    {
      "path": "./apps/group"
    },
    {
      "path": "./apps/normal"
    }
  ],
  "exclude": [
    "node_modules",
    "**/build",
    "**/dist",
    "**/__tests__",
    "**/*.test.ts",
    "**/*.test.tsx",
    "**/*.spec.ts",
    "**/*.spec.tsx",
    "**/*.stories.tsx",
    "**/.storybook",
    "coverage",
    "storybook-static",
    "public",
    "**/eslint.config.js",
    "**/jest.config.js"
  ]
}

📌compilerOptions

두 프로젝트가 공통으로 사용하는 compilerOptions(TypeScript 컴파일러가 사용하는 다양한 설정)을 설정한다.

  • removeComments: true
    • 컴파일된 JavaScript 파일에서 주석을 제거한다. 최종 JavaScript 파일이 더 작아지고, 불필요한 주석을 포함하지 않게 된다.
  • strict: true
    • TypeScript의 엄격한 타입 검사 규칙을 활성화하는 설정이다. 이는 여러 개의 엄격한 타입 체크 옵션(noImplicitAny, strictNullChecks, strictFunctionTypes 등)을 모두 포함한 설정입니다. 이를 통해 코드의 타입 안전성을 최대한으로 보장한다.
      • noImplicitAny: true
        • any 타입 사용 금지한다.
        • 암시적인 any 타입을 허용하지 않도록 설정한다. 타입이 명시되지 않으면 any로 간주되는데, 이를 방지하여 모든 변수와 매개변수에 명시적으로 타입을 지정하도록 강제한다.
      • strictNullChecks: true
        • nullundefined를 엄격히 구분한다. 즉, null이나 undefined가 될 수 있는 값에 대해 타입이 명시되지 않으면 컴파일 오류가 발생한다. 이를 통해 런타임 오류를 줄일 수 있는 강력한 타입 안전성을 제공한다.
  • allowJs: true
    • JavaScript 파일(.js)도 TypeScript 프로젝트 내에서 허용되도록 설정한다. 이를 통해 JavaScript와 TypeScript가 혼합된 프로젝트에서도 TypeScript가 문제없이 작동한다.
  • resolveJsonModule: true
    • TypeScript에서 JSON 파일을 모듈로 불러올 수 있도록 허용한다. 이를 통해 JSON 파일을 import하여 사용할 수 있다.
  • noFallthroughCasesInSwitch: true
    • switch 문에서 case 구문이 다른 case로 이어지는 것(fallthrough)을 방지합니다. 의도적으로 break가 생략된 경우라도 오류를 발생시킵니다.
  • "forceConsistentCasingInFileNames": true
    • 파일 이름의 대소문자 일관성을 강제한다. 즉, 대소문자를 구분하여 파일 이름을 다르게 참조하는 것을 방지한다.

      import { myComponent } from './MyComponent'
      import { myComponent } from './mycomponent'
      동일하게 취급하지 않습니다.
  • composite: true
    • composite: true는 TypeScript 프로젝트 간 참조를 가능
    • 루트 tsconfig.json에 명시적인 composite 설정
      루트 tsconfig.json 파일에서 하위 프로젝트에 대한 references를 설정할 때, TypeScript는 모든 참조된 프로젝트composite 모드인지 확인합니다. 따라서 루트 tsconfig.json이 하위 프로젝트들을 참조하고 있다면, 루트 파일도 composite 모드를 설정해 빌드 의존성을 명확히 해야 합니다.
    • 의존성 관리
      composite 모드를 활성화하면 TypeScript는 각 하위 프로젝트의 빌드 결과를 .tsbuildinfo 파일로 저장하고, 이를 바탕으로 변경된 파일만 다시 빌드하여 효율성을 높입니다. 루트 파일에서 하위 프로젝트들을 제대로 인식하기 위해 루트에서도 composite을 활성화해야 합니다.

📌references

  • root의 tsconfig에게 개별 tsconfig 파일이 위치한 경로를 알려주는 역할이다. 이것이 필요한 이유는 typescript 컴파일러가 코드를 컴파일 할 때, 개별 tsconfig 파일의 내용도 알아야 하기 때문이다. (references로 root를 연결가능하도록 참조할 폴더 경로를 적으면 거기서 extends로 root tsconfig.json 경로를 적어서 연결할 수 있다.)

📌exclude

  • 여기에 설정한 값은 타입스크립트 컴파일러가 컴파일 하지 않는다. 빌드, test, storybook, node_modules 등 타입스크립트로 컴파일이 필요하지않은 파일들을 넣는다.
    "node_modules",      // 패키지 매니저가 설치한 외부 모듈
    "**/build",          // 빌드된 파일 폴더 (예: Webpack, Vite 빌드 파일)
    "**/dist",           // 배포용 빌드 결과물 폴더
    "**/__tests__",      // 테스트 코드 폴더
    "**/*.test.ts",      // 테스트 파일 (확장자는 프로젝트에 맞게 조정 가능)
    "**/*.test.tsx",
    "**/*.spec.ts",      // 테스트 스펙 파일
    "**/*.spec.tsx",
    "**/*.stories.tsx",  // 스토리북 파일
    "**/.storybook",     // 스토리북 설정 파일
    "coverage",          // 테스트 커버리지 폴더
    "storybook-static",  // 스토리북 빌드 파일
    "public",
    "**/eslint.config.js"

위 내용을 제외하고 react에 종속된 설정은 개별 tscofig에 둔다.

🍋apps/프로젝트/tsconfig.json 파일 작성

{
  "extends": "../../tsconfig.json", // 추가
  "references": [
    { "path": "./tsconfig.app.json" },
    { "path": "./tsconfig.node.json" }
  ]
}
  • references : tsconfig.json 파일이 tsconfig.node.json과 tsconfig.app.json을 참조하고 있음을 나타낸다.

root에 tsconfig 파일로 compoilerOption을 분리했을 경우 프로젝트 레벨에서는 extends 프로퍼티의 값으로 root tsconfig 파일의 경로를 반드시 입력해서 연결해야한다.

vite 프로젝트일 경우 tsconfig.app.json 과 tsconfig.node.json에서 에러발생

  1. eslint.config.js 파일은 입력 파일을 덮어쓰므로 쓸 수 없습니다.

이 문제는 tsconfig.json 파일이 eslint.config.js와 같은 JavaScript 파일을 빌드 대상으로 인식하고, 그로 인해 TypeScript가 이 파일을 처리하려다 발생하는 충돌이다. tsconfig.jsonexclude 섹션에 eslint.config.js 파일을 추가하여 TypeScript가 이 파일을 컴파일하려고 시도하지 않게 되며, 무시하도록 설정하면 관련된 에러가 해결된다.

해결 : root/tsconfig.json 수정

{
  "compilerOptions": {
    "removeComments": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strict": true,
    "allowJs": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["apps/**/*", "packages/**/*"],
  "references": [
    {
      "path": "./apps/group-version"
    },
    {
      "path": "./apps/normal-version"
    }
  ],
  "exclude": [
    "node_modules",
    "**/build",
    "**/dist",
    "**/__tests__",
    "**/*.test.ts",
    "**/*.test.tsx",
    "**/*.spec.ts",
    "**/*.spec.tsx",
    "**/*.stories.tsx",
    "**/.storybook",
    "coverage",
    "storybook-static",
    "public",
    **"**/eslint.config.js" // 추가**
  ]
}
  1. 참조되는 프로젝트 tsconfig.app.json'에는 \"composite\": true 설정이 있어야 합니다.

composite: true는 TypeScript 프로젝트 간 참조를 가능하게 하며, 이때 declaration: true를 통해 타입 선언 파일(.d.ts)을 생성해 타입 정보를 공유한다. noEmit: true는 결과물 생성 없이 타입 체크만 진행하며, allowImportingTsExtensions: true는 확장자를 명시적으로 사용할 수 있게 하는데, noEmit 옵션이 켜져 있을 때만 유효하다. noEmit을 제거하고 확장자 설정을 삭제하면 정상적으로 동작하게 된다.

해결 : 📁apps/normal, 📁apps/group tsconfig.app.jsontsconfig.node.json 수정

  • composite: true 추가
  • "declaration": true 추가
  • noEmit: true 제거
  • allowImportingTsExtensions: true 제거
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "Bundler",
    // "allowImportingTsExtensions": true, // 제거 
    "isolatedModules": true,
    "moduleDetection": "force",
    // "noEmit": true, // 제거
    "jsx": "react-jsx",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedSideEffectImports": true,

    "composite": true, // 추가
    "declaration": true // 추가
  },
  "include": ["src"]
}

📌composite: true 추가
tsconfig.json에서 composite: true를 추가함으로써 TypeScript 프로젝트가 "프로젝트 참조(Project References)"를 사용할 수 있다. 이는 프로젝트의 일부가 다른 프로젝트에 의존할 때 사용된다. composite 옵션이 활성화되면 TypeScript는 declaration 파일을 생성하고, 프로젝트가 다른 프로젝트에 참조될 수 있도록 준비한다. 이 옵션이 없으면 references를 사용한 프로젝트 참조가 제대로 동작하지 않아 에러가 발생할 수 있습니다.

📌"declaration": true 추가
TypeScript에서 타입 선언 파일(.d.ts)을 생성하기 위해 사용된다. 이 옵션이 활성화되면 TypeScript는 컴파일 과정에서 .ts 또는 .tsx 파일을 변환할 때, 해당 파일의 타입 정보를 포함하는 .d.ts 파일을 함께 생성한다.

프로젝트 참조와 composite: composite 옵션을 사용할 때 declaration: true가 필수적이다. 이는 프로젝트가 서로 참조할 때 타입 정보를 기반으로 올바른 종속성을 유지할 수 있게 해준다. 따라서, "declaration": true는 프로젝트 간 타입 정의를 공유하거나, 외부 프로젝트에서 올바르게 타입을 참조할 수 있도록 하기 위한 중요한 설정이다.

📌noEmit: true 제거

  • 역할: noEmit: true는 TypeScript가 코드를 컴파일해 JS 파일을 생성하지 않고, 타입 검사만 수행하도록 한다.
  • 문제점: composite 프로젝트(다중 모듈 간 참조를 사용하는 설정)에서는 코드의 타입 선언 파일(.d.ts)을 생성해야 하는데, noEmit: true가 있으면 이 선언 파일도 생성되지 않아서 composite 설정이 정상적으로 작동하지 않는다.
  • 해결 방법: noEmit: true 옵션을 제거해, TypeScript가 타입 선언 파일(declaration 파일 등)을 생성하도록 한다.

📌allowImportingTsExtensions: true 삭제

  • 역할: allowImportingTsExtensions: true는 TypeScript가 .ts.tsx 파일을 명시적으로 확장자까지 써서 가져와야 하도록 요구하는 설정이다.
  • 문제점: TypeScript는 원래 확장자를 생략해도 .ts, .tsx 등을 자동으로 인식해서 처리할 수 있기 때문에, 굳이 확장자를 쓰지 않아도 충분히 파일을 인식한다.
  • 해결 방법: allowImportingTsExtensions: true를 삭제하고, TypeScript가 기본 방식대로 확장자를 생략해도 파일을 인식하도록 한다.
  • 제약 이유: 컴파일을 하지 않고 타입 체크만 하는 경우, 명시적인 확장자 사용이 허용되지만, 실제 빌드 시에는 확장자 없이 파일을 가져오는 것이 더 일반적인 관행이기 때문에, 빌드 과정에서 확장자 명시는 권장되지 않는다.

즉, 컴파일 결과물을 만들지 않는 noEmit: true일 때만 allowImportingTsExtensions: true를 허용하여, 타입 체크 과정에서 확장자 명시를 요구할 수 있다.

noEmit: trueallowImportingTsExtensions: true의 관계

  • noEmit: true로 타입 검사만 수행할 때는 확장자를 명시하는 설정을 추가해도 괜찮지만, 실제로 빌드를 할 때는 확장자를 생략하는 방식이 더 일반적이다.

🍋packages/공통요소/package.json 설정

packages에는 공통적으로 사용할 요소(Common, UI, Utils 등)를 모아서 관리한다. 후에 packages의 각 패키지를 설치해서 apps 내의 프로젝트에서 사용할 것이다.

의존성 설정

common, utils, ui패키지를 사용하기 위해서 각 폴더의 package.json에서 의존성을 설정한다.
각 폴더는 apps 프로젝트에서 사용할 패키지가 된다.

각 폴더(패키지)의 package.json 파일 생성 후 설정

// packages/common/package.json
{
  "name": "@test/common",
  "version": "1.0.0",
  "main": "index.ts",
}

// packages/ui/package.json
{
  "name": "@test/ui",
  "version": "1.0.0",
  "main": "index.ts",
}

// packages/utils/package.json
{
  "name": "@test/utils",
  "version": "1.0.0",
  "main": "index.ts",
}

📌 "name"

  • 패키지의 고유한 이름을 정의한다.
    • 설치할 때 패키지 이름이 될 부분이다.

💡심볼릭 링크(Symbolic Link)
파일이나 폴더의 별명을 만들어 둔다고 생각하면 된다. 모노레포에서는 각 패키지를 설치할 때 실제 파일 복사본이 아닌 별명(=심볼릭 링크)으로 연결된다. 이렇게 하면 중복 파일이 생기지 않고, 모노레포 내에서 패키지들이 서로를 쉽게 참조할 수 있다.
예를 들어서 packages/common 에 @test/common 이라는 이름을 붙이면, 모노레포 구조 내 다른 프로젝트에서도 @test/common이라는 별명으로 이 패키지를 쉽게 쓸 수 있다.

yarn workspace normal add @test/common

💡@(스코프)
패키지 이름에 @test/common 처럼 스코프를 붙이면, 패키지들을 그룹으로 묶을 수 있다.

yarn workspace normal add @test/common

패키지를 설치하면 node_modules에 심볼릭 링크가 생성된다.

스코프 설정을 통해 node_modules에 생성되는 심볼릭 링크 폴더 구조가 스코프별로 그룹화된다.

📌 "main"
패키지의 진입점을 지정하는데 사용된다. 이 필드를 통해 패키지를 사용할 때 가장 먼저 불러올 파일을 정의한다. main 필드를 설정하지 않으면 기본적으로 index.js 파일을 찾는다. 하지만 index.js 파일이 없으면 도구가 여러 파일을 검색하게 되어 비효율적이기 때문에 필수는 아니지만 설정하는 것을 권장한다. main 필드를 명시해 두면, 패키지를 사용하는 도구나 개발자가 특정 진입 파일을 빠르게 찾을 수 있어 성능과 유지 보수성 측면에서 유리하다. 특히 모노레포 외부 환경이나 타 도구와의 호환성도 높아진다.

💡애플리케이션 vs 라이브러리

애플리케이션 (apps 폴더)
해당 위치의 폴더는 실제로 실행 가능한 애플리케이션을 포함한다.
각 애플리케이션은 독립적으로 실행되며, 다른 애플리케이션에서 직접적으로 import될 필요가 없다.
따라서 애플리케이션은 자체적인 실행 환경을 갖추고 있기 때문에 main 필드를 설정할 필요가 없다. 대신 애플리케이션의 구조와 실행 파일을 통해 애플리케이션의 진입점이 결정된다.

라이브러리 (packages 폴더)
반면, packages 폴더에 위치한 패키지들은 재사용 가능한 컴포넌트, 유틸리티 등을 제공한다. 이러한 패키지들은 다른 애플리케이션이나 패키지에서 재사용될 수 있다. 라이브러리 패키지에는 main 필드를 설정하여 명확한 진입점을 지정하는 것이 중요하다. 이렇게하면 해당 패키지를 사용할 때 어떤 파일이 가장 먼저 로드될지를 정의할 수 있다.

예시
index.ts를 각 패키지의 진입파일로 지정

// packages/common/index.ts
export { default as bgImg } from './img/bgimg.jpg';

불러오는 방식
1. 패키지에서 내보낸 값 가져오기

import { imgPath } from '@doc_packages/common';
  1. 직접 이미지 파일 가져오기
import bgimg from '@doc_packages/common/img/bgimg.jpg';

🍋활용

root/package.json script 필드 추가

{
  "name": "monorepo-test",
  "private": true,
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "normal": "yarn workspace normal",
    "group": "yarn workspace group",
    "common": "yarn workspace @test/common", // 추가
    "ui": "yarn workspace @test/ui", // 추가 
    "utils": "yarn workspace @test/utils" // 추가 
  },
    "devDependencies": {
    "typescript": "^5.6.3"
  }
}

apps/프로젝트 에서 packages/common/img 에 있는 이미지를 사용하고 싶을 경우

pakages/common/imgbgImg.jpg 이미지를 하나 넣고, apps/normal 프로젝트에서 사용해보자

1. 패키지 설치
apps/normal 에 packages 내에서 사용하고 싶은 패키지를 설치한다.

yarn normal add @test/common@1.0.0

💡스코프 패키지("name": "@doc_packages/common") 설치 시 주의할 점

yarn berry 작업 시에는 패키지가 정상적으로 설치되었지만,
yarn classic은 패키지 설치가 동작하지 않았다.

Yarn Berry (Yarn 2 이상)
PnP(Plug'n'Play) 모드를 지원하여 패키지를 노드 모듈 폴더가 아닌 Yarn의 PnP 캐시 디렉토리에서 관리한다. 이로 인해, 스코프 패키지(예: @packages/common)가 로컬 패키지로 인식되고, 종속성이 효과적으로 해결된다. 기본적으로 Yarn Berry는 모든 스코프 패키지를 자동으로 링크하여 사용할 수 있도록 처리한다.

Yarn Classic
노드 모듈 폴더를 사용하여 종속성을 설치하며, 스코프 패키지를 처리할 때 해당 패키지가 실제로 공용 레지스트리(npm, yarnpkg)에 존재하는지 확인한다. 이로 인해 로컬에 존재하는 스코프 패키지를 찾지 못하고, 오류가 발생할 수 있다.

해결 방법

https://github.com/yarnpkg/yarn/issues/4878

Yarn은 버전이 없을 때 항상 레지스트리에서 해결하려고 시도하기 때문에 설치 시 패키지 뒤에 버전을 입력하면 정상 동작한다.

설치 후 apps/normal package.json에서 설치된 걸 확인할 수 있다.

2. apps/normal에서 import 후 사용

import "./App.css";
import bgimg from "@test/common/img/bgimg.jpg";

function App() {

  return (
    <>
      <img src={bgimg} alt="background image" />
    </>
  );
}

export default App;

개발모드를 실행하서 적용을 확인한다.

yarn normal dev

🍋별칭 설정

모노레포 프로젝트에서 여러 앱이 공용으로 사용하는 packages/utils 에 있는 함수를 별칭으로 사용하는 설정을 단계별로 구성한다. 이 과정에서 tsconfig.jsonpaths 별칭 설정과 Vite의 vite-tsconfig-paths 플러그인을 사용하여 절대 경로로 utils 모듈을 불러올 수 있다.

1. 함수 파일 생성

packages/utils 폴더에 함수 파일을 생성한다.

// packages/utils/sumArray.ts
export function sumArray(numbers: number[]): number {
    return numbers.reduce((acc, num) => acc + num, 0);
}

2. 절대 경로 별칭 설정

root/tsconfig.json 파일에서 baseUrlpaths 설정을 추가한다.
@utils 별칭은 packages/utils 디렉터리를 참조한다.

{
    "compilerOptions": {
        "removeComments": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "strict": true,
        "allowJs": true,
        "resolveJsonModule": true,
        "forceConsistentCasingInFileNames": true,
        **"composite": true,**
        **"baseUrl": ".", // 추가
        "paths": {
            "@utils/*": ["packages/utils/*"] // 추가
        }**
    },
    "include": ["apps/**/*", "packages/**/*"],
    "references": [
        {
            "path": "./apps/group"
        },
        {
            "path": "./apps/normal"
        },
        **{
            "path": "./packages/utils"
        }**
    ],
    "exclude": [
        "node_modules",
        "**/build",
        "**/dist",
        "**/__tests__",
        "**/*.test.ts",
        "**/*.test.tsx",
        "**/*.spec.ts",
        "**/*.spec.tsx",
        "**/*.stories.tsx",
        "**/.storybook",
        "coverage",
        "storybook-static",
        "public",
        "**/eslint.config.js"
    ]
}

compilerOptions에 baseUrl, paths 설정
📌baseUrl

  • 루트 폴더(.)를 기준으로 별칭 경로를 시작하도록 설정한다.
  • baseUrl을 설정한 곳이 기준점이된다.

📌paths

  • "@utils/*": ["packages/utils/*"]를 통해 @utils 별칭으로 packages/utils 경로를 참조한다.
  • baseUrl가 시작 기준이 되기 때문에 그 이후 경로부터 적어주면 된다.
    "paths": {"@utils/*": ["./packages/utils/*"]}
    "paths": {"@utils/*": ["packages/utils/*"]}

3. root/tsconfg.json 상속 설정

packages/utils에서 모노레포 루트의 tsconfig.json 설정을 상속받기 위해 extends를 사용한다.

// packages/utils/tsconfig.json
{
    "extends": "../../tsconfig.json",
    "compilerOptions": {
        "composite": true
    }
}

extends 에는 root/tsconfig.json의 경로 입력한다.
utils/logSum.ts 함수 생성 후 처음 만든 함수 sumArray를 별칭으로 불러온다.

// packages/utils/logSum.ts
import { sumArray } from "@utils/sumArray";

export function logSum(numbers: number[]): void {
    const result = sumArray(numbers);
    alert(`배열 [${numbers.join(", ")}]의 합은 ${result}입니다.`);
}

4. apps/프로젝트에서 utils 패키지 사용하기

4-1. 패키지를 설치한다.
apps/normal@test/utils 패키지를 설치한다.

yarn normal add @test/utils@1.0.0

4-2. vite-tsconfig-paths 플러그인을 설치한다.
Vite가 TypeScript paths 별칭을 인식하도록 apps/normalvite-tsconfig-paths을 설치한다.

yarn normal add vite-tsconfig-paths -D

4-3. vite.config.ts를 설정한다.
vite-tsconfig-paths 플러그인을 Vite 설정 파일에 추가한다.

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
**import tsconfigPaths from 'vite-tsconfig-paths'; // 추가**

export default defineConfig({
    plugins: [react(), **tsconfigPaths()**], **// 추가**
});

5. apps/normal에서 utils 함수 사용하기

App.tsx에서 logSum 함수 사용한다.

// apps/normal/src/App.tsx
import { logSum } from '@doc_packages/utils/logSum';
import bgimg from '@doc_packages/common/img/bgimg.jpg';

function App() {
    return (
        <>
            <img src={bgimg} alt="bgimg" onClick={() => logSum([1, 2, 3, 4, 5])} />
        </>
    );
}

export default App;

개발모드를 실행하서 적용을 확인한다.

yarn normal-version dev

profile
개미는 뚠뚠..오늘도 뚠뚠🐜

0개의 댓글