๐Ÿš€ Next.js ์‹ฌํ™” 10์žฅ: Deployment & Infrastructure โ€” Vercel vs Docker vs Node.js ์„œ๋ฒ„

๐Ÿ“‹ ๊ฐœ์š”

Vercel, Docker, Node.js ์„œ๋ฒ„ ๊ฐ ๋ฐฐํฌ ๋ฐฉ์‹์˜ ์ฐจ์ด์™€ ์ธํ”„๋ผ ์„ค๊ณ„ ๊ธฐ์ค€์„ ์•Œ์•„๋ด…๋‹ˆ๋‹ค.

๐Ÿ“‹ ๋ชฉ์ฐจ


๐Ÿ“Œ ์ด ๋ฌธ์„œ๋ฅผ ์ฝ๊ธฐ ์ „์—

โฑ๏ธ ์˜ˆ์ƒ ์ฝ๊ธฐ ์‹œ๊ฐ„: 18๋ถ„ (์ „์ฒด) / ํ•ต์‹ฌ ํŒŒํŠธ๋งŒ: 9๋ถ„

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ๋ฐฐ๊ฒฝ ์„ธ๊ณ„๊ด€: '์˜์ˆ˜๋„ค ์ปค๋ฎค๋‹ˆํ‹ฐ'

  • ์˜์ˆ˜(PM): "์ด์ œ ์„œ๋น„์Šค๋ฅผ ์‹ค์ œ๋กœ ๋ฐฐํฌํ•ด์•ผ ํ•ด์š”. Vercel๋กœ ๋น ๋ฅด๊ฒŒ ๋‚˜๊ฐ€์•ผ ํ•˜๋‚˜์š”, ์•„๋‹ˆ๋ฉด ํšŒ์‚ฌ ์„œ๋ฒ„(AWS EC2)์— Docker๋กœ ์˜ฌ๋ ค์•ผ ํ•˜๋‚˜์š”? ์ฐจ์ด๊ฐ€ ๋ญ”์ง€ ์•Œ๊ณ  ๊ฒฐ์ •ํ•˜๊ณ  ์‹ถ์–ด์š”."
  • ์˜ํ˜ธ(๋ฆฌ๋“œ): "ํŒ€ ๊ทœ๋ชจ, ๋น„์šฉ, ๊ธฐ์ˆ  ์„ฑ์ˆ™๋„์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์š”. ์Šคํƒ€ํŠธ์—… ์ดˆ๊ธฐ๋ผ๋ฉด Vercel์ด ์ตœ์ ์ด์—์š”. ISR, Edge Functions, ํ”„๋ฆฌ๋ทฐ ๋ฐฐํฌ๊ฐ€ ๊ธฐ๋ณธ์ด๊ณ  ์ธํ”„๋ผ ๊ฑฑ์ •์„ ์•„์˜ˆ ์•ˆ ํ•ด๋„ ๋˜๊ฑฐ๋“ ์š”. ๋ฐ˜๋ฉด ๊ธฐ์—… ๋‚ด๋ถ€ ์„œ๋ฒ„๋‚˜ ํŠน์ • ๊ทœ์ •(์ปดํ”Œ๋ผ์ด์–ธ์Šค)์ด ์žˆ์œผ๋ฉด Docker๋กœ ์ž์ฒด ์„œ๋ฒ„์— ์˜ฌ๋ ค์•ผ ํ•ด์š”."

๐Ÿ—บ๏ธ ์ด ๋ฌธ์„œ์˜ ํ๋ฆ„
์„ธ ๊ฐ€์ง€ ์ „๋žต ๋น„๊ต โ†’ Vercel ๋ฐฐํฌ ์„ค์ • โ†’ Docker ์ตœ์  Dockerfile โ†’ Node.js ์„œ๋ฒ„ ์ง์ ‘ ๋ฐฐํฌ โ†’ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ

๐ŸŽฏ ์ด ๋ฌธ์„œ๋ฅผ ๋‹ค ์ฝ์œผ๋ฉด ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ

  • ํŒ€๊ณผ ์„œ๋น„์Šค ์ƒํ™ฉ์— ๋งž๋Š” ๋ฐฐํฌ ์ „๋žต์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค
  • Next.js ์•ฑ์„ ์œ„ํ•œ ์ตœ์ ํ™”๋œ Dockerfile์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค
  • ๋น„๋ฐ€ํ‚ค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•˜๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์›์น™์„ ์ง€ํ‚ฌ ์ˆ˜ ์žˆ๋‹ค

๐Ÿค” ์™œ ์•Œ์•„์•ผ ํ•˜๋Š”๊ฐ€

