$ git clone https://github.com/thepsyentist-public/learn-monorepo
$ yarn set version berry
$ yarn
$ yarn workspace @thepsyentist/client dev
learn-monorepo
ํ๋ก์ ํธ๋ฅผ ๊ตฌ์ฑํ๊ณ ์๋ ๋ด์ฉ์ผ๋ก ์์ฑํ์ต๋๋ค.
๋ชจ๋
ธ๋ ํฌ๋ฅผ ๊ตฌ์ฑํ๊ธฐ ์ํด์๋ Node.js
yarn
๋ฒ์ ์ค์ ์ด ํ์ํฉ๋๋ค.
https://yarnpkg.com/getting-started/install
Node.js ๋ฒ์ ๋งค๋์ง์ด ํ์ํ๋ค๋ฉด nvm ์ ์ค์นํด์ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.
https://jang8584.tistory.com/295
Node.js: 16.10.0 ๋ฒ์ ์ด์
yarn: berry
ํ๋ก์ ํธ ํด๋ ์์ฑ
: learn-monorepo ํด๋๋ฅผ ์์ฑํฉ๋๋ค.
$ mkdir learn-monorepo
$ cd learn-monorepo
yarn ๋ฒ์ ์ค์
: yarn berry ๋ฒ์ ์ผ๋ก ์ค์ ํฉ๋๋ค.
$ yarn set version berry
workspace ํจํค์ง ์ค์
: ๋ชจ๋
ธ๋ ํฌ ๊ตฌ์ฑ์ ์ํด yarn workspace ํ๋ก์ ํธ๋ก ์ค์ ํฉ๋๋ค.
- ์ถ๊ฐ์ ์ธ yarn berry cli๋ฅผ ํ์ธํด๋ณด์ธ์.
$ yarn init -w
{rootDirectory} package.json ํ์ผ ์์
: workspaces ์์ฑ์์ ๊ด๋ฆฌ๋ฅผ ํ workspace ํด๋ ๊ฒฝ๋ก๋ฅผ ๋ฑ๋กํฉ๋๋ค.
// package.json
{
"name": "learn-monorepo",
"packageManager": "[email protected]",
"private": true,
"workspaces": [
"apps/*", // ๐ ์ถ๊ฐ
"packages/*" // ๐ ์ถ๊ฐ
]
}
์์ ๊ฐ์ด ์ค์ ํ๋ฉด apps/*
packages/*
๋ด๋ถ ํด๋๋ ์ํฌ์คํ์ด์ค๋ก ํ์ฉ ๊ฐ๋ฅํฉ๋๋ค.
apps ํด๋์ Nextjs ํ๋ก์ ํธ ์ถ๊ฐ
: Nextjs CRA๋ฅผ ํตํด ํ๋ก์ ํธ๋ฅผ ์์ฑํฉ๋๋ค.
$ cd apps
$ yarn create next-app
client
ํ๋ก์ ํธ๋ฅผ ์์ฑํ์ต๋๋ค.
์ด ํ๋ก์ ํธ์ ๋ค๋ฅธ ํจํค์ง๋ค์ ์ถ๊ฐํ๋ ํ์์ผ๋ก ์งํํ๊ฒ ์ต๋๋ค.
workspace ์ด๋ฆ ์ค์
: ํ๋ก์ ํธ์ ์ํฌ์คํ์ด์ค ์ด๋ฆ์ ์ค์ ํฉ๋๋ค. apps/client/package.json ํ์ผ ๋ด name์ ๋ณ๊ฒฝํฉ๋๋ค.
workspace ์ ์ฉ
: ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ฉด .yarnrc.yml์ ํ๋ก์ ํธ์ ์ข
์์ฑ์ ์ธ์งํ๋๋ก ์ถ๊ฐํด์ผํฉ๋๋ค.
// {rootDirector} ๋ก ์ด๋
$ ..cd
$ yarn
yarn ๋ช
๋ น์ด๋ฅผ ํตํด์ workspaces์ ๋ฑ๋ก๋ ํ๋ก์ ํธ๋ค์ ์ธ๋ฑ์ฑํ์ฌ .yarnrc.yml์ ์ข
์์ฑ์ ์ถ๊ฐํฉ๋๋ค.
@thepsyentist/client ํ๋ก์ ํธ ์คํ
: ์ง๊ธ๊น์ง ์ค์ ํ ํ๋ก์ ํธ๋ฅผ ์คํ ํด๋ด
๋๋ค.
$ yarn workspace @thepsyentist/client dev
์ง๊ธ๊น์ง์ ์ค์ ์ผ๋ก๋ ./apps/client/pages/index.tsx
ํ์ผ์ ์ฝ๋๋ฅผ ํ์ธํ๋ฉด typescript error๊ฐ ๋ฐ์ํฉ๋๋ค.
์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ์ด์ ๋, yarn berry pnp๋ node_modules
๋ชจ๋์ ๋ถ๋ฌ์ค๋ ๋ฐฉ์์ด ๋ฌ๋ผ์ ๋ฐ์ํ๋ ๋ฌธ์ ์
๋๋ค.
typescript ์ค์น
: {rootDirectory}์ typescript ์์กด์ฑ์ ์ค์นํฉ๋๋ค.
$ yarn add -D typescript
// vscode์ ๊ฒฝ์ฐ๋ ์๋ cli๋ ์ค์นํฉ๋๋ค.
$ yarn dlx @yarnpkg/sdks vscode
Node.js ํจํค์ง ๋งค๋์ ์ค์
: ์น์คํฐ์ ๊ฒฝ์ฐ settings โ Nodejs ๊ฒ์ โ ํจํค์ง ๋งค๋์ ์์ Yarn berry๋ก ์ง์ ์ ์ค์ ํฉ๋๋ค.
vscode extension ์ค์น
: vscode IDE๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, arcanis.vscode-zipfs extends๋ฅผ ์ค์นํฉ๋๋ค.
// .vscode/extensions.json
{
"recommendations": ["arcanis.vscode-zipfs"]
}
.vscode/extensions.json ์ ๋ด์ฉ์ ์ถ๊ฐํ๋ฉด, ๋ค๋ฅธ ์ฌ์ฉ์๊ฐ ํ๋ก์ ํธ ์ด์ฉ์ extensions๋ฅผ ์ค์น๋ฅผ ์ถ์ฒํ๋๋ก ์ค์ ํ ์ ์์ต๋๋ค.
packages/lib
์ ๊ณตํต์ผ๋ก ์ฌ์ฉํ ํจํค์ง๋ฅผ ๋ง๋ค์ด์ ์ ์ฉํด๋ด
๋๋ค.
packages/lib ์์ฑ
: lib ํจํค์ง๋ฅผ ์์ฑํฉ๋๋ค.
$ cd packages/lib
$ yarn init
$ yarn add typescript // ํ์
์คํฌ๋ฆฝํธ ์์กด์ฑ ์ถ๊ฐ
workspace ์ด๋ฆ ์ค์
: packages/lib/package.json ๋ด name์ @thepsyentist/lib
๋ก ๋ณ๊ฒฝํฉ๋๋ค.
// packages/lib/package.json
{
"name": "@thepsyentist/lib",
"main": "./src/index.ts",
"packageManager": "[email protected]",
"dependencies": {
"typescript": "^5.1.3"
}
}
tsconfig.json ์ถ๊ฐ
: 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"]
}
๊ณต์ ํจ์ ์ถ๊ฐ
: packages/lib/src/index.js ํ์ผ ์์ฑ ํ, ์๋ ์ฝ๋ ์์ฑํฉ๋๋ค.
export const sayHello = () => {
return "Hello World";
};
์ํฌ์คํ์ด์ค ๋ฑ๋ก
: {rootDirectory}๋ก ์ด๋ํด์ ํ๋ก์ ํธ์ ์์กด์ฑ์ ์ถ๊ฐํฉ๋๋ค.
$ cd ..
$ cd ..
// ํ์ฌ ๊ฒฝ๋ก {rootDirectory}
$ yarn
์ฌ๊ธฐ๊น์ง packages/lib
์ ๊ณตํต ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์์ต๋๋ค.
์ค์น
: Step4์์ ์์ฑํ packages/lib
๋ฅผ apps/client
ํ๋ก์ ํธ์ ์์กด์ฑ์ผ๋ก ์ถ๊ฐํด์ ํ์ฉํด๋ด
๋๋ค.
$ yarn workspace @thepsyentist/client add @thepsyentist/lib
Nextjs ์ค์
: Nextjs์์๋ ์ธ๋ถ ํจํค์ง๋ฅผ ์์กด์ฑ์ ์ถ๊ฐํ ๊ฒฝ์ฐ, ํด๋น ํจํค์ง๋ฅผ ํธ๋์คํ์ผ ์ต์
์ ์ ์ฉํ ์ ์์ต๋๋ค.
// apps/client/next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
transpilePackages: ['@thepsyentist/lib'],
};
module.exports = nextConfig;
์ ์ฉ
: apps/client/src/app/page.tsx ํ์ผ ๋ด sayHello ํจ์๋ฅผ ํธ์ถํด์ ์ ์ฉํฉ๋๋ค.
์คํ
: @thepsyentist/client ํ๋ก์ ํธ๋ฅผ ์คํํ์ฌ ๋ด์ฉ์ด ๋ฐ์๋์๋์ง ํ์ธํฉ๋๋ค.
$ yarn workspace @thepsyentist/client dev
๋ชจ๋ ธ๋ ํฌ๋ฅผ ํตํด ํ์ฉํ ์ ์๋, ์ค์ ์ ์ฉ ๋ฐ ๊ณต์ , ๊ณตํต UI ํ์ฉ, ์คํฌ๋ฆฝํธ ํ์ฉ ๋ด์ฉ์ ํ์ตํฉ๋๋ค.
tsconfig.base.json ์์ฑ
: ์ํฌ์คํ์ด์ค ๋ง๋ค ๋งค๋ฒ tsconfig ์ค์ ์ ํ๋๊ฒ์ ๋ฒ๊ฑฐ๋ก์ธ ์์
์
๋๋ค. {rootDirectory}์ ์ํฌ์คํ์ด์ค์ ๊ณต์ ํ ์ ์๋ ํ์ผ์ ์์ฑํฉ๋๋ค.
// 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", "**/.*/"]
}
client, lib์ ์ ์ฉ
: tsconfig ์ค์ ์ค extends ํญ๋ชฉ์ ํตํด tsconfig.base.json๋ฅผ ์ ์ฉํฉ๋๋ค.
// 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,
"paths": {
"@/*": ["./*"]
}
},
"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"]
}
์ ์ ๊ฐ์ด extends์ tsconfig.base.json์ ์ถ๊ฐํ์ฌ ํ์ ์คํฌ๋ฆฝํธ ์ค์ ์ ๊ณต์ ํ ์ ์์ต๋๋ค.
์์กด์ฑ ์ค์น
: ์๋ CLI๋ฅผ ์คํํ์ฌ eslint, prettier ๊ด๋ จ ์์กด์ฑ์ {rootDirectory}์์ ์ค์นํฉ๋๋ค.
$ 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
ํ๋ฌ๊ทธ์ธ ์ค์น
: IDE์ ํ๋ฌ๊ทธ์ธ์์ eslint, prettier๋ฅผ ๊ฒ์ํ์ฌ ์ค์นํฉ๋๋ค.
eslint ์ค์ ํ์ผ ์ถ๊ฐ
: {rootDirectory}์์ .eslintrc.js ํ์ผ์ ์์ฑํฉ๋๋ค.
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' }],
// Hoisting์ ์ ๋ต์ ์ผ๋ก ์ฌ์ฉํ ๊ฒฝ์ฐ๊ฐ ๋ง์์
'@typescript-eslint/no-use-before-define': 'off',
// ๋ชจ๋ธ ์ ์ ๋ถ๋ถ์์ class์ interface๋ฅผ ํฉ์น๊ธฐ ์ํด ์ฌ์ฉํ๋ ์ฉ๋ฒ๋ ์ก๊ณ ์์ด์
'@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.memo, React.forwardRef์์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋ง๊ณ ์์ด์
'react/display-name': 'off',
'react-hooks/exhaustive-deps': 'error',
'react/react-in-jsx-scope': 'off',
'react/no-unknown-property': ['error', { ignore: ['css'] }],
},
};
eslint ์ค์ ๋ฑ๋ก
: IDE์ eslint๊ฐ ํ์ฑํ ๋๋๋ก ์ค์ ์ ๋ฑ๋กํฉ๋๋ค.
- Webstorm ์ settings โ eslint โ Automatic Eslint configuration ์ ์ง์ ํฉ๋๋ค.
- vscode ๋ .vscode/setting.json ์ ์ค์ ์ ์ถ๊ฐํฉ๋๋ค.
{
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"eslint.nodePath": ".yarn/sdks",
"prettier.prettierPath": ".yarn/sdks/prettier/index.js",
// ๊ธฐ๋ณธ ํฌ๋งทํฐ prettier๋ก ์ฌ์ฉ
"editor.defaultFormatter": "esbenp.prettier-vscode",
// ํ์ผ ์ ์ฅ์ formatter ์คํ
"editor.formatOnSave": true,
"editor.rulers": [120],
// ์ถ๊ฐ๋๋ ๋ด์ฉ
"eslint.packageManager": "yarn",
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"]
}
eslint ํจํค์ง๋ก ๊ตฌ์ฑํด์ ๊ณต์ ํ๊ณ ์ถ๋ค๋ฉด?
: https://tech.kakao.com/2019/12/05/make-better-use-of-eslint๋ฅผ ์ฐธ๊ณ ํด์ ๊ตฌ์ฑํด๋ณผ์ ์์ต๋๋ค.
.prettierrc ํ์ผ ์์ฑ
: {rootDirectory}์์ .prettierrc ํ์ผ์ ์์ฑํฉ๋๋ค.
{
"arrowParens": "avoid",
"bracketSameLine": false,
"bracketSpacing": true,
"endOfLine": "lf",
"jsxSingleQuote": false,
"printWidth": 120,
"proseWrap": "preserve",
"quoteProps": "as-needed",
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5"
}
prettier ์ค์ ๋ฑ๋ก
: IDE์ prettier๊ฐ ํ์ฑํ ๋๋๋ก ์ค์ ์ ๋ฑ๋กํฉ๋๋ค.
- Webstorm์ settings โ prettier ๊ฒ์ โ Automatic Prettier configuration์ ์ง์ ํฉ๋๋ค.
- vscode๋ `.vscode/settings.json` ๋ด์ฉ์ ์ถ๊ฐํฉ๋๋ค.
{
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"eslint.nodePath": ".yarn/sdks",
"prettier.prettierPath": ".yarn/sdks/prettier/index.js",
// ๊ธฐ๋ณธ ํฌ๋งทํฐ prettier๋ก ์ฌ์ฉ
"editor.defaultFormatter": "esbenp.prettier-vscode",
// ํ์ผ ์ ์ฅ์ formatter ์คํ
"editor.formatOnSave": true,
"editor.rulers": [120]
}
ํ๋ก์ ํธ ์์ฑ
: packages/ui ํด๋๋ฅผ ์์ฑํ๊ณ , ํ๋ก์ ํธ๋ฅผ ๊ตฌ์ฑํฉ๋๋ค.
$ cd packages/ui
$ yarn init
์ํฌ์คํ์ด์ค ์ด๋ฆ ๋ณ๊ฒฝ
: packages/ui/package.json ํ์ผ ๋ด name์ ๋ณ๊ฒฝํฉ๋๋ค.
{
"name": "@thepsyentist/ui",
...
}
๋ฆฌ์กํธ ์์กด์ฑ ์ค์น
: ์๋ cli๋ฅผ {rootDirectory}๋ก ์ด๋ํ์ฌ ์์กด์ฑ์ ์ค์นํฉ๋๋ค.
// rootํด๋๋ก ์ด๋
$ cd ../../
// ๊ฐฑ์
$ yarn
// install
$ yarn workspace @wanted/ui add typescript react react-dom @types/node @types/react @types/react-dom -D
tsconfig ์ถ๊ฐ
: packages/ui/tsconfig.js ํ์ผ์ ์์ฑํ์ฌ, ์๋ ๋ด์ฉ์ ์ถ๊ฐํฉ๋๋ค.
{
"$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"]
}
Button UI ์ถ๊ฐ
: ๊ณต์ ํ ๋ฒํผ ์ปดํฌ๋ํธ๋ฅผ ์ถ๊ฐํฉ๋๋ค.
// packages/ui/src/Button.tsx
import { ButtonHTMLAttributes, MouseEventHandler, ReactNode } from 'react';
export type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
children: ReactNode,
onClick?: MouseEventHandler<HTMLButtonElement>,
};
const Button = (props: ButtonProps) => {
const { children, onClick, ...other } = props;
return (
<button type="button" onClick={onClick} {...other}>
{children}
</button>
);
};
export default Button;
// packages/ui/src/index.ts
export { default as Button } from './Button';
main ๋ฑ๋ก
: package.json์ main ์์ฑ์ ์ถ๊ฐํฉ๋๋ค. main์ ์ง์ ํ์ฌ ๋ค๋ฅธ ์ํฌ์คํ์ด์ค์์ ์ฌ์ฉํ ๊ฒฝ์ฐ, ์ฐธ์กฐํ๋๋ก ํฉ๋๋ค.
{
"name": "@thepsyentist/ui",
"packageManager": "[email protected]",
"main": "src/index.ts", // ๐ ์ถ๊ฐ
"devDependencies": {
"@types/node": "^18.16.3",
"@types/react": "^18.2.0",
"@types/react-dom": "^18.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.0.4"
}
}
client์์ Button ํ์ฉํ๊ธฐ
: {rootDirectory}์์ pakcages/ui ์์กด์ฑ์ ์ถ๊ฐํฉ๋๋ค.
$ cd ..
$ cd ..
$ yarn workspace @thepsyentist/client add @thepsyentist/ui
Next.js config ์์
: transpilePackages์ ์ถ๊ฐํฉ๋๋ค.
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
transpilePackages: ['@thepsyentist/lib', '@thepsyentist/ui'],
};
module.exports = nextConfig;
ํ์ด์ง์ Button ์ถ๊ฐํ๊ธฐ
: apps/client/pages/index.tsx ํ์ผ์ ๋ฒํผ์ ์ถ๊ฐํฉ๋๋ค.
ํ๋ก์ ํธ ์คํ
: ํ์ด์ง์ ๋ฒํผ์ด ์ถ๊ฐ๋์๋์ง ํ๋ก์ ํธ๋ฅผ ์คํํฉ๋๋ค.
$ yarn workspace @thepsyentist/client dev
์ฌ๋ฌ ์ํฌ์คํ์ด์ค๋ง๋ค ์คํฌ๋ฆฝํธ๋ฅผ ๋ณ๋ ฌ๋ก ์คํํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด๊ฒ ์ต๋๋ค.
๋ฑ๋ก ๋ ์ํฌ์คํ์ด์ค๋ง๋ค lint, type check ๋ฑ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ์ฌ ์ฒดํฌ๋ฅผ ํ ์ ์์ต๋๋ค.
๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ผ๋ก๋ ๊ฐ ์ํฌ์คํ์ด์ค ๋๋ ํ ๋ฆฌ์ ์ ๊ทผํ์ฌ ๋ช
๋ น์ด๋ก ์คํ ํ ์์์ต๋๋ค.
ํ์ง๋ง ์ฌ๋ฌ ์ํฌ์คํ์ด์ค๋ฅผ ๊ด๋ฆฌํ๋ ๋ชจ๋
ธ๋ ํฌ ๊ตฌ์ฑ์์๋ ๋งค์ฐ ๋นํจ์จ์ ์ธ ๋ฐฉ์์
๋๋ค.
์ด๊ฒ์ ํ์ฉํ๊ธฐ์ํด yarn plugin
์ ํ์ฉํด๋ด
๋๋ค.
workspace-tools plugin ์ค์น
: ์๋ CLI๋ฅผ ์
๋ ฅํ์ฌ workspace-tools๋ฅผ ์ค์นํฉ๋๋ค.
$ yarn plugin import workspace-tools
typecheck script ์ถ๊ฐ
: ์๋ ๊ฐ ์ํฌ์คํ์ด์ค์ type check script๋ฅผ ์ถ๊ฐํฉ๋๋ค.
- apps/client/package.json
- packages/lib/package.json
- packages/ui/package.json
"scripts": {
"typecheck": "tsc --project ./tsconfig.json --noEmit"
},
g:typecheck ์คํฌ๋ฆฝํธ ์ถ๊ฐ
: {rootDirectory}์์ ์คํฌ๋ฆฝํธ ์คํ์ ์ํด package.json์ script๋ฅผ ์ถ๊ฐํฉ๋๋ค.
"scripts": {
"g:typecheck": "yarn workspaces foreach -pv run typecheck"
},
yarn workspaces foreach
๋ช ๋ น์ด option ํ์ธ
p
: ๋ณ๋ ฌ ์คํ
v
: workspace name ์ถ๋ ฅ
ํ์
์ฒดํฌ ์คํฌ๋ฆฝํธ ์คํ
: {rootDirectory}์์ ์ํฌ์คํ์ด์ค์ ํ์
์ฒดํฌ ์คํฌ๋ฆฝํธ๋ฅผ ์คํํฉ๋๋ค.
์ ๋ด์ฉ์ฒ๋ผ ๊ฐ ์ํฌ์คํ์ด์ค์์ ์ํ๋ ์คํฌ๋ฆฝํธ๋ฅผ ๋ฑ๋กํ๊ณ ,
{rootDirectory} package.json์ ์คํฌ๋ฆฝํธ๋ฅผ ๋ฑ๋กํ์ฌ ๋ชจ๋ ์ํฌ์คํ์ด์ค๋ฅผ ๊ด๋ฆฌํ ์ ์์ต๋๋ค.