monorepo 세팅: yarn berry
2023.02.19
1. project 폴더 생성
mkdir yarn-berry-workspace
cd yarn-berry-workspace
2. yarn 버전 변경
yarn set version berry
yarn set version stable
3. yarn workspace 패키지 만들기
- packages 디렉토리 만들기
- 루트 초기화
yarn init -w
- 실행 결과
- 빈 package 파일
- package.json
package.json
{ "name": "yarn-berry-workspace", "packageManager": "yarn@3.5.0", "private": true, "workspaces": [ "packages/*" ] }
4. 프로젝트용 폴더 추가
-
루트에 apps폴더 추가
-
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
-
Usage Error: The nearest package directory ~~ 오류시 중간에 해결 방법이 있다.
-
해당 폴더 상위 폴더에 yarn.lock, package.json가 있다면 삭제
-
package.json에 해당 설치 경로를 추가 안했다면 추가 (root pacakge.json apps를 추가했다.)
-
yarn.lock 파일 생성
1번이랑 비슷한 오류인 것 같은데 상위 폴더에 yarn이나 pacakge파일이 있으면 해당 폴더가 이미 pacakge로 관리 되는듯하다.
그러므로 yarn.lock 파일을 생성하여 오류를 해결 할 수 있다.
-
6. 설치 세팅 및 실행 확인
client
→ @client/web
으로 변경
-
실행
yarn workspace @client/web run dev
(빌드)
yarn workspace @client/admin build
- 오류 해결
위의 실행 명령어할때 다음 오류
이때는 yarn install 해준 뒤 다시 하면 된다.
Internal Error: Assertion failed: Expected workspace @client/web (/Users/bm/Desktop/yarn-berry-workspace/apps/client/package.json) to have been resolved. Run "yarn install" to update the lockfile
- 오류 해결
위의 실행 명령어할때 다음 오류
6.1. typescript 문제 해결
- yarn berry는 모듈을 불러오는 방식이 달라서 다음과 같이 오류가 나온다.
- 해결하기
typescript를 따로 설치하고 vsCode를 사용하면 오른쪽에 문구가 나오는데 Allow를 하면 된다.
yarn add -D typescript yarn dlx @yarnpkg/sdks vscode
- vscode extension 설치 yarn berry의 yarn PnP를 사용을 위한 익스텐션을 설치한다. https://marketplace.visualstudio.com/items?itemName=arcanis.vscode-zipfs .vscode에 추가가 되어 있지 않다면 추가해준다.
7. pacakges 공통 패키지 만들기
cd packages
mkdir lib
cd lib
yarn init
7.1. 공통 모듈 세팅
해당 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
을 만들어서 다음을 추가
{
"$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를 사용하기
-
의존성 추가
yarn workspace @client/web add @client/lib
@client/web에 다음과 같이 workspace로 추가 되었다.
-
사용
-
packages/lib/src/index.ts
파일 추가packages/lib/src/index.tsexport const say = () => { return "hello @client/lib"; };
-
import 후 실행
yarn workspace @client/web run dev
-
결과 화면
-
8. tsconfig 설정 공유하기
-
base tsconfig
root에 기본 tsconfig 파일을 생성
tsconfig.base.json
tsconfig.base.json{ "compilerOptions": { "strict": true, "useUnknownInCatchVariables": true, "allowJs": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "incremental": true, "newLine": "lf" }, "exclude": ["**/node_modules", "**/.*/"] }
-
해당 파일을 기본으로 하는 tsconfig
apps/client/tsconfig.json{ "$schema": "https://json.schemastore.org/tsconfig", "extends": "../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./src", "target": "esnext", "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", "jsx": "preserve", "incremental": true }, "exclude": ["**/node_modules", "**/.*/"], "include": [ "next-env.d.ts", "**/*.ts", "**/*.tsx", "**/*.mts", "**/*.js", "**/*.cjs", "**/*.mjs", "**/*.jsx", "**/*.json" ] }
packages/lib/tsconfig.json{ "$schema": "https://json.schemastore.org/tsconfig", "extends": "../../tsconfig.base.json", "compilerOptions": { "module": "ESNext", "moduleResolution": "node", "target": "ESNext", "lib": ["ESNext", "dom"], "esModuleInterop": true, "allowSyntheticDefaultImports": true, "baseUrl": "./src", "noEmit": false, "incremental": true, "resolveJsonModule": true }, "exclude": ["**/node_modules", "**/.*/", "./dist", "./coverage"], "include": ["**/*.ts", "**/*.js", "**/.cjs", "**/*.mjs", "**/*.json"] }
9. prettier, eslint
-
모든 파일이 같은 컨벤션으로 가질 수 있게 설치
yarn add prettier eslint eslint-config-prettier eslint-plugin-import eslint-plugin-react eslint-plugin-react-hooks eslint-import-resolver-typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser -D yarn dlx @yarnpkg/sdks
-
필요 extension
esbenp.prettier-vscode
dbaeumer.vscode-eslint
-
.vscode/settings.json에 prettier 설정 추가
.vscode/settings.json"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.rulers": [ 90 ],
-
root에
.eslintrc.js
파일 추가apps/client에 있는 .eslintrc.json은 삭제해야한다.
module.exports = { root: true, env: { es6: true, node: true, browser: true, }, parser: "@typescript-eslint/parser", parserOptions: { ecmaFeatures: { jsx: true }, }, extends: [ "eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:react/recommended", "plugin:react-hooks/recommended", "prettier", ], plugins: ["@typescript-eslint", "import", "react", "react-hooks"], settings: { "import/resolver": { typescript: {} }, react: { version: "detect" }, }, rules: { "no-implicit-coercion": "error", "no-warning-comments": [ "warn", { terms: ["TODO", "FIXME", "XXX", "BUG"], location: "anywhere", }, ], curly: ["error", "all"], eqeqeq: ["error", "always", { null: "ignore" }], "@typescript-eslint/no-use-before-define": "off", "@typescript-eslint/no-empty-interface": "off", "@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/no-parameter-properties": "off", "@typescript-eslint/no-var-requires": "warn", "@typescript-eslint/no-non-null-asserted-optional-chain": "warn", "@typescript-eslint/no-inferrable-types": "warn", "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/naming-convention": [ "error", { format: ["camelCase", "UPPER_CASE", "PascalCase"], selector: "variable", leadingUnderscore: "allow", }, { format: ["camelCase", "PascalCase"], selector: "function" }, { format: ["PascalCase"], selector: "interface" }, { format: ["PascalCase"], selector: "typeAlias" }, ], "@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/array-type": ["error", { default: "array-simple" }], "@typescript-eslint/no-unused-vars": [ "error", { ignoreRestSiblings: true }, ], "@typescript-eslint/member-ordering": [ "error", { default: [ "public-static-field", "private-static-field", "public-instance-field", "private-instance-field", "public-constructor", "private-constructor", "public-instance-method", "private-instance-method", ], }, ], "import/order": [ "error", { groups: [ "builtin", "external", "internal", "parent", "sibling", "index", "object", ], alphabetize: { order: "asc", caseInsensitive: true }, }, ], "react/prop-types": "off", "react/display-name": "off", "react-hooks/exhaustive-deps": "error", "react/react-in-jsx-scope": "off", "react/no-unknown-property": ["error", { ignore: ["css"] }], }, };
-
.vscode/settings.json 에 eslint 설정 추가
.vscode/settings.json"eslint.packageManager": "yarn", "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
-
eslint 오류 확인 세팅이 재대로 됬다면 다음과 같은 오류가 나온다. 다음과 같이 import 순서를 수정하면 사라진다.
- 오류가 나오지 않는다면
command
+shift
+p
ESLint: Restart ESLint Server
로 eslint를 재실행하여 해결할 수 있다.
- 오류가 나오지 않는다면
10. react 라이브러리 패키지 만들기
-
packages/ui
에 package.json 생성 및 수정cd packages/ui yarn init
packages/ui/pacakge.json
수정packages/ui/pacakge.json{ "name": "@client/ui", "packageManager": "yarn@3.3.0" }
10.1. react 의존성 파일 설치
root경로에서 다음 install
yarn
yarn workspace @client/ui add typescript react react-dom @types/node @types/react @types/react-dom -D
- packages/ui/tsconfig.json
packages/ui/tsconfig.json
{ "$schema": "https://json.schemastore.org/tsconfig", "extends": "../../tsconfig.base.json", "compilerOptions": { "baseUrl": "./src", "target": "esnext", "lib": ["dom", "dom.iterable", "esnext"], "module": "esnext", "jsx": "react-jsx", "noEmit": false, "incremental": true }, "exclude": ["**/node_modules", "**/.*/", "dist", "build"] }
10.2. 공통 컴포넌트 생성 및 테스트
다음 경로에 파일 추가
-
packages/ui/src/Button.tsx
packages/ui/src/Button.tsximport React from "react"; export interface ButtonProps { children: React.ReactNode; } const Button: React.FC<ButtonProps> = ({ children }) => { return ( <button style={{ padding: "10px 30px", border: "1px solid #fefefe", background: "#333", }} > {children} </button> ); }; export default Button;
-
packages/ui/src/index.ts
packages/ui/src/index.tsexport { default as Button } from "./Button";
-
내보내기 설정
main 경로 설정
packages/ui/package.json{ "name": "@client/ui", "packageManager": "yarn@3.5.0", "main": "src/index.ts", "devDependencies": { "@types/node": "^18.15.3", "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "react": "^18.2.0", "react-dom": "^18.2.0", "typescript": "^5.0.2" } }
10.2.1. 공통 컴포넌트 실행
-
사용할 apps에 의존성 추가
yarn workspace @client/web add @client/ui
-
@client/web 에 추가한 뒤 실행
yarn workspace @client/web dev
-
결과
11. Typecheck
-
typescript package.json에 script추가
apps/client/package.json
packages/lib/package.json
packages/ui/package.json
"scripts": { "typecheck": "tsc --project ./tsconfig.json --noEmit" },
-
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 타입 체크
-
yarn에서 workspace를 관리하기 위한 플러그인 설치
→ https://yarnpkg.com/api/modules/plugin_workspace_tools.html
yarn plugin import workspace-tools
-
root pacakge.json에 추가
"scripts": { "g:typecheck": "yarn workspaces foreach -pv run typecheck" },
-
실행
yarn g:typecheck
각 파일별로 타입 체크를 수행한다.
아까와 같이
@client/web
에 오류가 나는 것을 볼 수 있다.
12. yarn berry storybook 세팅
-
해당 repo workspace로 이동
모두 설치할 레포에서 진행함 (ex: cd
pacakges/ui
)npx sb init --builder webpack5
-
node_modules 삭제
-
main.js 숫정
main.jsconst 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;
-
package 추가 설치
yarn add -D @babel/core babel-loader core-js webpack util @babel/preset-react @babel/preset-typescript @storybook/nextjs
-
babel.config.json 추가
babel.config.json{ "presets": [ ["@babel/react", { "runtime": "automatic" }], ["@babel/typescript", { "onlyRemoveTypeImports": true }] ] }