๋กœ์ปฌ์—์„œ ์ž˜ ๋™์ž‘ํ•˜๋˜ ์ฝ”๋“œ๊ฐ€ ๋ฐฐํฌ ํ›„ ๊ฐ‘์ž๊ธฐ ์•ˆ ๋™์ž‘ํ•˜๊ฑฐ๋‚˜, ์„œ๋ฒ„ ๋น„์šฉ์ด ์˜ˆ์ƒ๋ณด๋‹ค 10๋ฐฐ ๋‚˜์˜ค๊ฑฐ๋‚˜, ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ํด๋ผ์ด์–ธํŠธ์— ๋…ธ์ถœ๋˜๋Š” ์‚ฌ๊ณ  โ€” ๋ฐฐํฌ๋ฅผ ๋ชจ๋ฅด๋ฉด ์ด๋Ÿฐ ์ผ์ด ์ƒ๊ฒจ.

๋ฐฐํฌ๋Š” "์–ด๋–ค ์ธํ”„๋ผ์—์„œ ์–ด๋–ป๊ฒŒ ์‹คํ–‰ํ•  ๊ฒƒ์ธ๊ฐ€"์— ๋Œ€ํ•œ ์•„ํ‚คํ…์ฒ˜ ๊ฒฐ์ •์ด์•ผ. ์ฝ”๋”ฉ๋งŒํผ ์ค‘์š”ํ•œ ์„ ํƒ์ด์•ผ.


๐Ÿ—๏ธ ๋น„์œ ๋กœ ๋จผ์ € ์ดํ•ดํ•˜๊ธฐ

๐Ÿง’ 5์‚ด์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?
์Œ์‹ ๋ฐฐ๋‹ฌ ๋ฐฉ์‹์— ๋น„์œ ํ•ด๋ด:

  • Vercel = ๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ์ž…์ . ์ฃผ๋ฐฉ๋งŒ ์žˆ์œผ๋ฉด ๋˜๊ณ , ๋ฐฐ๋‹ฌยท๊ฒฐ์ œยท๊ด‘๊ณ ๋Š” ํ”Œ๋žซํผ์ด ๋‹ค ํ•ด์ค˜. ๋น ๋ฅธ ๋Œ€์‹  ์ˆ˜์ˆ˜๋ฃŒ(๋น„์šฉ)๊ฐ€ ์žˆ๊ณ , ํ”Œ๋žซํผ ๊ทœ์น™์„ ๋”ฐ๋ผ์•ผ ํ•ด.
  • Docker = ์Œ์‹์  ์ง์ ‘ ์šด์˜ + ๋ฐฐ๋‹ฌ ํŠธ๋Ÿญ ๋ณด์œ . ๋ชจ๋“  ๊ฑธ ์ง์ ‘ ๊ด€๋ฆฌํ•ด์•ผ ํ•˜์ง€๋งŒ ์ž์œ ๋„๊ฐ€ ์ตœ๊ณ ์•ผ.
  • Node.js ์ง์ ‘ ๋ฐฐํฌ = ์Œ์‹์  ์ง์ ‘ ์šด์˜์ธ๋ฐ ๋ฐฐ๋‹ฌ ํŠธ๋Ÿญ์€ ์„ธ๋†“๊ธฐ. Docker๋ณด๋‹ค ์กฐ๊ธˆ ๋” ๋‹จ์ˆœํ•˜์ง€๋งŒ ๊ด€๋ฆฌํ•  ๊ฒŒ ์—ฌ์ „ํžˆ ๋งŽ์•„.

๐Ÿงฉ ์„ธ ๊ฐ€์ง€ ๋ฐฐํฌ ์ „๋žต ๋น„๊ต ๐ŸŸข

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • ์„ธ ๊ฐ€์ง€ ๋ฐฐํฌ ์ „๋žต์˜ ์žฅ๋‹จ์ ์„ ์ •ํ™•ํžˆ ์•Œ๊ณ  ์ƒํ™ฉ์— ๋งž๋Š” ์„ ํƒ์„ ํ•  ์ˆ˜ ์žˆ๋‹ค
๊ธฐ์ค€VercelDocker (K8s ํฌํ•จ)Node.js ์„œ๋ฒ„ ์ง์ ‘
์„ค์ • ๋ณต์žก๋„โญ ๊ฑฐ์˜ ์—†์Œ๐Ÿ”ด ๋†’์Œ๐ŸŸก ์ค‘๊ฐ„
๋น„์šฉ ์˜ˆ์ธก์„ฑ๐ŸŸก ํŠธ๋ž˜ํ”ฝ ๋”ฐ๋ผ ๋ณ€๋™โœ… ์˜ˆ์ธก ๊ฐ€๋Šฅโœ… ์˜ˆ์ธก ๊ฐ€๋Šฅ
ISR / Edge ์ง€์›โœ… ์™„๋ฒฝ ์ง€์›๐ŸŸก ์„ค์ • ํ•„์š”๐ŸŸก ์ œํ•œ์ 
์ž์ฒด ์„œ๋ฒ„ ํ†ตํ•ฉโŒ ์–ด๋ ค์›€โœ… ์™„๋ฒฝ ํ†ตํ•ฉโœ… ๊ฐ€๋Šฅ
์ปดํ”Œ๋ผ์ด์–ธ์Šค๐ŸŸก ๋ฐ์ดํ„ฐ ๋ฏธ๊ตญ ์„œ๋ฒ„โœ… ์ž์ฒด ์„œ๋ฒ„ ์™„์ „ ์ œ์–ดโœ… ์ž์ฒด ์ œ์–ด
๊ฐ€์žฅ ์ ํ•ฉํ•œ ์ƒํ™ฉ์Šคํƒ€ํŠธ์—…, ์‚ฌ์ด๋“œ ํ”„๋กœ์ ํŠธ, ๋น ๋ฅธ ์ถœ์‹œ๊ธฐ์—…, ๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค, ๊ณ ๊ฐ€์šฉ์„ฑ๊ธฐ์กด VM ๋ณด์œ , ์ค‘์†Œ ๊ทœ๋ชจ

