1. ์์ํ๊ธฐ
yarn set version berry
yarn init
yarn add -D typescript @types/node @types/react
๋๋ ํ ๋ฆฌ ํ๋๋ฅผ ํ๊ณ yarn ๋ฒ์ ์ berry๋ก ์ค์ ํ ํ์ package.json์ ์์ฑํด์ค๋ค. ํ์ ์คํฌ๋ฆฝํธ๋ ํจ๊ป ์ธํ ํด์ค๋ค.
yarn ๊ด๋ จ ์ค์ ์ ์ด์ ๊ธ์์ ์งค๋งํ๊ฒ ์ ์ด๋์๋ค. ๋ค์ ๊ฐ๋ตํ๊ฒ ์ ์๋ฉด,
// ./package.json
{
"name": "dudoong-front",
"packageManager": "yarn@3.3.0",
"private": true,
"workspaces": {
"packages": [
"apps/*",
"shared/*"
]
},
// ...
}
๋ฃจํธ์ package.json์์ workspaces๋ฅผ ์ถ๊ฐํด์ค๋ค. workspaces์ ๊ฐ์ฒด๊ฐ ์๋๋ผ ๋ฐ๋ก ๋ฐฐ์ด์ ๋ฃ์ด์ค ์ ์๋๋ฐ, ๋ํดํธ๋ก packages ์ค์ ์ผ๋ก ๋ค์ด๊ฐ๊ฒ ๋๋ค. ๊ฐ์ฒด๋ก ์์ฑํด์ค๋ค๋ฉด "packages" ์ธ์๋ "nohoist" ์ค์ ์ ์ถ๊ฐํ ์ ์๋ค. ๋ช ์๋ ์์กด์ฑ ํจํค์ง๋ ๋ฃจํธ์ ๋ ธ๋๋ชจ๋๋ก ํธ์ด์คํ ๋์ง ์๊ณ , ํด๋น ์๋น์ค์ ๋ ธ๋๋ชจ๋์ ์ค์น๋๋ค.
eslint์ tsc ์ค์ ์ ์ถ๊ฐํด์ค๋ค. ๋ ๋ค --init์ผ๋ก ์ด๊ธฐ์ธํ ์ ์์ํ ์ ์๋ค.
.tsconfig
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"jsxImportSource": "@emotion/react"
},
"references": [
{
"path": "apps/ticket"
},
{
"path": "apps/admin"
},
{
"path": "shared/ui"
},
{
"path": "shared/utils"
}
],
"include": [],
"exclude": ["apps/**/dist/**"]
}
tsconfig์ ๊ฒฝ์ฐ references
๋ฅผ ์ถ๊ฐํด์ค๋ค. ๊ฐ ํ๋ก์ ํธ์ tsconfig์ ๋ฃจํธ์ ์ค์ ์ ์ค๋ฒ๋ผ์ด๋ํ ์ ์๋ค.
2. ํ๋ก์ ํธ ์์ฑ
โโโ apps
โ โโโ admin # ์ด๋๋ฏผ ์๋น์ค. react + vite
โ โโโ ticket # ํฐ์ผ ์๋น์ค. nextjs
โโโ shared
โโโ ui # ๊ณต์ ์ปดํฌ๋ํธ, theme. react + storybook
โโโ utils # ์ ํธ ํจ์, ๊ณต์ฉ ํ
, ํ์
๋ฑ. react
์ด ๋ค๊ฐ์ ์๋น์ค๋ฅผ ๋ง๋ ๋ค. ์ผ๋ฐ ์ฌ์ฉ์๋ฅผ ์ํ ํฐ์ผ ์๋น์ค์ ํธ์คํธ๋ฅผ ์ํ ์ด๋๋ฏผ ์๋น์ค. ํฐ์ผ ์๋น์ค๋ Nextjs๋ก, ์ด๋๋ฏผ ์๋น์ค๋ vite๋ก ์ฒ์ ์ธํ ํด์ฃผ์๋ค. SEO์ ํจ๊ป ๋น ๋ฅธ ๋ก๋ฉ ๋ฑ ์ฌ์ฉ์ ๊ฒฝํ์ด ๋ ์ค์ํด์ก๊ธฐ ๋๋ฌธ์ Nextjs๋ฅผ ์ ํํ๋ค. ๊ฐ์ ํ์ ์ค์์ Nextjs๋ฅผ ์๋ก ๊ณต๋ถํ๋ฉด์ ํ๋ก์ ํธ๊ฐ ์ด๋ ค์ด ์น๊ตฌ๋ค์ด ์์ด์ ์ด๋๋ฏผ ์๋น์ค๋ ์๋์ ๊ฐ์ด ๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ๋ค. ์ Vite๋ฅผ ์ ํํ๋์ง๋ ์๋์์ ์งค๋งํ๊ฒ ์ธ๊ธํ ๊ฒ ๊ฐ๋ค.
shared ๋๋ ํ ๋ฆฌ์ ์๋ ui์ utils ์๋น์ค๋ ์ผ๋ฐ ๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ๋ค. ๋ณ๋์ ๋น๋๊ฐ ํ์์๋ ๊ฒ๋ค์ด๊ณ , vite์์ ์คํ ๋ฆฌ๋ถ์ ์ธํ ํ๊ณ ๋ฐฐํฌํ๋๊ฒ ์ ๋ณด๊ฐ ๋ณ๋ก ์์ด์ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ์ ํํ๋ค. ํด๋น ๋๋ ํ ๋ฆฌ๋ก ๊ฐ์ ๊ฐ๊ฐ cra cna ๋ฑ์ ํฌํ๋ฆฟ์ผ๋ก ์์ฑํด์ค๋ค.
//shared/utils/package.json
{
"name": "@dudoong/utils",
"main": "src/index.ts",
"types": "src/index.ts",
// ...
}
ui์ util๊ฐ์ shared ๋ ํฌ์งํ ๋ฆฌ๋ค์ package.json์๋ main๊ณผ types ํ๋๋ฅผ ์ค์ ํด์ค๋ค. ํจํค์ง์ ๋ํ ์ํธ๋ฆฌํฌ์ธํธ๋ฅผ ๋ฃจํธ๊ฐ ์๋ src/index.js
๋ก ๋ฐ๊ฟ ์ ์๋ค. ํจํค์ง ์ด๋ฆ์ @dudoong/
์ผ๋ก ์์ํ๊ฒ ํ๋๋ฐ,
// apps/ticket/package.json
"dependencies": {
"@dudoong/ui": "workspace:shared/ui",
"@dudoong/utils": "workspace:shared/utils",
apps์์๋ dependencies์์ ๋๊ฐ์ ์ด๋ฆ์ผ๋ก ๊ทธ ํจํค์ง๋ค์ ๊ฐ์ ธ๋ค ์ฌ์ฉํ ์ ์๋ค. ์ ๋๊ฒฝ๋ก๋ฅผ ์ค์ ํ ๋ @๋ก ์์ํ๋ ๊ฒ๊ณผ ๋๊ฐ์ ํ์์ด๋ค.
3. ๊ทธ๋ฆฌ๊ณ ์ฌ๋ฌ๊ฐ์ง๋ค
transpile
๋ชจ๋ ธ๋ ํฌ ๋ด๋ถ์์ ๋ง๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์ค๋ ค๊ณ ๋ณด๋ unexpected token ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
Module parse failed: Unexpected token (1:21)
You may need an appropriate loader to handle this file type,
currently no loaders are configured to process this file.
See https://webpack.js.org/concepts#loaders
์ปดํฌ๋ํธ๋ค์ Next ์์ผ๋ก ๊ฐ์ ธ์์ ์ฌ์ฉํ๊ธฐ ์ํด ๋ณ๋์ ํธ๋์คํ์ผ๋ง์ด ํ์ํ๋ค. yarn์์ next-transpile-modules
ํ๋ฌ๊ทธ์ธ์ ์ค์นํ๋ค.
const withTM = require('next-transpile-modules')([
'@dudoong/ui',
'@dudoong/utils',
]);
module.exports = withTM({
// Any additional config for next goes in here
});
next.config.js๋ฅผ ์ด๋ ๊ฒ ์์ ํ๋ค. ํฐ๋ณด๋ ํฌ ๋ฌธ์์์๋ transpilePackages
๋ผ๋ ๊ฒ์ ์ฌ์ฉํ๋ผ๊ณ ๋์์๋ค. Next13.1๋ถํฐ ์ ์ฉ ๊ฐ๋ฅํ๋ค. ์ฒ์ ๋ฌธ์๋ ์ ๊ฒ ์๋๋ผ ๋ด๊ฐ ํ ๋ฐฉ๋ฒ๋๋ก ์จ ์์๋๋ฐ ๊ทธ์ ์
๋ฐ์ดํธ ๋ ๋ฏ.
์ฒ์ ํ๋ก์ ํธ๋ฅผ ๊ตฌ์ถํ ๋ ์ด ๋ฌธ์์์ vite๋ ๋ณ๋์ ํธ๋์คํ์ผ ์ค์ ์ด ํ์์๋ค๋ ๊ฒ์ ๋ณด์๋ค. ๊ทธ๋์ admin ํ์ด์ง๋ vite๋ฅผ ์ฌ์ฉํ๋๋ฐ, ์๊ฐํด๋ณด๋๊น ๊ทธ๋ฅ ๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ์ด๋ ์๊ด ์์์ ๊ฒ ๊ฐ๋ค. ๊ทธ๋๋ vite๋ฅผ ์ฒ์ ์ฌ์ฉํด๋ดค๋๋ฐ, ๋น๋๋ ์์ฒญ ๋น ๋ฅด๊ณ ๋ณ๋์ eject๋ craco ์์ด๋ vite.config.ts
์์ ๋ชจ๋ ๊ด๋ จ ์ค์ ์ด ํธํ๋ค๋ ์ ์ด ์ข๋ค.
emotion
yarn add @emotion/react @emotion/styled
์ด๋ชจ์ ์ ์ค์นํด์ค๋ค. (+ CSS Prop ๊ด๋ จ ์ค๋ฅ, ๊ทธ๋ฆฌ๊ณ babel-plugin ๋ฑ์ ์ค์ ์ ๊ฐ์ด ํด์ค๋ค.)
//emotion.d.ts
declare module '@emotion/react' {
export interface Theme {
palette: TypeOfPalette;
typo: TypeOfTypo;
}
}
ThemeProvider๋ฅผ ์ฌ์ฉํ๋ฉด์ emotion ํ์
๋ชจ๋์ ์ง์ ์ ์ธํด์ ์ฌ์ฉํด์ผ ํ๋ค. theme๊ณผ global style ๊ด๋ จ ํ์ผ๋ค์ shared/ui
์์ ์์
ํ๊ณ apps๋ก ์ํฌํธํ๊ณ ์ฌ์ฉํ๊ณ ์๋๋ฐ, ui ๋ ํฌ์งํ ๋ฆฌ ์ธ๋ถ์์๋ emotion.d.ts
์ ๊ฒฝ๋ก๋ฅผ ๊ฐ์ ธ์ค์ง ๋ชปํด ๋ฌธ์ ๊ฐ ์๊ฒผ๋ค.
// tsconfig.json
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "../../**/*.d.ts"],
๊ฐ ๋ ํฌ์งํ ๋ฆฌ์ tsconfig์์ ์ง์ ํด๋น ๊ฒฝ๋ก์ d.ts ํ์ผ์ includeํ๋๋ก ์ค์ ํด์ฃผ์ด์ ํด๊ฒฐํ๋ค.
Storybook ์ค์
yarn dlx sb init --builder webpack5
storybook์ ๋ค์ด๋ฐ๋๋ค. webpack5๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ ๋ณ๋์ ์ค์ ์ด ํ์ํ๋ค.
//...
"resolutions": {
"webpack": "5",
"@storybook/core-common/webpack": "^5",
"@storybook/core-server/webpack": "^5",
"@storybook/react/webpack": "^5"
},
๋ฃจํธ์ package.json์์ resolution์ ์ถ๊ฐ๋ฅผ ํด์ค๋ค. ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์กดํ๊ณ ์๋ ๋ค๋ฅธ ํจํค์ง๋ค์ ๋ฒ์ ์ ๊ณ ์ ํ ์ ์๋ค.
๊ทผ๋ฐ ์ webpack5๋ฅผ ์ฌ์ฉํ๋๊ฑฐ์ง... ์์งํ๊ฒ ๋งํ๋ฉด ์ฐ์ํ ๋ธ๋ก๊ทธ์์ 5๋ฅผ ์ฌ์ฉํ๊ธธ๋...
code-workspace
ํ๋ก์ ํธ์ ๋ฃจํธ๋ก ๋ค์ด๊ฐ์๋ ๊ฐ๊ฐ์ง ์ค์ ํ์ผ๋ค์ด ๋ง์์ง๊ณ , ๋ถํ์ํ ๋๋ ํ ๋ฆฌ๋ค ๋๋ฌธ์ ์์ ํ๊ธฐ ๋ถํธํ๊ฒ ๋์๋ค. ํ์ฌ ๋ด๊ฐ ์์ ํ๊ณ ์๋ ๋ถ๋ถ์ admin์ธ๋ฐ๋ ticket ๋ถ๋ถ๊น์ง ํ์๊ธฐ์ ์ซ ์์ ํ์๋ ์๋ค.
//set-admin.code-workspaces
{
"folders": [
{
"path": "apps/admin"
},
{
"path": "shared"
}
]
}
์ด๋ ๊ฒ .code-workspaces
ํ์ผ์ ๋ฃจํธ์ ๋ง๋ค์ด์ฃผ๋ฉด, vscode์์ ์์
๊ณต๊ฐ์ ์ค์ ํด ๋ค์ด๊ฐ ์ ์๋ค.
๋ค์ (3ํธ)์์๋ ๋ชจ๋ ธ๋ ํฌ ์๋น์ค ๋ฐฐํฌ์ CICD ๊ตฌ์ถ ์ฝ์ง๊ธฐ๋ก!