๐Ÿš€ Next.js ํ”„๋กœ์ ํŠธ ์„ค์ • ์‹ค์ „ โ€” package.json ์™„์„ฑํ˜• ์•„ํ‚คํ…์ฒ˜

2026๋…„ 3์›” 5์ผ ์ˆ˜์ •๋จ

๐Ÿ“‹ ๊ฐœ์š”

Next.js 14 App Router ํ”„๋กœ์ ํŠธ์˜ package.json ์„ ์ฒ˜์Œ๋ถ€ํ„ฐ ์„ค๊ณ„ํ•œ๋‹ค. ์˜์กด์„ฑ ๋ถ„๋ฅ˜ ๊ทผ๊ฑฐ, Turbopack, .nvmrc, 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 ๋ถ„๋ฆฌ, tailwindcss devDependencies ๋Š” ๋ฐฐ์› ๋Š”๋ฐ... 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/clientServer Component, Route Handler ์—์„œ DB ์ฟผ๋ฆฌ ์‹คํ–‰
next-auth๋ฏธ๋“ค์›จ์–ด / API routes ์—์„œ ์„ธ์…˜ ์ฒ˜๋ฆฌ
jotaiํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ
@tanstack/react-queryํด๋ผ์ด์–ธํŠธ ๋ฐ์ดํ„ฐ ํŽ˜์นญ, SSR hydration
@tanstack/react-query-devtoolsprocess.env.NODE_ENV ์ฒดํฌ ํ›„ ๋ Œ๋” โ†’ ํ”„๋กœ๋•์…˜ ๋ฒˆ๋“ค์—์„œ ์ œ๊ฑฐ๋จ. ๊ทธ๋Ÿฌ๋‚˜ ์กฐ๊ฑด๋ถ€ import ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด dependencies ๊ถŒ์žฅ
zodRoute 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-stagedgit ํ›… ๊ด€๋ฆฌ. ๊ฐœ๋ฐœ์ž ๋กœ์ปฌ์—์„œ๋งŒ ์‹คํ–‰
@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.0

nvm ์ž๋™ ์ „ํ™˜ ์„ค์ • (์„ ํƒ): ~/.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
Turbopacknext dev --turbo ๋กœ ๊ฐœ๋ฐœ ์„œ๋ฒ„ ์†๋„ ํ–ฅ์ƒ. build ๋Š” ์•„์ง ์‹คํ—˜์ 
.nvmrcํŒ€ Node.js ๋ฒ„์ „ ํ†ต์ผ. nvm use ๋กœ ์ž๋™ ์ „ํ™˜
.npmrcengine-strict=true ๋กœ ๋ฒ„์ „ ๋ถˆ์ผ์น˜ ์ฐจ๋‹จ
Docker ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€builder ์—์„œ ๋นŒ๋“œ ํ›„ runner ์— ๊ฒฐ๊ณผ๋ฌผ๋งŒ ๋ณต์‚ฌ โ†’ devDependencies ์ œ๊ฑฐ
workspaceworkspace:* ๋กœ ๋กœ์ปฌ ํŒจํ‚ค์ง€ ์ฐธ์กฐ. ๋ชจ๋…ธ๋ ˆํฌ์˜ ๊ธฐ์ดˆ

๐Ÿ“ ๋งˆ๋ฌด๋ฆฌ ํ€ด์ฆˆ

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 ์ด๋ž‘ ๋ณด์•ˆํŽธ ํ•ด์•ผ์ง€.


๐Ÿ”— ๋” ์•Œ์•„๋ณด๊ธฐ