2026๋…„ ๊ธฐ์ค€ ์„ ํƒ ๊ฐ€์ด๋“œ:

ํŒ€ ๊ทœ๋ชจ ์†Œ + ๋น ๋ฅธ ์ถœ์‹œ + ๋น„์šฉ ์ตœ์†Œํ™” โ†’ Vercel
๋ฐ์ดํ„ฐ ๊ตญ๋‚ด ์„œ๋ฒ„ ํ•„์ˆ˜ + ๊ธฐ์—… ๋ณด์•ˆ ๊ทœ์ • โ†’ Docker (์‚ฌ๋‚ด K8s)
๊ธฐ์กด VM ๋ณด์œ  + ๋‹จ์ˆœ ๋ฐฐํฌ + ์˜ˆ์ธก ๋น„์šฉ โ†’ Node.js ์ง์ ‘ ๋ฐฐํฌ

๐Ÿš€ Vercel ๋ฐฐํฌ โ€” ์ œ๋กœ ์„ค์ •์˜ ๋งˆ๋ฒ• ๐ŸŸก

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • Vercel์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๊ณ  ์ปค์Šคํ…€ ๋„๋ฉ”์ธ์„ ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค
  • vercel.json์œผ๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ์™€ ํ—ค๋”๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค

Vercel ๋ฐฐํฌ ๊ณผ์ •:

# 1. Vercel CLI ์„ค์น˜
npm i -g vercel
 
# 2. ํ”„๋กœ์ ํŠธ ์—ฐ๊ฒฐ ๋ฐ ๋ฐฐํฌ
vercel
 
# 3. ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ
vercel --prod

๋˜๋Š” GitHub ์—ฐ๋™์œผ๋กœ pushํ•  ๋•Œ๋งˆ๋‹ค ์ž๋™ ๋ฐฐํฌ (๊ถŒ์žฅ).

vercel.json ์„ค์ •:

{
  "framework": "nextjs",
  "buildCommand": "npm run build",
  "devCommand": "npm run dev",
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        { "key": "X-Frame-Options", "value": "DENY" },
        { "key": "X-Content-Type-Options", "value": "nosniff" },
        { "key": "Strict-Transport-Security", "value": "max-age=31536000; includeSubDomains" }
      ]
    }
  ],
  "redirects": [
    { "source": "/old-blog/(.*)", "destination": "/posts/$1", "permanent": true }
  ]
}

Vercel ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •:

Vercel Dashboard โ†’ ํ”„๋กœ์ ํŠธ โ†’ Settings โ†’ Environment Variables

๊ฐœ๋ฐœ: DATABASE_URL=postgresql://localhost:5432/dev
ํ”„๋ฆฌ๋ทฐ: DATABASE_URL=postgresql://preview-server/preview
ํ”„๋กœ๋•์…˜: DATABASE_URL=postgresql://prod-server/prod

โš ๏ธ NEXT_PUBLIC_ ์ ‘๋‘์‚ฌ๊ฐ€ ๋ถ™์€ ๋ณ€์ˆ˜๋งŒ ํด๋ผ์ด์–ธํŠธ์— ๋…ธ์ถœ๋จ!

Vercel์˜ ํŠน๋ณ„ ๊ธฐ๋Šฅ:

โœ… Preview URL: PR๋งˆ๋‹ค ๊ณ ์œ ํ•œ ๋ฏธ๋ฆฌ๋ณด๊ธฐ URL ์ž๋™ ์ƒ์„ฑ
   - https://my-app-pr-123-team.vercel.app

โœ… ISR ์ง€์›: revalidate ์„ค์ • ์ฆ‰์‹œ ๋™์ž‘
โœ… Edge Functions: middleware๊ฐ€ Vercel Edge์—์„œ ์ž๋™ ์‹คํ–‰
โœ… Analytics: Core Web Vitals ์‹ค์ œ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ์ž๋™ ์ˆ˜์ง‘

