monorepo 세팅: yarn berry

1. project 폴더 생성

mkdir yarn-berry-workspace
cd yarn-berry-workspace

2. yarn 버전 변경

yarn set version berry
yarn set version stable

3. yarn workspace 패키지 만들기

  1. packages 디렉토리 만들기
  2. 루트 초기화
yarn init -w

4. 프로젝트용 폴더 추가

  1. 루트에 apps폴더 추가

  2. package.json 수정

    package.json
    {
        "name": "yarn-berry-workspace",
        "packageManager": "yarn@3.5.0",
        "private": true,
        "workspaces": [
            "apps/*",
            "packages/*"
        ]
    }
    

5. 프로젝트 추가

cd apps

yarn create next-app

6. 설치 세팅 및 실행 확인

client@client/web으로 변경

6.1. typescript 문제 해결

7. pacakges 공통 패키지 만들기

cd packages
mkdir lib
cd lib

yarn init

7.1. 공통 모듈 세팅

해당 pacakge.json을 다음과 같이 수정한다.

/packages/lib/pacakge.json
{
  "name": "@client/lib",
  "packageManager": "yarn@3.5.0",
  "version": "1.0.0",
  "private": true,
  "main": "./src/index.ts",
  "dependencies": {
    "typescript": "^5.0.2"
  }
}

packages/lib/tsconfig.json을 만들어서 다음을 추가

/packages/lib/tsconfig.json
{
    "$schema": "https://json.schemastore.org/tsconfig",
    "compilerOptions": {
        "strict": true,
        "useUnknownInCatchVariables": true,
        "allowJs": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "isolatedModules": true,
        "newLine": "lf",
        "module": "ESNext",
        "moduleResolution": "node",
        "target": "ESNext",
        "lib": ["ESNext", "dom"],
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "baseUrl": "./src",
        "noEmit": false,
        "incremental": true,
        "resolveJsonModule": true,
        "paths": {}
    },
    "exclude": ["**/node_modules", "**/.*/", "./dist", "./coverage"],
    "include": ["**/*.ts", "**/*.js", "**/.cjs", "**/*.mjs", "**/*.json"]
}

7.2. 공통 라이브러리 사용하기

apps/client에서 pacakge/lib를 사용하기

  1. 의존성 추가

    yarn workspace @client/web add @client/lib
    

    @client/web에 다음과 같이 workspace로 추가 되었다.

  2. 사용

    • packages/lib/src/index.ts 파일 추가

      packages/lib/src/index.ts
      export const say = () => {
        return "hello @client/lib";
      };
      
    • import 후 실행 yarn workspace @client/web run dev

    • 결과 화면

8. tsconfig 설정 공유하기

9. prettier, eslint

10. react 라이브러리 패키지 만들기

10.1. react 의존성 파일 설치

root경로에서 다음 install

yarn
yarn workspace @client/ui add typescript react react-dom @types/node @types/react @types/react-dom -D

10.2. 공통 컴포넌트 생성 및 테스트

다음 경로에 파일 추가

10.2.1. 공통 컴포넌트 실행

11. Typecheck

  1. typescript package.json에 script추가

    • apps/client/package.json
    • packages/lib/package.json
    • packages/ui/package.json
    "scripts": {
      "typecheck": "tsc --project ./tsconfig.json --noEmit"
    },
    
  2. Button 컴포넌트 수정

    import React from "react";
    
    export interface ButtonProps {
      children: React.ReactNode;
      variant: "inline" | "outline";
    }
    
    const Button: React.FC<ButtonProps> = (props) => {
      const { children, ...other } = props;
      return (
        <button
          style={{
            padding: "10px 30px",
            border: "1px solid #fefefe",
            background: "#333",
          }}
          {...other}
        >
          {children}
        </button>
      );
    };
    
    export default Button;
    

    다음과 같이 button 컴포넌트에 props를 추가한다.

    • 타입 검사

    yarn workspace @client/web typecheck 와 같이 타입 검사

    에러 체크 확인

11.1. root 타입 체크

  1. yarn에서 workspace를 관리하기 위한 플러그인 설치

    https://yarnpkg.com/api/modules/plugin_workspace_tools.html

    yarn plugin import workspace-tools
    
  2. root pacakge.json에 추가

    "scripts": {
      "g:typecheck": "yarn workspaces foreach -pv run typecheck"
    },
    
  3. 실행

    yarn g:typecheck

    각 파일별로 타입 체크를 수행한다.

    아까와 같이 @client/web에 오류가 나는 것을 볼 수 있다.

12. yarn berry storybook 세팅

  1. 해당 repo workspace로 이동

    모두 설치할 레포에서 진행함 (ex: cd pacakges/ui)

    npx sb init --builder webpack5
    
  2. node_modules 삭제

  3. main.js 숫정

    main.js
    const config = {
      stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
      addons: [
        "@storybook/addon-links",
        "@storybook/addon-essentials",
        "@storybook/addon-interactions",
      ],
      framework: {
        name: "@storybook/nextjs",
        options: {},
      },
      docs: {
        autodocs: "tag",
      },
      webpackFinal: async (config) => {
        config.module.rules.push({
          test: /\.(ts|tsx)$/,
          loader: require.resolve("babel-loader"),
        });
        return config;
      },
    };
    export default config;
    
  4. package 추가 설치

    yarn add -D @babel/core babel-loader core-js webpack util @babel/preset-react @babel/preset-typescript @storybook/nextjs
    
  5. babel.config.json 추가

    babel.config.json
    {
      "presets": [
        ["@babel/react", { "runtime": "automatic" }],
        ["@babel/typescript", { "onlyRemoveTypeImports": true }]
      ]
    }