๋ค์ด๊ฐ๋ฉฐ
Next.js 16์ด ์ถ์๋๋ฉด์ Turbopack์ด ๊ธฐ๋ณธ ๋ฒ๋ค๋ฌ๋ก ์๋ฆฌ ์ก์์ต๋๋ค. ํ์ง๋ง pnpm ๋ชจ๋ ธ๋ ํฌ ํ๊ฒฝ์์ Docker ์ด๋ฏธ์ง๋ฅผ ๋น๋ํ๋ ค๋ค ๋ณด๋ฉด ์์์น ๋ชปํ ๋ณต๋ณ๋ค์ ๋ง๋๊ฒ ๋ฉ๋๋ค.
์ด ๊ธ์ ์ ๊ฐ ์ค์ ๋ก ๊ฒช์๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐ ๊ณผ์ ์ ์์ธํ ๊ธฐ๋กํ ๊ฒ์ ๋๋ค. ๋จ์ํ ํํ ๋ฆฌ์ผ์ด ์๋, ์ ์ด๋ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๊ณ , ์ด๋ป๊ฒ ํด๊ฒฐํ๋์ง์ ์ด์ ์ ๋ง์ถฐ ์์ฑํ์ต๋๋ค.
๐ฏ ๋ชฉํ
pnpm workspace ๋ชจ๋ ธ๋ ํฌ ๊ตฌ์กฐ์์ Next.js 16 ํ๋ก ํธ์๋๋ฅผ Docker๋ก ๋ฐฐํฌํ ์ ์๋ Dockerfile ์์ฑ
ํ๋ก์ ํธ ๊ตฌ์กฐ:
web10-beastcamp/
โโโ pnpm-workspace.yaml
โโโ package.json
โโโ frontend/
โ โโโ package.json
โ โโโ next.config.ts
โโโ backend/
โ โโโ api-server/
โ โโโ ticket-server/
โโโ packages/
โโโ shared-types/
1๋จ๊ณ: ์ฒซ ์๋ - Nginx ๊ธฐ๋ฐ ์ ๊ทผ
์ฒ์์๋ Next.js๋ฅผ ์ ์ ์ฌ์ดํธ๋ก ๋น๋ํด์ Nginx๋ก ์๋นํ๋ ์ ํต์ ์ธ ๋ฐฉ์์ ์๋ํ์ต๋๋ค.
# โ ์ด๊ธฐ ๋ฒ์ (์๋ํ์ง ์์)
FROM node:22-alpine AS builder
WORKDIR /app
COPY . .
RUN npm install -g pnpm
RUN pnpm install
RUN pnpm build
FROM nginx:alpine
COPY --from=builder /app/frontend/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
๐จ ์ฒซ ๋ฒ์งธ ๋ฒฝ: ํ์ผ์ ์ฐพ์ ์ ์๋ค
$ docker build -f frontend/Dockerfile -t frontend .
[+] Building 2.3s (8/10)
=> ERROR [deps 4/7] COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
------
> [deps 4/7] COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
------
failed to compute cache key: "/pnpm-workspace.yaml" not found
์์ธ: Dockerfile์ด frontend/ ๋๋ ํ ๋ฆฌ์ ์์ง๋ง, ๋น๋์ ํ์ํ ํ์ผ๋ค์ด ์์ ๋๋ ํ ๋ฆฌ(๋ฃจํธ)์ ์์ด์ ์ ๊ทผํ ์ ์์์ต๋๋ค.
ํด๊ฒฐ: Docker build context๋ฅผ ํ๋ก์ ํธ ๋ฃจํธ๋ก ์ค์ ํ๊ณ , Dockerfile ๊ฒฝ๋ก๋ฅผ -f ํ๋๊ทธ๋ก ์ง์ ํฉ๋๋ค.
# โ
์ฌ๋ฐ๋ฅธ ๋น๋ ๋ช
๋ น
docker build -f frontend/Dockerfile -t frontend .
# ^ ๋ฃจํธ์์ ์คํ
2๋จ๊ณ: pnpm workspace ์์กด์ฑ ์ค์น
๋ชจ๋
ธ๋ ํฌ์์๋ workspace ๊ฐ ์์กด์ฑ์ด ์๊ธฐ ๋๋ฌธ์, ๋จ์ํ pnpm install๋ง์ผ๋ก๋ ๋ถ์กฑํฉ๋๋ค.
# Stage 1: Dependencies
FROM node:22-alpine AS deps
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# ๋ฃจํธ workspace ํ์ผ๋ค์ ๋จผ์ ๋ณต์ฌ
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
COPY packages ./packages
COPY frontend/package.json ./frontend/
# --filter ํ๋๊ทธ๋ก frontend์ ๊ทธ ์์กด์ฑ๋ง ์ค์น
RUN pnpm install --frozen-lockfile --filter frontend...
๐ก ํต์ฌ ํฌ์ธํธ
--filter frontend...: frontend ํจํค์ง์ ๊ทธ๊ฒ์ด ์์กดํ๋ ๋ชจ๋ workspace ํจํค์ง๋ฅผ ์ค์นํฉ๋๋ค--frozen-lockfile: lock ํ์ผ ๋ณ๊ฒฝ์ ๋ฐฉ์งํ์ฌ ์ผ๊ด๋ ๋น๋๋ฅผ ๋ณด์ฅํฉ๋๋คpackages๋๋ ํ ๋ฆฌ๋ฅผ ๋จผ์ ๋ณต์ฌํด์ผ workspace ์์กด์ฑ ํด์์ด ๊ฐ๋ฅํฉ๋๋ค
3๋จ๊ณ: Next.js 16 + Turbopack์ ์ถฉ๊ฒฉ
๋น๋๊ฐ ์์๋๋๊ฐ ์ถ๋๋, ์๋ก์ด ์๋ฌ๊ฐ ๋ฑ์ฅํ์ต๋๋ค.
$ pnpm build
Error: Next.js inferred your workspace root at /app, but it may not be correct.
We couldn't find the Next.js package (next/package.json).
Please configure `turbopack.root` in your next.config.js to point to your project root.
๐ ๋ฌธ์ ๋ถ์
Next.js 16๋ถํฐ Turbopack์ด ๊ธฐ๋ณธ ๋ฒ๋ค๋ฌ๊ฐ ๋๋ฉด์, workspace root๋ฅผ ์๋์ผ๋ก ์ถ๋ก ํ๋ ค๊ณ ์๋ํฉ๋๋ค. ํ์ง๋ง pnpm์ ํน์ํ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ(์ฌ๋ณผ๋ฆญ ๋งํฌ ๊ธฐ๋ฐ)์ ์ถฉ๋ํ๋ฉด์ next/package.json์ ์ฐพ์ง ๋ชปํฉ๋๋ค.
pnpm์ node_modules ๊ตฌ์กฐ:
node_modules/
โโโ .pnpm/ # ์ค์ ํจํค์ง๋ค์ด ์ ์ฅ๋๋ store
โ โโโ next@16.1.1/
โ โโโ react@19.2.3/
โโโ next -> .pnpm/next@16.1.1/node_modules/next # ์ฌ๋ณผ๋ฆญ ๋งํฌ
Turbopack์ ์ด ์ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ์ ๋๋ก ๋ฐ๋ผ๊ฐ์ง ๋ชปํ๊ณ ์์์ต๋๋ค.
โ ํด๊ฒฐ ๋ฐฉ๋ฒ: turbopack.root ์ค์
next.config.ts์ ๋ช
์์ ์ผ๋ก workspace root๋ฅผ ์ง์ ํฉ๋๋ค.
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
turbopack: {
root: "..", // ์์ ๋๋ ํ ๋ฆฌ๋ฅผ workspace root๋ก ์ง์
},
reactCompiler: true,
output: "standalone",
};
export default nextConfig;
์ด์ Turbopack์ด ์ฌ๋ฐ๋ฅธ ์์น์์ ํจํค์ง๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค.
4๋จ๊ณ: Standalone ๋ชจ๋์ ์ต์ ํ
Nginx ๋์ Next.js์ standalone ๋ชจ๋๋ฅผ ์ฌ์ฉํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
์ Standalone์ธ๊ฐ?
- Server-Side Rendering(SSR) ์ง์: Nginx๋ ์ ์ ํ์ผ๋ง ์๋นํ ์ ์์ง๋ง, standalone์ SSR๊ณผ API Routes๋ฅผ ์ง์ํฉ๋๋ค
- ์ต์ ๋ฒ๋ค ํฌ๊ธฐ: ํ์ํ ์์กด์ฑ๋ง ํฌํจ๋ ๊ฒฝ๋ ๋น๋๋ฅผ ์์ฑํฉ๋๋ค
- self-contained: Node.js๋ง ์์ผ๋ฉด ์คํ ๊ฐ๋ฅํ ๋ ๋ฆฝ์ ์ธ ์๋ฒ์ ๋๋ค
// next.config.ts
const nextConfig: NextConfig = {
output: "standalone", // ์ด ํ ์ค์ด ๋ง๋ฒ์ ์ผ์ผํต๋๋ค
};
Standalone ๋น๋๊ฐ ์์ฑํ๋ ๊ฒ
frontend/.next/
โโโ standalone/ # ์คํ ๊ฐ๋ฅํ ์๋ฒ
โ โโโ server.js # ์ง์
์
โ โโโ package.json
โ โโโ node_modules/ # ํ์ํ ์์กด์ฑ๋ง (์ฌ๋ณผ๋ฆญ ๋งํฌ)
โโโ static/ # ์ ์ ์์ฐ๋ค
5๋จ๊ณ: ์ต์ข Dockerfile - Multi-stage Build
์ด์ ๋ชจ๋ ํผ์ฆ ์กฐ๊ฐ์ ๋ง์ถฐ๋ด ์๋ค.
# Stage 1: Dependencies
FROM node:22-alpine AS deps
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# Copy root workspace files
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml ./
COPY packages ./packages
# Copy frontend package.json
COPY frontend/package.json ./frontend/
# Install dependencies
RUN pnpm install --frozen-lockfile --filter frontend...
# Stage 2: Builder
FROM node:22-alpine AS builder
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# Build arguments for Next.js environment variables
ARG NEXT_PUBLIC_API_URL
ARG NEXT_PUBLIC_API_MODE
# Copy dependencies from deps stage
COPY --from=deps /app/node_modules ./node_modules
COPY --from=deps /app/frontend/node_modules ./frontend/node_modules
COPY --from=deps /app/packages ./packages
# Copy source files
COPY frontend ./frontend
COPY pnpm-workspace.yaml package.json ./
# Build application
WORKDIR /app/frontend
RUN pnpm build
# Stage 3: Production with Node.js (for standalone Next.js)
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# standalone์ ํ์ํ ๋ชจ๋ ์์กด์ฑ์ ์ด๋ฏธ ํฌํจํ๊ณ ์์
# ์ ์ฒด standalone ๊ตฌ์กฐ๋ฅผ ๋ณต์ฌ
COPY --from=builder --chown=nextjs:nodejs /app/frontend/.next/standalone ./
# static ํ์ผ๋ค์ ์ฌ๋ฐ๋ฅธ ์ค์ฒฉ ๊ฒฝ๋ก์ ๋ณต์ฌ
COPY --from=builder --chown=nextjs:nodejs /app/frontend/.next/static ./frontend/.next/static
COPY --from=builder --chown=nextjs:nodejs /app/frontend/public ./frontend/public
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
# frontend ๋๋ ํ ๋ฆฌ๋ก ์ด๋ํด์ server.js ์คํ
WORKDIR /app/frontend
CMD ["node", "server.js"]
๐จ ์ํคํ ์ฒ ์ค๋ช
Stage 1 (deps): ์์กด์ฑ๋ง ์ค์น
- ๋น๋ ์บ์ฑ ์ต์ ํ: package.json์ด ๋ณ๊ฒฝ๋์ง ์์ผ๋ฉด ์ด ๋ ์ด์ด๋ฅผ ์ฌ์ฌ์ฉ
--filter frontend...๋ก ํ์ํ ํจํค์ง๋ง ์ค์น
Stage 2 (builder): ์ ํ๋ฆฌ์ผ์ด์ ๋น๋
- ์์ค ์ฝ๋ ๋ณ๊ฒฝ์ด ์์กด์ฑ ์ค์น์ ์ํฅ์ ์ฃผ์ง ์์
ARG๋ก ๋น๋ ํ์ ํ๊ฒฝ๋ณ์ ์ฃผ์ ๊ฐ๋ฅ
Stage 3 (runner): ํ๋ก๋์ ์คํ
- ๋น๋ ์ฐ์ถ๋ฌผ๋ง ๋ณต์ฌํ์ฌ ์ด๋ฏธ์ง ํฌ๊ธฐ ์ต์ํ
- non-root ์ฌ์ฉ์(nextjs)๋ก ์คํํ์ฌ ๋ณด์ ๊ฐํ
- standalone ์ถ๋ ฅ๋ฌผ์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ์ ์ง
6๋จ๊ณ: ๋น๋ ์ต์ ํ - .dockerignore
๋ถํ์ํ ํ์ผ๋ค์ ์ ์ธํ์ฌ ๋น๋ ์๋๋ฅผ ๋์ ๋๋ค.
# .dockerignore
node_modules
.next
.git
.gitignore
README.md
.env*.local
.DS_Store
*.md
์คํ ๋ฐ ๊ฒ์ฆ
๋น๋
docker build -f frontend/Dockerfile -t beastcamp-frontend .
์คํ
docker run -d \
--name frontend \
-p 3000:3000 \
-e NEXT_PUBLIC_API_URL=https://api.example.com \
beastcamp-frontend
๊ฒ์ฆ
$ docker logs -f frontend
โฒ Next.js 16.1.1
- Local: http://localhost:3000
- Network: http://0.0.0.0:3000
โ Starting...
โ Ready in 89ms
$ curl -I http://localhost:3000
HTTP/1.1 200 OK
x-powered-by: Next.js
์ฃผ์ ๊ตํ (Key Takeaways)
1. Turbopack๊ณผ pnpm์ ๊ถํฉ ๋ฌธ์
- Next.js 16์ Turbopack์ pnpm์ ์ฌ๋ณผ๋ฆญ ๋งํฌ ๊ตฌ์กฐ๋ฅผ ์๋ฒฝํ ์ง์ํ์ง ๋ชปํฉ๋๋ค
turbopack.root์ค์ ์ผ๋ก workspace root๋ฅผ ๋ช ์ํ๋ฉด ํด๊ฒฐ๋ฉ๋๋ค
2. Standalone ๋ชจ๋์ ์ฅ์
- SSR/ISR์ ์ง์ํ๋ ์์ ํ ์๋ฒ
- ์ต์ํ์ ์์กด์ฑ๋ง ํฌํจ๋์ด ์ด๋ฏธ์ง ํฌ๊ธฐ ์ต์ํ
- Nginx ์ค์ ์์ด ๋ฐ๋ก ํ๋ก๋์ ๋ฐฐํฌ ๊ฐ๋ฅ
3. Multi-stage Build์ ์ค์์ฑ
- ์์กด์ฑ ์ค์น์ ๋น๋๋ฅผ ๋ถ๋ฆฌํ์ฌ ์บ์ฑ ํจ์จ ๊ทน๋ํ
- ์ต์ข ์ด๋ฏธ์ง์๋ ์คํ์ ํ์ํ ํ์ผ๋ง ํฌํจ
4. pnpm Workspace ํํฐ๋ง
--filter frontend...๋ ์ธ ๊ฐ์ ์ (...)์ด ํต์ฌ- ํด๋น ํจํค์ง์ ๊ทธ๊ฒ์ด ์์กดํ๋ ๋ชจ๋ workspace ํจํค์ง๋ฅผ ํฌํจ
5. ๋ณด์ ๊ณ ๋ ค์ฌํญ
- non-root ์ฌ์ฉ์๋ก ์ปจํ ์ด๋ ์คํ
- ๋ฏผ๊ฐํ ์ ๋ณด๋ ๋น๋ ์๊ท๋จผํธ๋ ํ๊ฒฝ๋ณ์๋ก ์ฃผ์
.dockerignore๋ก ๋ถํ์ํ ํ์ผ ๋ ธ์ถ ๋ฐฉ์ง
์ฑ๋ฅ ๋ฉํธ๋ฆญ
| ํญ๋ชฉ | ์์น |
|---|---|
| ์ต์ข ์ด๋ฏธ์ง ํฌ๊ธฐ | ~180MB (alpine ๊ธฐ๋ฐ) |
| ๋น๋ ์๊ฐ (์บ์ ์์) | ~3๋ถ |
| ๋น๋ ์๊ฐ (์บ์ ์์) | ~15์ด |
| ์์ ์๊ฐ | ~90ms |
| ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ | ~150MB (์ ํด ์ํ) |
ํธ๋ฌ๋ธ์ํ ๊ฐ์ด๋
Q: "Cannot find module 'next'" ์๋ฌ๊ฐ ๋ฐ์ํฉ๋๋ค
์์ธ: standalone ๋น๋์ ์ฌ๋ณผ๋ฆญ ๋งํฌ๊ฐ ์ ๋๋ก ๋ณต์ฌ๋์ง ์์์ต๋๋ค.
ํด๊ฒฐ:
# standalone ์ ์ฒด ๊ตฌ์กฐ๋ฅผ ๋ณต์ฌ
COPY --from=builder /app/frontend/.next/standalone ./
Q: ๋น๋๋ ๋๋๋ฐ ์คํ ์ 404๊ฐ ๋ฉ๋๋ค
์์ธ: static ํ์ผ๋ค์ด ์ฌ๋ฐ๋ฅธ ๊ฒฝ๋ก์ ๋ณต์ฌ๋์ง ์์์ต๋๋ค.
ํด๊ฒฐ:
# ์ค์ฒฉ๋ ๊ฒฝ๋ก ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ์ ์ง
COPY --from=builder /app/frontend/.next/static ./frontend/.next/static
COPY --from=builder /app/frontend/public ./frontend/public
Q: ํ๊ฒฝ๋ณ์๊ฐ ๋น๋ ํ์์ ์ ์ฉ๋์ง ์์ต๋๋ค
์์ธ: NEXT_PUBLIC_* ๋ณ์๋ ๋น๋ ์์ ์ ๋ฒ๋ค์ ํฌํจ๋ฉ๋๋ค.
ํด๊ฒฐ:
ARG NEXT_PUBLIC_API_URL
# ๋น๋ ์ ์ ๋ฌ: docker build --build-arg NEXT_PUBLIC_API_URL=...
๋ง์น๋ฉฐ
Next.js 16์ Turbopack๊ณผ pnpm ๋ชจ๋ ธ๋ ํฌ๋ฅผ Docker๋ก ๋น๋ํ๋ ๊ณผ์ ์ ์๊ฐ๋ณด๋ค ๋ณต์กํ์ง๋ง, ๊ฐ ๋ฌธ์ ์ ์์ธ์ ์ดํดํ๊ณ ๋๋ ํด๊ฒฐ์ฑ ์ด ๋ช ํํด์ก์ต๋๋ค.
ํต์ฌ์ Turbopack์ workspace root ์ค์ ๊ณผ standalone ๋ชจ๋์ ๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ ์ ์ง์์ต๋๋ค.
์ด ๊ธ์ด ๋น์ทํ ๋ฌธ์ ๋ฅผ ๊ฒช๊ณ ์๋ ๋ถ๋ค์๊ฒ ๋์์ด ๋๊ธธ ๋ฐ๋๋๋ค. ์ง๋ฌธ์ด๋ ๊ฐ์ ์ฌํญ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ง ํผ๋๋ฐฑ ์ฃผ์ธ์!
์ฐธ๊ณ ์๋ฃ
- Next.js Standalone Output
- pnpm Workspace Filtering
- Turbopack Configuration
- Docker Multi-stage Builds
'๐ ํ๋ก ํธ์๋(FE)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| React ์ฑ๋ฅ ์ต์ ํ (0) | 2026.01.20 |
|---|---|
| CSR / SSR / SSG / ISR ๋ ๋๋ง ๋ฐฉ์ ๋น๊ต (1) | 2026.01.17 |
| ์ง๋ ๋ทฐํฌํธ ๊ธฐ๋ฐ ์ค์๊ฐ ์์ ๊ฒ์ ๊ตฌํ (0) | 2026.01.14 |
| ์น ์ ๋๋ฉ์ด์ ์ ๋๋ก ํ์ฉํ๊ธฐ: Transition, Keyframes, rAF, WAAPI ๋น๊ต์ ํ์ฉ๋ฒ (0) | 2025.09.24 |
| Webpack Tree Shaking ๋์ ์๋ฆฌ (0) | 2025.09.17 |