๐Ÿณ Docker ์ปจํ…Œ์ด๋„ˆ ๋ฐฐํฌ โ€” ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ํ‘œ์ค€ ๐ŸŸก

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • Next.js ์•ฑ์„ ์œ„ํ•œ ์ตœ์ ํ™”๋œ ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ Dockerfile์„ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ์ตœ์†Œํ™”ํ•˜๋Š” ๋นŒ๋“œ ํŒจํ„ด์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

Next.js ๊ณต์‹ ๊ถŒ์žฅ ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ Dockerfile:

# Dockerfile
 
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Stage 1: ์˜์กด์„ฑ ์„ค์น˜
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
FROM node:20-alpine AS deps
WORKDIR /app
 
# package.json๊ณผ lock ํŒŒ์ผ๋งŒ ๋จผ์ € ๋ณต์‚ฌ (๋ ˆ์ด์–ด ์บ์‹ฑ ์ตœ์ ํ™”)
COPY package.json package-lock.json ./
RUN npm ci --only=production
 
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Stage 2: ๋นŒ๋“œ
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
FROM node:20-alpine AS builder
WORKDIR /app
 
# ๋นŒ๋“œ์— ํ•„์š”ํ•œ ์ „์ฒด ์˜์กด์„ฑ ์„ค์น˜ (devDependencies ํฌํ•จ)
COPY package.json package-lock.json ./
RUN npm ci
 
COPY . .
 
# ๋นŒ๋“œ ํƒ€์ž„ ํ™˜๊ฒฝ ๋ณ€์ˆ˜
ARG NEXT_PUBLIC_API_URL
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
 
# standalone ๋ชจ๋“œ๋กœ ๋นŒ๋“œ (์„œ๋ฒ„์— ํ•„์š”ํ•œ ํŒŒ์ผ๋งŒ ์ถœ๋ ฅ)
RUN npm run build
 
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
# Stage 3: ์‹คํ–‰ ์ด๋ฏธ์ง€ (์ตœ์†Œ ํฌ๊ธฐ)
# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
FROM node:20-alpine AS runner
WORKDIR /app
 
ENV NODE_ENV=production
 
# ๋ณด์•ˆ: ๋ฃจํŠธ ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ธฐ
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
 
# standalone ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ๋งŒ ๋ณต์‚ฌ (์ตœ์†Œ ํŒŒ์ผ)
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
 
USER nextjs
 
EXPOSE 3000
 
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
 
CMD ["node", "server.js"]
// next.config.ts โ€” standalone ๋ชจ๋“œ ํ™œ์„ฑํ™” (Docker ํ•„์ˆ˜ ์„ค์ •)
const nextConfig = {
  output: 'standalone',    // ์„œ๋ฒ„์— ํ•„์š”ํ•œ ์ตœ์†Œ ํŒŒ์ผ๋งŒ .next/standalone์— ์ถœ๋ ฅ
}
# docker-compose.yml โ€” ๋กœ์ปฌ ๊ฐœ๋ฐœ/์Šคํ…Œ์ด์ง•์šฉ
services:
  app:
    build:
      context: .
      args:
        NEXT_PUBLIC_API_URL: https://api.youngsu.community
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/youngsu
      - SESSION_SECRET=your-secret-key-here
    depends_on:
      - db
 
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: youngsu
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    volumes:
      - postgres_data:/var/lib/postgresql/data
 
volumes:
  postgres_data:
# ์ด๋ฏธ์ง€ ๋นŒ๋“œ ๋ฐ ์‹คํ–‰
docker build -t youngsu-community .
docker run -p 3000:3000 --env-file .env.production youngsu-community
 
# ๋˜๋Š” compose๋กœ
docker-compose up -d

๐Ÿ–ฅ๏ธ Node.js ์„œ๋ฒ„ ์ง์ ‘ ๋ฐฐํฌ ๐ŸŸก

๊ธฐ์กด EC2, NCP ๊ฐ™์€ VM์— ์ง์ ‘ ๋ฐฐํฌํ•˜๋Š” ๋ฐฉ๋ฒ•์ด์•ผ.

# server.sh โ€” ์„œ๋ฒ„ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ
#!/bin/bash
set -e
 
echo "๐Ÿ“ฆ ์˜์กด์„ฑ ์„ค์น˜..."
npm ci
 
echo "๐Ÿ”จ ๋นŒ๋“œ..."
npm run build
 
echo "๐Ÿ”„ ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ (PM2 ์‚ฌ์šฉ)..."
pm2 restart youngsu-community || pm2 start npm --name youngsu-community -- start
 
