๐ Next.js ํ๋ก์ ํธ ์ค์ ์ค์ โ package.json ์์ฑํ ์ํคํ ์ฒ
๐ ๊ฐ์
Next.js 14 App Router ํ๋ก์ ํธ์ package.json ์ ์ฒ์๋ถํฐ ์ค๊ณํ๋ค. ์์กด์ฑ ๋ถ๋ฅ ๊ทผ๊ฑฐ, Turbopack, .nvmrc, Docker ์ต์ ํ, ๋ชจ๋ ธ๋ ํฌ workspace ๊ธฐ์ด๊น์ง ๋ค๋ฃฌ๋ค.
๐ ๋ชฉ์ฐจ
- ๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
- ๐ค ์ ์์์ผ ํ๋๊ฐ
- ๐๏ธ ์์ฑํ package.json ์ ์ฒด
- ๐งช ์์กด์ฑ ๋ถ๋ฅ ๊ทผ๊ฑฐ
- โก Turbopack & ๊ฐ๋ฐ ์๋ฒ ์ต์ ํ
- ๐ ํ ํ๊ฒฝ ํต์ผ ํ์ผ๋ค
- ๐ณ Docker ์ต์ ํ
- ๐๏ธ ๋ชจ๋ ธ๋ ํฌ workspace ๊ธฐ์ด
- ๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
- ๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
- ๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
- ๐ ๋ ์์๋ณด๊ธฐ
๐ ์ด ๋ฌธ์๋ฅผ ์ฝ๊ธฐ ์ ์
โฑ๏ธ ์์ ์ฝ๊ธฐ ์๊ฐ: 20๋ถ(์ ์ฒด) / ํต์ฌ ํํธ๋ง: 10๋ถ (์์ฑํ package.json + Docker)
๐บ๏ธ ์ด ๋ฌธ์์ ํ๋ฆ
์์ฑํ package.json โ ์์กด์ฑ ๋ถ๋ฅ ๊ทผ๊ฑฐ โ Turbopack ์ค์ โ ํ ํ๊ฒฝ ํต์ผ โ Docker ์ต์ ํ โ ๋ชจ๋
ธ๋ ํฌ ๊ธฐ์ด
๐ฏ ์ด ๋ฌธ์๋ฅผ ๋ค ์ฝ์ผ๋ฉด ํ ์ ์๋ ๊ฒ
- Next.js 14 App Router ํ๋ก์ ํธ์ package.json ์ ์ฒ์๋ถํฐ ์ค๊ณํ ์ ์๋ค
- ๋ชจ๋ ํจํค์ง์ dependencies/devDependencies ๋ฐฐ์น ๊ทผ๊ฑฐ๋ฅผ ์ฆ์ ์ค๋ช ํ ์ ์๋ค
-
.nvmrc์.npmrc๋ก ํ ์ ์ฒด์ Node.js, npm ๋ฒ์ ์ ํต์ผํ ์ ์๋ค - Docker ๋ฉํฐ ์คํ ์ด์ง ๋น๋์์ devDependencies ๋ฅผ ์ ์ธํด ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ต์ํํ ์ ์๋ค
- npm workspaces ๋ก ๊ฐ๋จํ ๋ชจ๋ ธ๋ ํฌ ๊ตฌ์กฐ๋ฅผ ์ค๊ณํ ์ ์๋ค
๐บ๏ธ ์ด ๋ฌธ์์ ๋ฐฐ๊ฒฝ ์ธ๊ณ๊ด: '์์๋ค ์ปค๋ฎค๋ํฐ'
- ๐ฃ ์์ฒ ( ์ ์
): "๋ฆฌ๋ ๋, ์
next create-app์ผ๋ก ๋ง๋ ๊ธฐ๋ณธ ๊ตฌ์กฐ์์ ํจํค์ง๋ค์ ๋ง ์ถ๊ฐํ๋๋ฐ, ์ด๋ ๊ฒ ์ด๋ ๋ค์ด๊ฐ์ผ ํ๋์ง ์์ ์ด ์์ด์. ํนํ@prisma/client๋prisma๋ถ๋ฆฌ,tailwindcssdevDependencies ๋ ๋ฐฐ์ ๋๋ฐ... Docker ์์๋ devDependencies ๋ฅผ ์ ์ธํด์ผ ํ๋ค๋ ๊ฑด ์ด๋ป๊ฒ ํ๋ ๊ฑด๊ฐ์? ๊ทธ๋ฆฌ๊ณ ํ์ด ์ปค์ง๋ฉด ๋ชจ๋ ธ๋ ํฌ๋ก ๊ฐ์ผ ํ๋ค๋๋ฐ, workspace ๋ ์ด๋ป๊ฒ ์์ํ๋์?" - ๐ฆ ์ํธ ( ๋ฆฌ๋ ): "์ค๋ ์์๋ค ์ปค๋ฎค๋ํฐ ์ ์ฒด package.json ์ ์ฒ์๋ถํฐ ์ค๊ณํด๋ณผ๊ฒ์. ๋ชจ๋ ํจํค์ง๋ฅผ ์ด๋์ ๋๋์ง, ์ ๊ฑฐ๊ธฐ ๋ฃ๋์ง ๊ทผ๊ฑฐ๊น์ง ๊ฐ์ด ์ค๋ช ํ ๊ฒ์. Docker ์ต์ ํ๋ workspace ๋์."
๐ค ์ ์์์ผ ํ๋๊ฐ
ํ์ด ์ปค์ง๊ณ ํ๋ก์ ํธ๊ฐ ์ฑ์ํด์ง์๋ก, "์ผ๋จ ๋์๊ฐ๋ฉด ๋์ง" ์์ package.json ์ ๊ธฐ์ ๋ถ์ฑ๊ฐ ๋๋ค. devDependencies ์ ๋ค์ด๊ฐ์ผ ํ ๊ฒ dependencies ์ ์์ผ๋ฉด Docker ์ด๋ฏธ์ง๊ฐ ๋ถํ์ํ๊ฒ ์ปค์ง๊ณ , engines ๊ฐ ์์ผ๋ฉด ํ์๋ง๋ค ๋ค๋ฅธ Node ๋ฒ์ ์ ์ฐ๋ค ๋ฌํ ๋ฒ๊ทธ๊ฐ ์๊ธด๋ค.
์ด ๊ฐ์ด๋์์๋ ์์๋ค ์ปค๋ฎค๋ํฐ์ package.json ์ ์ฒ์๋ถํฐ ์ค๊ณํ๋ ๊ณผ์ ์ ๋ณด์ฌ์ค๋ค. ๋จ์ํ ํ
ํ๋ฆฟ์ ๋ณต์ฌํ๋ ๊ฒ์ด ์๋๋ผ, ์ ์ด ๊ตฌ์กฐ์ธ์ง ๊ทผ๊ฑฐ๋ฅผ ํจ๊ป ์ดํดํ๋ ๊ฒ์ด ๋ชฉํ๋ค.
๐๏ธ ์์ฑํ package.json ์ ์ฒด
{
"name": "youngsoo-community",
"version": "1.0.0",
"private": true, // ๐ฆ ์ค์๋ก npm registry์ ๋ฐฐํฌ๋๋ ๊ฑธ ๋ง์์ฃผ๋ ๊ฐ์ฅ ์ค์ํ ์์ ์ฅ์น์์!
"description": "๊ฐ๋ฐ์ ์คํฐ๋ ๋งค์นญ ์ปค๋ฎค๋ํฐ ์๋น์ค",
"engines": {
// ๐ก ํ์ ๋ชจ๋๊ฐ "์ต์ํ ์ด ๋ฒ์ ์ด์์ Node.js"๋ฅผ ์จ์ผ ๋ฒ๊ทธ๊ฐ ์ ์๊น๋๋ค.
"node": ">=18.17.0",
"npm": ">=9.0.0"
},
"scripts": {
"dev": "next dev",
"dev:turbo": "next dev --turbo", // ๐ฆ Rust๋ก ๋ง๋ ์ด๊ณ ์ ์์ง! ๊ฐ๋ฐ ์๋๊ฐ ํจ์ฌ ๋นจ๋ผ์ ธ์.
"build": "next build",
"validate": "run-s type-check lint test", // ๐ก ๋ฐฐํฌ ์ '3์ข
์ธํธ'๋ฅผ ํ ๋ฒ์ ๊ฒ์ฌํฉ๋๋ค.
"prebuild": "npm run type-check", // ๋น๋ ๋ฒํผ ๋๋ฅด์๋ง์ ํ์
์๋ฌ๋ถํฐ ์ฒดํฌ!
"prepare": "husky", // ๐ฏ npm install๋ง ํด๋ git ํ
์ด ์๋ ์ธํ
๋๊ฒ ํฉ๋๋ค.
"postinstall": "prisma generate" // ์์กด์ฑ ๊น๊ณ ๋์ DB ํด๋ผ์ด์ธํธ๋ ์๋ ์์ฑ!
},
"dependencies": {
"next": "^14.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"@prisma/client": "^5.9.1",
"next-auth": "^4.24.5",
"jotai": "^2.6.4",
"@tanstack/react-query": "^5.18.0",
"@tanstack/react-query-devtools": "^5.18.0",
"zod": "^3.22.4",
"socket.io-client": "^4.7.2",
"date-fns": "^3.3.0"
},
"devDependencies": {
"typescript": "^5.3.3",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@types/node": "^20.11.5",
"eslint": "^8.56.0",
"eslint-config-next": "^14.1.0",
"prettier": "^3.2.4",
"tailwindcss": "^3.4.1",
"autoprefixer": "^10.4.17",
"postcss": "^8.4.33",
"prisma": "^5.9.1",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"@testing-library/react": "^14.1.2",
"@testing-library/jest-dom": "^6.4.1",
"husky": "^9.0.10",
"lint-staged": "^15.2.2",
"npm-run-all": "^4.1.5",
"@next/bundle-analyzer": "^14.1.0",
"cross-env": "^7.0.3"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"],
"*.{json,md,css}": "prettier --write"
}
}๐งช ์์กด์ฑ ๋ถ๋ฅ ๊ทผ๊ฑฐ โ ๋ชจ๋ ํจํค์ง๋ฅผ ์ ๊ฑฐ๊ธฐ์ ๋์๋
dependencies ๋ถ๋ฅ ๊ทผ๊ฑฐ
ํ๋จ ๊ธฐ์ค: "ํ๋ก๋์
์๋ฒ๊ฐ ์คํ ์ค์ ์ด ํจํค์ง์ ์ฝ๋๊ฐ ์คํ๋๋๊ฐ?"
| ํจํค์ง | ๋ถ๋ฅ ๊ทผ๊ฑฐ |
|---|---|
next | ์๋ฒ(Node.js ๋ฐํ์)๊ฐ ์์ฒญ์ ์ฒ๋ฆฌ. ๋ฐํ์ ํ์ |
react, react-dom | ์๋ฒ ์ปดํฌ๋ํธ ๋ ๋๋ง + ํด๋ผ์ด์ธํธ hydration. ๋ฐํ์ ํ์ |
@prisma/client | Server Component, Route Handler ์์ DB ์ฟผ๋ฆฌ ์คํ |
next-auth | ๋ฏธ๋ค์จ์ด / API routes ์์ ์ธ์ ์ฒ๋ฆฌ |
jotai | ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์ ์ญ ์ํ ๊ด๋ฆฌ |
@tanstack/react-query | ํด๋ผ์ด์ธํธ ๋ฐ์ดํฐ ํ์นญ, SSR hydration |
@tanstack/react-query-devtools | process.env.NODE_ENV ์ฒดํฌ ํ ๋ ๋ โ ํ๋ก๋์
๋ฒ๋ค์์ ์ ๊ฑฐ๋จ. ๊ทธ๋ฌ๋ ์กฐ๊ฑด๋ถ import ์ฒ๋ฆฌ๋ฅผ ์ํด dependencies ๊ถ์ฅ |
zod | Route Handler ์์ ์์ฒญ ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ |
socket.io-client | ํด๋ผ์ด์ธํธ์์ WebSocket ์ฐ๊ฒฐ |
date-fns | ์๋ฒ ๋ฐ ํด๋ผ์ด์ธํธ ๋ ์ง ํฌ๋งทํ |
devDependencies ๋ถ๋ฅ ๊ทผ๊ฑฐ
| ํจํค์ง | ๋ถ๋ฅ ๊ทผ๊ฑฐ |
|---|---|
typescript, @types/* | ์ปดํ์ผ ํ์ ํ์ ์ฒดํฌ. JS ๋ก ๋ณํ ํ ๋ฐํ์์ ์์ |
tailwindcss, postcss, autoprefixer | ๋น๋ ํ์ CSS ์์ฑ ๋๊ตฌ. ์์ฑ๋ CSS ํ์ผ๋ง ๋ฐํ์์ ์ฌ์ฉ |
prisma (CLI) | ์คํค๋ง ๊ด๋ฆฌ / ๋ง์ด๊ทธ๋ ์ด์ CLI ๋๊ตฌ. ๋ฐํ์ ์คํ ์์ |
eslint, prettier | ์ฝ๋ ํ์ง ๋๊ตฌ. ๋น๋ ๊ฒฐ๊ณผ๋ฌผ์ ํฌํจ๋์ง ์์ |
jest, @testing-library/* | ํ ์คํธ ๋๊ตฌ. ํ๋ก๋์ ๊ณผ ๋ฌด๊ด |
husky, lint-staged | git ํ ๊ด๋ฆฌ. ๊ฐ๋ฐ์ ๋ก์ปฌ์์๋ง ์คํ |
@next/bundle-analyzer | ๋ฒ๋ค ๋ถ์ ๋๊ตฌ. ๋ถ์ ์์๋ง ์คํ |
cross-env | ํ๊ฒฝ๋ณ์ ์ค์ ๋๊ตฌ. ์คํฌ๋ฆฝํธ ์คํ ์์๋ง ์ฌ์ฉ |
npm-run-all | ๋ค์ค ์คํฌ๋ฆฝํธ ์คํ ๊ด๋ฆฌ ๋๊ตฌ |
โก Turbopack & ๊ฐ๋ฐ ์๋ฒ ์ต์ ํ
Turbopack ์ด๋
Vercel ์ด ๊ฐ๋ฐํ Rust ๊ธฐ๋ฐ ๋ฒ๋ค๋ฌ. ๊ธฐ์กด Webpack ๋๋น:
- ์ด๊ธฐ ์ปดํ์ผ ์ต๋ 76% ๋น ๋ฆ
- ์ฝ๋ ๋ณ๊ฒฝ ์ HMR ์ต๋ 96% ๋น ๋ฆ
{
"scripts": {
"dev": "next dev",
"dev:turbo": "next dev --turbo"
}
}Turbopack ์ฌ์ฉ ์ ํ์ธ ์ฌํญ:
// next.config.js ์ ์ปค์คํ
webpack ์ค์ ์ด ์๋ค๋ฉด
const nextConfig = {
webpack: (config) => {
// โ ์ด๋ฐ ์ปค์คํ
์ค์ ์ด Turbopack ๊ณผ ํธํ๋๋์ง ๋จผ์ ํ์ธ
return config
}
}ํ์ฌ Next.js 14.1+ ์์ Turbopack ์ ๊ฐ๋ฐ ์๋ฒ(next dev) ์์ ์์ ํ๋จ. next build ์์์ Turbopack ์ ์์ง ์คํ์ .
๐ ํ ํ๊ฒฝ ํต์ผ ํ์ผ๋ค โ .nvmrc, .npmrc
.nvmrc โ Node.js ๋ฒ์ ํต์ผ
# .nvmrc (ํ๋ก์ ํธ ๋ฃจํธ)
# ๐ฆ "์ฐ๋ฆฌ ํ์ Node.js 18.17.0 ๋ฒ์ ์ ๊ธฐ์ค์ผ๋ก ๊ฐ๋ฐํฉ๋๋ค" ๋ผ๊ณ ๋งํด์ฃผ๋ ํ์ผ์ด์์.
18.17.0# ํ์์ด ํ๋ก์ ํธ ์ง์
์
nvm use # .nvmrc ์ ๋ฒ์ ์ผ๋ก ์๋ ์ ํ
# Found '/path/to/project/.nvmrc' with version <18.17.0>
# Now using node v18.17.0nvm ์๋ ์ ํ ์ค์ (์ ํ): ~/.zshrc ์ ๋ค์์ ์ถ๊ฐํ๋ฉด ๋๋ ํฐ๋ฆฌ ์ง์
์ ์๋์ผ๋ก Node ๋ฒ์ ์ด ๋ฐ๋๋ค:
# ~/.zshrc
autoload -U add-zsh-hook
load-nvmrc() {
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
nvm use
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc.npmrc โ npm ๋์ ์ ์ด
# .npmrc
engine-strict=true # engines ๋ฒ์ ๋ถ์ผ์น ์ install ์๋ฌ
# ๋ด๋ถ ๋ ์ง์คํธ๋ฆฌ ์ค์ (ํ์ฌ ๋ด๋ถ ํจํค์ง ์ฌ์ฉ ์)
@youngsoo:registry=https://npm.youngsoo.internal.com/
# CI ํ๊ฒฝ์์ audit ๊ฒฝ๊ณ ๋ฌด์ (CI ์์๋ ๋ณ๋ audit ๋จ๊ณ๊ฐ ์์)
# fund=false # ํ์ ๋ฉ์์ง ์จ๊ธฐ๊ธฐํ์ผ ๊ตฌ์กฐ ์ด์ ๋ฆฌ
youngsoo-community/
.nvmrc โ Node.js ๋ฒ์ 18.17.0
.npmrc โ engine-strict=true, ๋ ์ง์คํธ๋ฆฌ ์ค์
package.json โ engines ํ๋ ํฌํจ
package-lock.json โ ์ ํํ ๋ฒ์ ์ ๊ธ (git ์ปค๋ฐ ํ์)
.gitignore โ node_modules/, .next/
๐ณ Docker ์ต์ ํ โ ํ๋ก๋์ ์ด๋ฏธ์ง ์ต์ํ
๋ฉํฐ ์คํ ์ด์ง ๋น๋๋ก devDependencies ์ ๊ฑฐ
# โโโ Stage 1: ์์กด์ฑ ์ค์น + ๋น๋ (์๋ฆฌ ์ค๋น) โโโ
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci # ๐ฆ CI ํ๊ฒฝ์์ lockํ์ผ ๊ธฐ์ค์ผ๋ก ์๊ฒฉํ๊ฒ ์ค์น!
COPY . .
RUN npm run build # ๐ก ์ฌ๊ธฐ์ .next ํด๋๊ฐ ๋ง๋ค์ด์ง๋๋ค.
# โโโ Stage 2: ํ๋ก๋์
์คํ (์ง์ง ์๋น) โโโ
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# ๐ฏ standalone ๋ชจ๋: ๋น๋ ๊ฒฐ๊ณผ๋ฌผ ์ค '์คํ์ ๊ผญ ํ์ํ ํ์ผ'๋ง ์ ๊ณจ๋ผ๋
๋๋ค.
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
CMD ["node", "server.js"]๐ฅ ์ Stage ๊ฐ ๋ ๊ฐ์ธ๊ฐ:
- Stage 1 (builder):
devDependenciesํฌํจ ์ ์ฒด ์ค์น โ Next.js ๋น๋- Stage 2 (runner): ๋น๋ ๊ฒฐ๊ณผ๋ฌผ(
.next/)๋ง ๋ณต์ฌ โdevDependencies์์์ต์ข Docker ์ด๋ฏธ์ง์๋
devDependencies๊ฐ ํฌํจ๋์ง ์์ ์ด๋ฏธ์ง ํฌ๊ธฐ๊ฐ ํฌ๊ฒ ์ค์ด๋ ๋ค.
next.config.js ์ output: 'standalone' ํ์:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone'
}
module.exports = nextConfig์ด ์ค์ ์์ด๋ server.js ๊ฐ ์์ฑ๋์ง ์๋๋ค.
๐๏ธ ๋ชจ๋ ธ๋ ํฌ workspace ๊ธฐ์ด
ํ์ด ์ปค์ง๋ฉด apps/community (Next.js ์ฑ)์ packages/ui (๊ณต์ ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ)๋ฅผ ํ๋์ ๋ ํฌ์์ ๊ด๋ฆฌํ๋ ๋ชจ๋
ธ๋ ํฌ ๊ตฌ์กฐ๊ฐ ํ์ํ๋ค.
workspace ์ค์
youngsoo-monorepo/
package.json โ ๋ฃจํธ package.json (workspace ์ค์ )
apps/
community/ โ Next.js ์ฑ
package.json
admin/ โ ๊ด๋ฆฌ์ ์ฑ
package.json
packages/
ui/ โ @youngsoo/ui ๋ผ์ด๋ธ๋ฌ๋ฆฌ
package.json
utils/ โ @youngsoo/utils ๊ณต์ ์ ํธ
package.json
// ๋ฃจํธ package.json
{
"name": "youngsoo-monorepo",
"private": true,
"workspaces": [
"apps/*",
"packages/*"
]
}workspace ๋ด ํจํค์ง ์ฐธ์กฐ
// apps/community/package.json
{
"name": "youngsoo-community",
"dependencies": {
// ๐ฆ ๋ฉ๋ฆฌ ์๋ npm ์๋ฒ๊ฐ ์๋๋ผ, ์ฐ๋ฆฌ ํด๋ ์์ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด๊ฒ ๋ค๋ ๋ป์ด์์!
"@youngsoo/ui": "workspace:*",
"@youngsoo/utils": "workspace:*"
}
}workspace:* ๋ npm registry ๊ฐ ์๋ ๋ก์ปฌ packages/ui ํด๋์์ ์ง์ ๊ฐ์ ธ์จ๋ค.
workspace ๋ช ๋ น์ด
# ๋ฃจํธ์์ ์ ์ฒด ์์กด์ฑ ์ค์น
npm install # ๋ชจ๋ workspace ์ ์์กด์ฑ์ ํ ๋ฒ์ ์ค์น
# ํน์ workspace ์์ ๋ช
๋ น ์คํ
npm run build --workspace=apps/community
npm run build --workspace=packages/ui
# ๋ชจ๋ workspace ์์ ๋์์ ์คํ
npm run build --workspaces
# ํน์ workspace ์ ํจํค์ง ์ค์น
npm install react --workspace=apps/community๐ ์ด๋ฒ์ ๋ฐฐ์ด ๋ด์ฉ ์ด์ ๋ฆฌ
| ํญ๋ชฉ | ํต์ฌ ์์ฝ |
|---|---|
| ์์กด์ฑ ๋ถ๋ฅ | "ํ๋ก๋์ ์๋ฒ ์คํ ์ ํ์ํ๊ฐ?" โ YES: dependencies, NO: devDependencies |
| Turbopack | next dev --turbo ๋ก ๊ฐ๋ฐ ์๋ฒ ์๋ ํฅ์. build ๋ ์์ง ์คํ์ |
| .nvmrc | ํ Node.js ๋ฒ์ ํต์ผ. nvm use ๋ก ์๋ ์ ํ |
| .npmrc | engine-strict=true ๋ก ๋ฒ์ ๋ถ์ผ์น ์ฐจ๋จ |
| Docker ๋ฉํฐ ์คํ ์ด์ง | builder ์์ ๋น๋ ํ runner ์ ๊ฒฐ๊ณผ๋ฌผ๋ง ๋ณต์ฌ โ devDependencies ์ ๊ฑฐ |
| workspace | workspace:* ๋ก ๋ก์ปฌ ํจํค์ง ์ฐธ์กฐ. ๋ชจ๋
ธ๋ ํฌ์ ๊ธฐ์ด |
๐ ๋ง๋ฌด๋ฆฌ ํด์ฆ
Q1. Next.js ํ๋ก์ ํธ์์ tailwindcss ๋ฅผ devDependencies ์ ๋ฃ์ด์ผ ํ๋ ์ด์ ๋ก ๊ฐ์ฅ ์ ์ ํ ๊ฒ์?
a) Tailwind ๋ CSS ํ๋ ์์ํฌ๋ผ์ JavaScript ๋ฐํ์๊ณผ ๋ฌด๊ดํ๋ค
b) Next.js ๋น๋ ์ Tailwind ๊ฐ ์ฌ์ฉ๋ ํด๋์ค๋ฅผ ์ค์บํด CSS ํ์ผ์ ์์ฑํ๋ฏ๋ก, ๋น๋ ์๋ฃ ํ ํ๋ก๋์
์๋ฒ์๋ CSS ํ์ผ๋ง ์์ผ๋ฉด ๋๊ณ tailwindcss ์์ฒด๋ ํ์ ์๋ค
c) Tailwind ๋ CDN ์ผ๋ก๋ง ์ฌ์ฉํด์ผ ํ๋ค
d) devDependencies ์ dependencies ์ด๋์ ๋ฃ์ด๋ ๋์์ด ๊ฐ๋ค
โ
์ ๋ต: b
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: Next.js ๋น๋ ๊ณผ์ ์์
tailwindcss+postcss+autoprefixer๊ฐ.tsxํ์ผ์ Tailwind ํด๋์ค๋ฅผ ์ค์บํ๊ณ.next/static/css/xxx.css๋ฅผ ์์ฑํ๋ค. ์ด ๊ณผ์ ์ด ์๋ฃ๋ ํ, ํ๋ก๋์ ์๋ฒ๋ ์ด CSS ํ์ผ๋ง ์ ์ ์ผ๋ก ์๋นํ๋ค. ์๋ฒ๊ฐ ์คํ ์ค์tailwindcss์ฝ๋๊ฐ ์คํ๋๋ ์ผ์ ์์ผ๋ฏ๋กdevDependencies๊ฐ ๋ง๋ค. - ์ค์ฉ์ ์๋ฏธ: Docker ๋ฉํฐ ์คํ
์ด์ง ๋น๋์์
devDependencies๋ ์ต์ข ์ด๋ฏธ์ง์ ํฌํจ๋์ง ์๋๋ค.tailwindcss๋ฅผdependencies์ ์๋ชป ๋ฃ์ผ๋ฉด ์ฝ 5MB ์ ๋ถํ์ํ ์ฝ๋๊ฐ Docker ์ด๋ฏธ์ง์ ํฌํจ๋๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "Tailwind ๋ ๋น๋ ๊ณต์ฅ์ ๋๊ตฌ. ๊ณต์ฅ์์ ๋ฌผ๊ฑด์ด ๋์จ ๋ค ๊ณ ๊ฐ ์ง์ ๋๊ตฌ๋ฅผ ๋ณด๋ด์ง๋ ์๋๋ค."
Q2. Docker ๋ฉํฐ ์คํ ์ด์ง ๋น๋์์ Stage ๋ฅผ ๋ถ๋ฆฌํ๋ ์ฃผ์ ๋ชฉ์ ์?
a) ๋น๋ ์๋๋ฅผ ๋์ด๊ธฐ ์ํด ์ฌ๋ฌ CPU ์ฝ์ด๋ฅผ ์ฌ์ฉํ๋ ค๊ณ
b) Stage 1 ์์๋ง ์ฌ์ฉ๋๋ devDependencies, ๋น๋ ๋๊ตฌ ๋ฑ์ด ์ต์ข
์ด๋ฏธ์ง์ ํฌํจ๋์ง ์๋๋ก ํ์ฌ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ค์ธ๋ค
c) Dockerfile ์ฝ๋๋ฅผ ๋ณด๊ธฐ ์ข๊ฒ ์ ๋ฆฌํ๋ ค๊ณ
d) ์ฌ๋ฌ ๊ธฐ๋ฐ ์ด๋ฏธ์ง๋ฅผ ๋์์ ์ฌ์ฉํ๊ธฐ ์ํด
โ
์ ๋ต: b โ devDependencies ์ ์ธ๋ก ์ด๋ฏธ์ง ํฌ๊ธฐ ์ต์ํ
๐ก ์์ธ ํด์ค:
- ์๋ฆฌ ์ค๋ช
: Stage 1 (builder)์์๋ ๋น๋์ ํ์ํ ๋ชจ๋ ๊ฒ(devDependencies, ์์ค์ฝ๋, ๋น๋ ๋๊ตฌ)์ ์ฌ์ฉํด Next.js ๋น๋๋ฅผ ์คํํ๋ค. Stage 2 (runner)์์๋
.next/๊ฒฐ๊ณผ๋ฌผ๋ง ๋ณต์ฌํ๋ค. ์ต์ข Docker ์ด๋ฏธ์ง์๋ Stage 2 ์ ๋ด์ฉ๋ง ํฌํจ๋๋ฏ๋กdevDependencies๊ฐ ์์ ํ ์ ๊ฑฐ๋๋ค. ๊ฒฐ๊ณผ์ ์ผ๋ก ์ด๋ฏธ์ง ํฌ๊ธฐ๊ฐ ์๋ฐฑ MB ์ค์ด๋๋ ๊ฒฝ์ฐ๋ ๋ง๋ค. - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๋ฉํฐ ์คํ ์ด์ง = ๋น๋ ํ๊ฒฝ๊ณผ ์คํ ํ๊ฒฝ ๋ถ๋ฆฌ. ์คํ ํ๊ฒฝ์๋ ์ต์ํ๋ง."
Q3. ๐ฃ ์์ฒ ์ด์ ํ ์คํธ ํ์ โ ์๋์ด ๋ฉด์ ๋์
๋ฉด์ ๊ด (์ํธ ์คํ์ผ)์ด ๋ฌผ์๋ค. "Next.js ํ๋ก์ ํธ๋ฅผ Docker ๋ก ๋ฐฐํฌํ ๋, npm ci ์ npm ci --omit=dev ๋ฅผ Dockerfile ์ ์ด๋ค Stage ์์ ๊ฐ๊ฐ ์ฌ์ฉํด์ผ ํ๋์ง, ๊ทธ๋ฆฌ๊ณ ๊ทธ ์ด์ ๋ฅผ ์ค๋ช
ํด์ฃผ์ธ์."
โ
์ ๋ต: Stage 1 (builder)์์๋ npm ci, Stage 2 (runner)์์๋ Node.js standalone ์คํ โ --omit=dev ๋ ๋ฉํฐ ์คํ
์ด์ง์์ ๋ณ๋๋ก ์ฐ์ง ์์๋ ๋จ
# Stage 1: ๋น๋
FROM node:18-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci # devDependencies ํฌํจ. ๋น๋์ ํ์
COPY . .
RUN npm run build
# Stage 2: ์คํ
FROM node:18-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# ๋น๋ ๊ฒฐ๊ณผ๋ฌผ๋ง ๋ณต์ฌ โ node_modules ์ ์ฒด๋ฅผ ๋ณต์ฌํ์ง ์์!
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
CMD ["node", "server.js"]๐ก ์์ธ ํด์ค:
- Stage 1 ์์
npm ci(์ ์ฒด): Next.js ๋น๋ ์tailwindcss,typescript,@types/*๋ฑ devDependencies ๊ฐ ํ์ํ๋ค.--omit=dev๋ฅผ ์ฐ๋ฉด ๋น๋๊ฐ ์คํจํ๋ค. - Stage 2 ์์
--omit=dev๊ฐ ํ์ ์๋ ์ด์ :output: 'standalone'์ต์ ์ผ๋ก ๋น๋ํ๋ฉด.next/standalone/ํด๋์ ํ๋ก๋์ ์คํ์ ํ์ํ ์ต์ ํ์ผ(node_modules ํฌํจ)์ด ์๋์ผ๋ก ์์ฑ๋๋ค. ์ด ํด๋๋ง ๋ณต์ฌํ๋ฉด ์ด๋ฏธ devDependencies ๊ฐ ์ ๊ฑฐ๋ ์ํ๋ค. - ๋ง์ฝ standalone ์ ์ฐ์ง ์๋๋ค๋ฉด: ๋ค์์ฒ๋ผ production ์์กด์ฑ๋ง ๋ณ๋๋ก ์ค์นํ๋ Stage ๋ฅผ ์ถ๊ฐํ ์ ์๋ค:
FROM node:18-alpine AS deps WORKDIR /app COPY package.json package-lock.json ./ RUN npm ci --omit=dev # production ์์กด์ฑ๋ง - ๐ ํต์ฌ ๊ธฐ์ต๋ฒ: "๋น๋ํ ๋๋ ์ ๋ถ ํ์ํ๊ณ , ์คํํ ๋๋ ๊ฒฐ๊ณผ๋ฌผ๋ง ํ์ํ๋ค. ๋ฉํฐ ์คํ ์ด์ง๊ฐ ์ด ๋ ํ๊ฒฝ์ ๋ถ๋ฆฌํ๋ค."
๐ฃ ์์ฒ ์ด์ ํด๊ทผ ์ผ๊ธฐ
์ค๋ ๋๋์ด ์์๋ค ์ปค๋ฎค๋ํฐ ํ๋ก์ ํธ์ package.json ์ ์์ฑํ๋ค. ๊ทธ๋ฅ ๋ณต๋ถ์ผ๋ก ๋ง๋ ๊ฒ ์๋๋ผ, ๊ฐ ํจํค์ง๊ฐ ์ ๊ฑฐ๊ธฐ ๋ค์ด๊ฐ๋์ง ๊ทผ๊ฑฐ๋ฅผ ๊ฐ์ง๊ณ ์ค๊ณํ ์ฒซ ๋ฒ์งธ ๊ฒฝํ์ด์๋ค.
@prisma/client ๋ dependencies, prisma (CLI)๋ devDependencies โ ์ด๊ฒ ์ ๋๋๋์ง ์ด์ ๋ ๊ฐ๊ณ ๋ ์ค๋ช
ํ ์ ์๋ค. Docker ๋ฉํฐ ์คํ
์ด์ง ๋น๋๋ ์๊ฐ๋ณด๋ค ์ด๋ ต์ง ์์๋ค. "๋น๋ ํ๊ฒฝ๊ณผ ์คํ ํ๊ฒฝ์ ๋ถ๋ฆฌํ๋ค" ๋ ์์น ํ๋๋ก ์ ๋ถ ์ค๋ช
์ด ๋๋ค.
๐ก ์ค๋์ ๊ตํ: "์ข์ package.json ์ ๋จ์ํ ํจํค์ง ๋ชฉ๋ก์ด ์๋๋ค. ํ๋ก์ ํธ๊ฐ ์ด๋ค ํ๊ฒฝ์์ ์ด๋ป๊ฒ ์คํ๋ ์ง, ์ด๋ค ๋๊ตฌ๊ฐ ์ด๋ ๋จ๊ณ์์ ํ์ํ์ง์ ๋ํ ์ค๊ณ๋๋ค."
๋ชจ๋
ธ๋ ํฌ workspace ๋ ์ฌ์ฉ ๋ง๋ดค๋ค. ์ฒ์์ ๋ณต์กํด ๋ณด์๋๋ฐ, workspace:* ๋ก ๋ก์ปฌ ํจํค์ง๋ฅผ ์ฐธ์กฐํ๋ค๋ ๊ฒ ํต์ฌ์ด๋๋ผ. ํ์ด ์ปค์ง๋ฉด ๊ทธ๋ ๋ณธ๊ฒฉ์ ์ผ๋ก ํ์ผ๊ฒ ๋ค.
ํด๊ทผ ํ์ ์นํจ ์์ผฐ๋ค. ์ค๋์ ์ข ์์ถํด๋ ๋ ๊ฒ ๊ฐ๋ค. ๋ด์ผ์ ๋ง์ง๋ง์ผ๋ก npm audit ์ด๋ ๋ณด์ํธ ํด์ผ์ง.