echo "โœ… ๋ฐฐํฌ ์™„๋ฃŒ!"
// ecosystem.config.js โ€” PM2 ์„ค์ •
module.exports = {
  apps: [
    {
      name: 'youngsu-community',
      script: 'npm',
      args: 'start',
      instances: 'max',    // CPU ์ฝ”์–ด ์ˆ˜๋งŒํผ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰
      exec_mode: 'cluster',
      env: {
        NODE_ENV: 'production',
        PORT: 3000,
      },
    },
  ],
}

โš™๏ธ ํ™˜๊ฒฝ ๋ณ€์ˆ˜์™€ ๋น„๋ฐ€ํ‚ค ๊ด€๋ฆฌ ๐Ÿ”ด

๐ŸŽฏ ์ด ์„น์…˜์„ ์ฝ๊ณ  ๋‚˜๋ฉด:

  • NEXT_PUBLIC_ ์ ‘๋‘์‚ฌ์˜ ์˜๋ฏธ์™€ ์œ„ํ—˜์„ฑ์„ ์ดํ•ดํ•œ๋‹ค
  • ๋น„๋ฐ€ํ‚ค๊ฐ€ ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์— ํฌํ•จ๋˜๋Š” ์‚ฌ๊ณ ๋ฅผ ๋ฐฉ์ง€ํ•˜๋Š” ๊ทœ์น™์„ ์•ˆ๋‹ค

๐Ÿค” ์ž ๊น, ๋จผ์ € ์ƒ๊ฐํ•ด๋ด
NEXT_PUBLIC_DATABASE_URL=postgresql://...์ด๋ผ๊ณ  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•˜๋ฉด ์–ด๋–ค ์ผ์ด ์ƒ๊ธธ๊นŒ?

ํ™˜๊ฒฝ ๋ณ€์ˆ˜์˜ ๋‘ ์ข…๋ฅ˜:

NEXT_PUBLIC_ ์ ‘๋‘์‚ฌ ์žˆ์Œ โ†’ ํด๋ผ์ด์–ธํŠธ(๋ธŒ๋ผ์šฐ์ €) ๋ฒˆ๋“ค์— ํฌํ•จ
                           โ†’ ๋ˆ„๊ตฌ๋‚˜ DevTools์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Œ โš ๏ธ

NEXT_PUBLIC_ ์ ‘๋‘์‚ฌ ์—†์Œ โ†’ ์„œ๋ฒ„์—์„œ๋งŒ ์ ‘๊ทผ ๊ฐ€๋Šฅ
                           โ†’ ๋ธŒ๋ผ์šฐ์ €์— ์ ˆ๋Œ€ ๋…ธ์ถœ ์•ˆ ๋จ โœ…
# .env.local (๋กœ์ปฌ ์ „์šฉ โ€” .gitignore์— ๋ฐ˜๋“œ์‹œ ํฌํ•จ!)
DATABASE_URL=postgresql://localhost:5432/dev     # ์„œ๋ฒ„ ์ „์šฉ โœ…
SESSION_SECRET=dev-secret-key                    # ์„œ๋ฒ„ ์ „์šฉ โœ…
NEXT_PUBLIC_API_URL=http://localhost:3000         # ํด๋ผ์ด์–ธํŠธ ์ ‘๊ทผ ๊ฐ€๋Šฅ (๊ณต๊ฐœ OK)
 
# โŒ ์ ˆ๋Œ€ ๊ธˆ์ง€!
NEXT_PUBLIC_DATABASE_URL=postgresql://...        # DB ์ ‘์† ์ •๋ณด๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ๋…ธ์ถœ!
NEXT_PUBLIC_SESSION_SECRET=secret-key            # ์•”ํ˜ธํ™” ํ‚ค๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ๋…ธ์ถœ!

server-only ํŒจํ‚ค์ง€๋กœ ์‹ค์ˆ˜ ๋ฐฉ์ง€:

// lib/db.ts โ€” ์„œ๋ฒ„์—์„œ๋งŒ ์‹คํ–‰๋˜๋Š” ํŒŒ์ผ
import 'server-only'
import { PrismaClient } from '@prisma/client'
 
// ์ด ํŒŒ์ผ์ด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ import๋˜๋ฉด ๋นŒ๋“œ ์—๋Ÿฌ ๋ฐœ์ƒ
// โ†’ ์‹ค์ˆ˜๋กœ DB ์—ฐ๊ฒฐ ์ •๋ณด๊ฐ€ ํด๋ผ์ด์–ธํŠธ์— ๋…ธ์ถœ๋˜๋Š” ์‚ฌ๊ณ  ๋ฐฉ์ง€
export const db = new PrismaClient()

.env ํŒŒ์ผ ๊ณ„์ธต:

.env              โ†’ ๊ธฐ๋ณธ๊ฐ’ (git ์ถ”์  ๊ฐ€๋Šฅ, ๋ฏผ๊ฐ ์ •๋ณด ์—†์–ด์•ผ ํ•จ)
.env.local        โ†’ ๋กœ์ปฌ ๊ฐœ๋ฐœ (git ์ œ์™ธ, ๊ฐœ์ธ ์„ค์ •)
.env.development  โ†’ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ (git ์ถ”์  ๊ฐ€๋Šฅ)
.env.production   โ†’ ํ”„๋กœ๋•์…˜ (git ์ œ์™ธ! ๋˜๋Š” ๋น„๋ฐ€ ๊ด€๋ฆฌ ์„œ๋น„์Šค๋กœ)

๐Ÿ’ฅ ์—๋Ÿฌ ํ•ด๊ฒฐ ์นดํƒˆ๋กœ๊ทธ


โŒ Docker ๋นŒ๋“œ ํ›„ output: 'standalone' ๊ฒฐ๊ณผ๋ฌผ์— public ํŒŒ์ผ ์—†์Œ

์›์ธ: standalone ๋ชจ๋“œ๋Š” public ํด๋”๋ฅผ ์ž๋™์œผ๋กœ ํฌํ•จํ•˜์ง€ ์•Š์•„.

ํ•ด๊ฒฐ์ฑ…:

# Dockerfile์—์„œ public ํด๋”๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ณต์‚ฌ
COPY --from=builder /app/public ./public

โŒ ๋ฐฐํฌ ํ›„ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋ชป ์ฝ์Œ (undefined)

์›์ธ ์ฒดํฌ๋ฆฌ์ŠคํŠธ:

  1. .env.local์€ ๋กœ์ปฌ ์ „์šฉ. ์„œ๋ฒ„์—์„œ๋Š” ์‹œ์Šคํ…œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋˜๋Š” .env.production ์‚ฌ์šฉ
  2. NEXT_PUBLIC_ ์—†๋Š” ๋ณ€์ˆ˜๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ฝ”๋“œ์—์„œ ์ฝ์œผ๋ ค ํ•œ ๊ฒฝ์šฐ
  3. Docker ์‹คํ–‰ ์‹œ --env-file ์˜ต์…˜ ๋ˆ„๋ฝ

โŒ Vercel ๋ฐฐํฌ ํ›„ API Route๊ฐ€ 504 Timeout

์›์ธ: Vercel์˜ ๋ฌด๋ฃŒ ํ”Œ๋žœ์—์„œ ํ•จ์ˆ˜ ์‹คํ–‰ ์‹œ๊ฐ„ ํ•œ๋„(10์ดˆ) ์ดˆ๊ณผ.

ํ•ด๊ฒฐ์ฑ…:

// vercel.json์—์„œ ํŠน์ • ๊ฒฝ๋กœ ํƒ€์ž„์•„์›ƒ ์—ฐ์žฅ (Pro ํ”Œ๋žœ ํ•„์š”)
{
  "functions": {
    "app/api/heavy-task/route.ts": {
      "maxDuration": 60
    }
  }
}

๐Ÿ ์ด๋ฒˆ์— ๋ฐฐ์šด ๋‚ด์šฉ ์ด์ •๋ฆฌ

๐Ÿ“‹ ๋ฐฐํฌ ์ „๋žต ์„ ํƒ ๊ธฐ์ค€

์ƒํ™ฉ๊ถŒ์žฅ ๋ฐฐํฌ ์ „๋žต
์Šคํƒ€ํŠธ์—…, ์ดˆ๊ธฐ ์ถœ์‹œ, ์†Œ๊ทœ๋ชจ ํŒ€Vercel
๊ธฐ์—… ๋‚ด๋ถ€ ์„œ๋ฒ„, ๋ฐ์ดํ„ฐ ๊ตญ๋‚ด ๋ณด๊ด€ ํ•„์š”Docker + ์‚ฌ๋‚ด ์„œ๋ฒ„
๊ธฐ์กด VM ๋ณด์œ , ๋‹จ์ˆœ ๋ฐฐํฌNode.js ์ง์ ‘ (PM2)

โš ๏ธ ์ ˆ๋Œ€ ํ•˜์ง€ ๋ง ๊ฒƒ

์ƒํ™ฉโŒ ๋‚˜์œ ์˜ˆโœ… ์ข‹์€ ์˜ˆ
DB ์—ฐ๊ฒฐ ๋ฌธ์ž์—ดNEXT_PUBLIC_DATABASE_URLDATABASE_URL (์„œ๋ฒ„ ์ „์šฉ)
๋น„๋ฐ€ํ‚ค git ์ถ”์ .env.production git commitVercel ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋˜๋Š” AWS Secrets Manager
Docker์—์„œ root ์‹คํ–‰๊ธฐ๋ณธ root userUSER nextjs ์„ค์ •

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

Q1. NEXT_PUBLIC_SESSION_SECRET=abc123 ์œผ๋กœ ์„ค์ •ํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋Š”?

  • A) ์„œ๋ฒ„์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์ฝ์ง€ ๋ชปํ•œ๋‹ค
  • B) ๋ธŒ๋ผ์šฐ์ € DevTools์—์„œ ์ด ๊ฐ’์„ ๋ˆ„๊ตฌ๋‚˜ ๋ณผ ์ˆ˜ ์žˆ๋‹ค
  • C) Next.js ๋นŒ๋“œ๊ฐ€ ์‹คํŒจํ•œ๋‹ค
  • D) ์ฟ ํ‚ค๊ฐ€ ์ž๋™์œผ๋กœ ์ดˆ๊ธฐํ™”๋œ๋‹ค

โœ… ์ •๋‹ต: B

ํ•ด์„ค: NEXT_PUBLIC_ ์ ‘๋‘์‚ฌ๋Š” ํด๋ผ์ด์–ธํŠธ ๋ฒˆ๋“ค์— ๊ฐ’์„ ํฌํ•จ์‹œ์ผœ. ๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์—์„œ ๋ˆ„๊ตฌ๋‚˜ ํ™•์ธ ๊ฐ€๋Šฅํ•ด. ์„ธ์…˜ ๋น„๋ฐ€ํ‚ค๊ฐ€ ๋…ธ์ถœ๋˜๋ฉด ๋ชจ๋“  JWT๋ฅผ ์œ„์กฐํ•  ์ˆ˜ ์žˆ์–ด.

๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: NEXT_PUBLIC_ = "๊ณต๊ฐœ ๋ฐฉ์†ก". ๋น„๋ฐ€ํ‚ค์—” ์ ˆ๋Œ€ ๋ถ™์ด์ง€ ๋งˆ.


Q2. Docker output: 'standalone'์˜ ์žฅ์ ์œผ๋กœ ์˜ณ์ง€ ์•Š์€ ๊ฒƒ์€?

  • A) ๋ถˆํ•„์š”ํ•œ node_modules๋ฅผ ํฌํ•จํ•˜์ง€ ์•Š์•„ ์ด๋ฏธ์ง€ ํฌ๊ธฐ๊ฐ€ ์ž‘์•„์ง„๋‹ค
  • B) ์‹คํ–‰์— ํ•„์š”ํ•œ ์ตœ์†Œ ํŒŒ์ผ๋งŒ ํฌํ•จ๋œ๋‹ค
  • C) public ํด๋”๊ฐ€ ์ž๋™์œผ๋กœ ํฌํ•จ๋œ๋‹ค
  • D) ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ์™€ ํ•จ๊ป˜ ์“ฐ๋ฉด ์ด๋ฏธ์ง€ ํฌ๊ธฐ๋ฅผ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค

โœ… ์ •๋‹ต: C

ํ•ด์„ค: standalone ๋ชจ๋“œ๋Š” public ํด๋”๋ฅผ ์ž๋™์œผ๋กœ ํฌํ•จํ•˜์ง€ ์•Š์•„. Dockerfile์—์„œ ๋ช…์‹œ์ ์œผ๋กœ COPY --from=builder /app/public ./public์„ ํ•ด์ค˜์•ผ ํ•ด.

๐Ÿ“Œ ํ•ต์‹ฌ ๊ธฐ์–ต๋ฒ•: standalone์€ standalone์ด์•ผ. public์€ ๋ณ„๋„๋กœ ์ฑ™๊ฒจ์•ผ ํ•ด.


Q3. ์นœ๊ตฌ์—๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค๋ฉด?

์Šคํƒ€ํŠธ์—… ์ดˆ๊ธฐ์— Vercel์„ ์„ ํƒํ•˜๊ณ , ๋‚˜์ค‘์— Docker๋กœ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜๋Š” ์ด์œ ๋ฅผ ์„ค๋ช…ํ•ด๋ด.

์˜ˆ์‹œ ๋‹ต๋ณ€:

"Vercel์€ ๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ์ž…์ ์ด์•ผ. ์ฒ˜์Œ์—” ๋น ๋ฅด๊ณ  ํŽธํ•ด. ๊ทผ๋ฐ ํŠธ๋ž˜ํ”ฝ์ด ๋Š˜๋ฉด ์ˆ˜์ˆ˜๋ฃŒ(๋น„์šฉ)๊ฐ€ ๊ธฐํ•˜๊ธ‰์ˆ˜์ ์œผ๋กœ ๋Š˜์–ด. ๊ทธ๋ฆฌ๊ณ  '์šฐ๋ฆฌ ์„œ๋ฒ„์— ์ €์žฅํ•ด์•ผ ํ•œ๋‹ค'๋Š” ๊ธฐ์—… ๊ทœ์ •์ด ์ƒ๊ธฐ๋ฉด Vercel์€ ๋ฏธ๊ตญ ์„œ๋ฒ„๋ผ ๋ถˆ๊ฐ€๋Šฅํ•ด. ๊ทธ๋•Œ Docker๋กœ ์ž์ฒด ์„œ๋ฒ„์— ์˜ฎ๊ธฐ๋Š” ๊ฑฐ์•ผ. ์ดˆ๊ธฐ์—” ์†๋„, ๋‚˜์ค‘์—” ์ œ์–ด๊ถŒ์ด ๋” ์ค‘์š”ํ•ด์ง€๋Š” ์„ฑ์žฅ ๋‹จ๊ณ„์— ๋”ฐ๋ฅธ ์ „๋žต ๋ณ€ํ™”์•ผ."


๐Ÿฃ ์˜์ฒ ์ด์˜ ํ‡ด๊ทผ ์ผ๊ธฐ

์˜ค๋Š˜์€ ์ •๋ง ๋„ฅ์ŠคํŠธ ์•ฑ์ด ์„ธ์ƒ์œผ๋กœ ๋‚˜๊ฐ€๋Š” ๊ด€๋ฌธ์ธ 'Deployment & Infra' ๋ฅผ ๋ฐฐ์šฐ๋ฉด์„œ ๊ฐ€์Šด์ด ๋ฒ…์ฐจ์˜ฌ๋ž์–ด! ๋‹จ์ˆœํžˆ ์ฝ”๋“œ๋ฅผ ์งœ๋Š” ๊ฑธ ๋„˜์–ด, Docker์˜ ๋ฉ€ํ‹ฐ ์Šคํ…Œ์ด์ง€ ๋นŒ๋“œ๋กœ ์ด๋ฏธ์ง€๋ฅผ ๊นŽ๊ณ  Vercel๊ณผ ์ž์ฒด ์„œ๋ฒ„ ์‚ฌ์ด์˜ ์ „๋žต์„ ๊ณ ๋ฏผํ•˜๋Š” ๊ณผ์ •์ด ์ •๋ง ํฅ๋ฏธ๋กœ์› ์–ด.

๐Ÿ’ก ์˜ค๋Š˜์˜ ๊ตํ›ˆ: "๋ฐฐํฌ๋Š” ๋์ด ์•„๋‹ˆ๋ผ ์ƒˆ๋กœ์šด ์‹œ์ž‘์ด๋‹ค. Vercel๋กœ ๋น ๋ฅด๊ฒŒ ์ถœ์‹œํ•˜๊ณ , ์„ฑ์žฅ ๋‹จ๊ณ„์— ๋งž์ถฐ Docker์™€ ์ž์ฒด ์„œ๋ฒ„๋กœ ํ™•์žฅํ•˜๋ฉฐ ์ „ ์„ธ๊ณ„ ์œ ์ €์—๊ฒŒ ์•ˆ์ •์ ์ธ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜์ž!"

์˜ํ˜ธ ๋ฆฌ๋“œ ๋‹˜์ด ๋ฐฐ๋‹ฌ ์•ฑ ๋น„์œ ๋ฅผ ๋“ค์–ด ์„ค๋ช…ํ•ด ์ฃผ์‹ค ๋•Œ, ์™œ ์ƒํ™ฉ์— ๋งž๋Š” ๋ฐฐํฌ ์ „๋žต์ด ์ค‘์š”ํ•œ์ง€ ๋‹จ๋ฒˆ์— ์ดํ•ด๊ฐ€ ๊ฐ€๋”๋ผ. ํŠนํžˆ ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ํ•˜๋‚˜์— ์„œ๋น„์Šค์˜ ์šด๋ช…์ด ๊ฐˆ๋ฆด ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ง์”€์— ์ •์‹ ์ด ๋ฒˆ์ฉ ๋“ค์—ˆ์–ด. ์˜ค๋Š˜ ๋„ˆ๋ฌด ๊ธด์žฅํ•˜๋ฉด์„œ ์„œ๋ฒ„ ์„ค์ •์„ ๋งŒ์กŒ๋”๋‹ˆ ๋ฐฐ๊ฐ€ ์ถœ์ถœํ•˜๋„ค. ํ‡ด๊ทผ๊ธธ์— ๋‚ด๊ฐ€ ์ข‹์•„ํ•˜๋Š” '๋ฐฐ๋‹ฌ ์Œ์‹' ํ•˜๋‚˜ ์‹œ์ผœ์„œ ๋ง›์žˆ๊ฒŒ ๋จน์œผ๋ฉด์„œ ์˜ค๋Š˜ ๋ฐฐ์šด ๋ฐฐํฌ ๋กœ์ง์„ ๋ณต์Šตํ•ด์•ผ๊ฒ ์–ด. ๋‚ด์ผ์€ ๋” '์•ˆ์ •์ ์ธ' ์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋  ๊ฑฐ์•ผ! ๐Ÿฃ


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