๋ค์ด๊ฐ๋ฉฐ
๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋งํ ๋ ๊ฐ์ฅ ์ด๋ ค์ด ์ ์ "์ ์ด๋ ๊ฒ ๋ง๋ค์ด์ก์๊น?"๋ฅผ ์ดํดํ๋ ๊ฒ์ ๋๋ค. ์ ๊ฐ ๊ฐ๋ฐํ๊ณ ์๋ WMS(Warehouse Management System) ํ๋ก์ ํธ๋ ๋ง์ฐฌ๊ฐ์ง์์ต๋๋ค. Next.js ๊ธฐ๋ฐ ํ๋ก์ ํธ์์๋ ๋ถ๊ตฌํ๊ณ ๋ณ๋์ Express ์ปค์คํ ์๋ฒ๋ฅผ ์ด์ํ๊ณ ์์๋๋ฐ, ์ด๊ธฐ์๋ API ํ๋ก์ ๊ธฐ๋ฅ์ ๋น ๋ฅด๊ฒ ๊ตฌํํ๊ธฐ ์ํ ์ ํ์ด์์ ๊ฒ์ ๋๋ค.
ํ์ง๋ง ์๊ฐ์ด ์ง๋๋ฉฐ ์ด ์ ํ์ ๊ธฐ์ ๋ถ์ฑ๋ก ์์ฌ๊ฐ๊ณ , ๊ฒฐ๊ตญ 1,056์ค์ ๊ฑฐ๋ํ ๋จ์ผ ํ์ผ์ด ๋์ด ์ ์ง๋ณด์์ ๋ฐ๋ชฉ์ ์ก์์ต๋๋ค.
๋ฌธ์ ์ํฉ
1,056์ค์ง๋ฆฌ Express ์๋ฒ์ ํ๊ณ
WMS ํ๋ก ํธ์๋ ํ๋ก์ ํธ๋ Next.js ๊ธฐ๋ฐ์ด์ง๋ง, API ํ๋ก์๋ฅผ ์ํด ๋ณ๋์ Express ์ปค์คํ ์๋ฒ(server/index.js)๋ฅผ ์ฌ์ฉํ๊ณ ์์์ต๋๋ค. ์ด ํ์ผ์ 1,056์ค์ ๋ฌํ๊ณ , ๋ค์๊ณผ ๊ฐ์ ๋ฌธ์ ๋ฅผ ์๊ณ ์์์ต๋๋ค.
- Next.js์ ๋ด์ฅ ๊ธฐ๋ฅ์ ํ์ฉํ์ง ๋ชปํจ: Next.js๋ API Routes, Middleware ๋ฑ ์๋ฒ ์ฌ์ด๋ ๊ธฐ๋ฅ์ ์์ฒด ์ ๊ณตํ์ง๋ง, Express ์ปค์คํ ์๋ฒ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด ๊ธฐ๋ฅ๋ค์ ์จ์ ํ ์ธ ์ ์์ต๋๋ค. Hot Module Replacement(HMR)๋ ์ ๋๋ก ์๋ํ์ง ์์ ๊ฐ๋ฐ ๊ฒฝํ๋ ์ข์ง ์์์ต๋๋ค.
- ๋์ผํ ์ฝ๋์ ๋ฐ๋ณต: GET, POST, PUT, PATCH, DELETE ๊ฐ HTTP ๋ฉ์๋๋ง๋ค ๊ฑฐ์ ๋์ผํ ํ๋ก์ ๋ก์ง์ด ๋ณต์ฌ-๋ถ์ฌ๋ฃ๊ธฐ ์์ค์ผ๋ก ๋ฐ๋ณต๋๊ณ ์์์ต๋๋ค. ํ๋์ ๋ฒ๊ทธ๋ฅผ ์์ ํ๋ ค๋ฉด 5๊ณณ์ ๋ชจ๋ ์ฐพ์ ๊ณ ์ณ์ผ ํ์ต๋๋ค.
- ํ๋ ์์ํฌ ์ด์ค ์์กด: Express์ Next.js ๋ ํ๋ ์์ํฌ๋ฅผ ๋์์ ๊ด๋ฆฌํด์ผ ํ๊ณ , express, multer ๋ฑ Express ์ ์ฉ ์์กด์ฑ์ด ๋ถํ์ํ๊ฒ ํฌํจ๋์ด ์์์ต๋๋ค.
- ๋๋ฒ๊น ์ ์ด๋ ค์: ํน์ api endpoint๋ค์ '/excel'๊ณผ ๊ฐ์ ํ๋์ ๊ฒฝ๋ก๋ก ํ๋ก์ํ์ฌ ๊ฒฝ๋ก๋ฅผ ๊ฐ๋ก์ฑ ์ฒ๋ฆฌํ๋ ๊ฒฝ์ฐ ๋๋ฌธ์, ์ด๋ค ์์ฒญ์ด ์ด๋๋ก ๊ฐ๋์ง ์ถ์ ํ๊ธฐ ์ด๋ ค์ ์ต๋๋ค.
- ๋ฐฐํฌ ๋ณต์ก๋ ์ฆ๊ฐ: Dockerfile์์ server/ ๋๋ ํ ๋ฆฌ๋ฅผ ๋ณ๋๋ก ๋ณต์ฌํด์ผ ํ๊ณ , ์คํ ๋ช ๋ น์ด๋ node server/index.js๋ก Next.js ํ์ค๊ณผ ๋ฌ๋์ต๋๋ค. ์ ๊ท ํ์์ด ํฉ๋ฅํ ๋๋ง๋ค "์ Next.js์ธ๋ฐ Express ์๋ฒ๋ฅผ ๋์์ผ ํ๋์?"๋ผ๋ ์ง๋ฌธ์ ๋ฐ์์ต๋๋ค.
// ๊ธฐ์กด: server/index.js (1,056์ค)
const express = require("express");
const next = require("next");
const multer = require("multer");
app.prepare().then(() => {
const server = express();
// GET, POST, PUT, PATCH, DELETE๋ง๋ค ๊ฑฐ์ ๋์ผํ ํ๋ก์ ์ฝ๋๊ฐ ๋ฐ๋ณต...
server.post("/api/v2/*", async (req, res) => { /* ... */ });
server.patch("/api/v2/*", async (req, res) => { /* ... */ });
server.put("/api/v2/*", async (req, res) => { /* ... */ });
server.delete("/api/v2/*", async (req, res) => { /* ... */ });
server.get("/api/*", async (req, res) => { /* ... */ });
// ...์ด๋ฐ ํจํด์ด ์๋ฐฑ ์ค ๊ณ์๋จ
});
ํด๊ฒฐ ๋ฐฉ์
์ญํ ๋ถ์๋ถํฐ ์์
๋ฆฌํฉํ ๋ง์ ์ฒซ ๊ฑธ์์ ํ์ฌ ์์คํ ์ด ์ค์ ๋ก ๋ฌด์์ ํ๊ณ ์๋์ง ํ์ ํ๋ ๊ฒ์ด์์ต๋๋ค. Express ์ปค์คํ ์๋ฒ๊ฐ ์ํํ๋ ์ญํ ์ ๋ถ์ํ๋ฉด ํฌ๊ฒ ์ธ ๊ฐ์ง์์ต๋๋ค:
- API ํ๋ก์: ํ๋ก ํธ์๋์ API ์์ฒญ์ ๋ฐฑ์๋ ์๋ฒ๋ก ์ ๋ฌ
- ์ธ์ ์ฒดํฌ: ์ฟ ํค ๊ธฐ๋ฐ ์ธ์ฆ ํ์ธ
- ํฌ์ค ์ฒดํฌ: ์ปจํ ์ด๋ ์ํ ํ์ธ ์๋ํฌ์ธํธ
๋ณต์กํด ๋ณด์๋ 1,056์ค์ ์ฝ๋๊ฐ ๊ฒฐ๊ตญ ์ด ์ธ ๊ฐ์ง ๊ธฐ๋ฅ๋ง ์ํํ๊ณ ์์๊ณ , ์ด๋ ๋ชจ๋ Next.js์ ๋ค์ดํฐ๋ธ ๊ธฐ๋ฅ์ผ๋ก ๋์ฒด ๊ฐ๋ฅํ์ต๋๋ค.
| ๊ธฐ์กด (Express) | ๋ณ๊ฒฝ ํ (Next.js) |
| server/index.js (1,056์ค) | pages/api/ ๋๋ ํ ๋ฆฌ์ API Routes |
| Express ๋ฏธ๋ค์จ์ด๋ก ์ธ์ ์ฒดํฌ | middleware.ts |
| server.get("/health-check") | pages/api/health-check.ts |
| multer๋ก ํ์ผ ์ ๋ก๋ ์ฒ๋ฆฌ | formidable + lib/api/fileUploadUtils.ts |
| node server/index.js | next start |
์ด ๋ฐฉ์์ ์ ํํ ์ด์
1. ํ๋ ์์ํฌ ์ผ๊ด์ฑ ํ๋ณด
Next.js ํ๋ก์ ํธ์์ Express๋ฅผ ๋ณ๋๋ก ์ด์ํ๋ ๊ฒ์ ๋ถํ์ํ ๋ณต์ก์ฑ์ ์ถ๊ฐํฉ๋๋ค. Next.js๊ฐ ์ ๊ณตํ๋ API Routes๋ ์๋ฒ๋ฆฌ์ค ํจ์์ฒ๋ผ ๋์ํ๋ฉฐ, ๋ณ๋์ ์๋ฒ ์ค์ ์์ด๋ API ์๋ํฌ์ธํธ๋ฅผ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
- Next.js์ ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ ์์คํ ์ ์ผ๊ด๋๊ฒ ์ฌ์ฉ
- ํ๋ก ํธ์๋์ ๋ฐฑ์๋ ๋ก์ง์ ๋์ผํ ๋น๋ ํ์ดํ๋ผ์ธ์์ ๊ด๋ฆฌ
- Vercel ๋ฑ ์๋ฒ๋ฆฌ์ค ํ๋ซํผ์ผ๋ก์ ๋ฐฐํฌ ์ต์ ํ๋ณด
2. ์ฝ๋ ์ค๋ณต ์ ๊ฑฐ์ ์ ์ง๋ณด์์ฑ ํฅ์
๊ธฐ์กด Express ์๋ฒ์์๋ HTTP ๋ฉ์๋๋ณ๋ก ๊ฑฐ์ ๋์ผํ ํ๋ก์ ๋ก์ง์ด ๋ฐ๋ณต๋๊ณ ์์์ต๋๋ค. ํฉํ ๋ฆฌ ํจํด์ ์ ์ฉํ createProxyHandler ํจ์๋ฅผ ๋์ ํจ์ผ๋ก์จ:
- 1,056์ค์ ์ฝ๋๋ฅผ ์ฝ 550์ค(6๊ฐ ๋ชจ๋)๋ก ์ถ์
- ๊ณตํต ๋ก์ง์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋๋ก ๋ถ๋ฆฌ
- ์๋ก์ด ํ๋ก์ ์๋ํฌ์ธํธ ์ถ๊ฐ ์ ์ค์ ๊ฐ์ฒด๋ง ์์ฑํ๋ฉด ๋๋ ๊ตฌ์กฐ
3. Next.js ์์ฝ์์คํ ๊ณผ์ ํตํฉ
Next.js 13+ ๋ฒ์ ์์ ๋์ ๋ Middleware๋ Edge Runtime์์ ์คํ๋์ด ๋น ๋ฅธ ์๋ต์ด ๊ฐ๋ฅํฉ๋๋ค. ์ธ์ ์ฒดํฌ ๊ฐ์ ์ธ์ฆ ๋ก์ง์ Middleware๋ก ๊ตฌํํ๋ฉด:
- ๋ชจ๋ ํ์ด์ง ์์ฒญ์ ์ผ๊ด๋ ์ธ์ฆ ๋ก์ง ์ ์ฉ
- ํด๋ผ์ด์ธํธ ์์ฒญ์ด ํ์ด์ง ๋ ๋๋ง๊น์ง ๋๋ฌํ๊ธฐ ์ ์ ์กฐ๊ธฐ ์ฐจ๋จ
- Edge์์ ์คํ๋์ด ๋ฎ์ ๋ ์ดํด์
4. ๋ฐฐํฌ ๋ฐ ์ด์ ๋จ์ํ
ํ์ค Next.js ๊ตฌ์กฐ๋ก ์ ํํ๋ฉด์:
- Dockerfile์ด ๋จ์ํด์ ธ ์ ์ง๋ณด์ ๋ถ๋ด ๊ฐ์
- next start ๋ช ๋ น์ด๋ก ํ์คํ๋ ์คํ ๋ฐฉ์
- Next.js ๊ณต์ ๋ฌธ์์ ์ปค๋ฎค๋ํฐ ์๋ฃ ํ์ฉ ๊ฐ๋ฅ
5. ์์กด์ฑ ์ต์ํ
Express์ ๊ด๋ จ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๊ฑฐํจ์ผ๋ก์จ:
- package-lock.json์์ 645์ค ๊ฐ์
- ๋ณด์ ์ทจ์ฝ์ ๊ด๋ฆฌ ๋์ ๊ฐ์
- ๋ฒ๋ค ์ฌ์ด์ฆ ์ต์ ํ
์คํ ๊ณผ์
Step 1: ๊ณตํต ์ ํธ๋ฆฌํฐ ๋ชจ๋ํ
๊ฐ์ฅ ๋จผ์ ํ ์ผ์ ๋ฐ๋ณต๋๋ ์ฝ๋๋ฅผ ์ฐพ์๋ด๋ ๊ฒ์ด์์ต๋๋ค. GET, POST, PUT, PATCH, DELETE ๊ฐ ๋ฉ์๋๋ง๋ค ๊ฑฐ์ ๋์ผํ ๋ก์ง์ด 5๋ฒ ๋ฐ๋ณต๋๊ณ ์์์ต๋๋ค. ์ด๋ฅผ ์ญํ ๋ณ๋ก ๋ถ๋ฆฌํ์ฌ lib/api/ ๋๋ ํ ๋ฆฌ์ ๋ชจ๋ํํ์ต๋๋ค.
lib/api/
โโโ proxyHandler.ts # ํ๋ก์ ํธ๋ค๋ฌ ํฉํ ๋ฆฌ (312์ค)
โโโ headerBuilder.ts # ์์ฒญ ํค๋ ๋น๋
โโโ fileUploadUtils.ts # multipart/form-data ์ฒ๋ฆฌ
โโโ responseFilter.ts # ์๋ต ํํฐ๋ง
โโโ responseUtils.ts # ์๋ต ์ ํธ๋ฆฌํฐ
โโโ serverUtils.ts # URL ๋น๋, ํค๋ ์ ๋ฆฌ
ํต์ฌ์ ํฉํ ๋ฆฌ ํจํด์ ํ์ฉํ createProxyHandler ํจ์์ ๋๋ค. ์ด ํจ์๋ ์ค์ ๊ฐ์ฒด๋ฅผ ๋ฐ์์ API Route ํธ๋ค๋ฌ๋ฅผ ์์ฑํฉ๋๋ค.
์๋ฅผ ๋ค์ด:
- /api/v2/* ๊ฒฝ๋ก๋ GET, POST, PUT, PATCH, DELETE๋ฅผ ๋ชจ๋ ์ง์
- /api/basic/* ๊ฒฝ๋ก๋ GET, POST๋ง ์ง์
- ํ์ผ ์ ๋ก๋๊ฐ ํ์ํ ๊ฒฝ๋ก๋ multipart ์ต์ ํ์ฑํ
๊ธฐ์กด์๋ ์ด๋ฐ ์ฐจ์ด๋ง๋ค ๋ณ๋์ ๋ผ์ฐํธ ํธ๋ค๋ฌ๋ฅผ ์์ฑํด์ผ ํ์ง๋ง, ์ด์ ๋ ์ค์ ๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๋ฉ๋๋ค.
// lib/api/proxyHandler.ts
export const createProxyHandler = (config: ProxyConfig) => {
return async (req: NextApiRequest, res: NextApiResponse) => {
const method = req.method || "GET";
const pathString = extractPath(req);
const backendUrl = buildBackendUrl(url);
const headers = cleanHeaders(req.headers);
// GET, POST, PUT, PATCH, DELETE๋ฅผ ํ๋์ ํธ๋ค๋ฌ์์ ์ฒ๋ฆฌ
if (method === "GET") { /* ... */ }
else if (method === "POST") { /* ... */ }
// ...
};
};
Step 2: API Routes๋ก ์๋ํฌ์ธํธ ์ ํ
Express ๋ผ์ฐํธ๋ฅผ Next.js์ Dynamic API Routes๋ก ์ ํํ์ต๋๋ค. [...path].ts ํจํด์ผ๋ก ํ์ ๊ฒฝ๋ก๋ฅผ ๋ชจ๋ ์บ์นํฉ๋๋ค.
// pages/api/v2/[...path].ts
import { createProxyHandler, apiConfig } from "lib/api/proxyHandler";
export const config = apiConfig; // bodyParser: false
export default createProxyHandler({
pathPrefix: "/api/v2",
logPrefix: "[api/v2]",
allowedMethods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
multipart: true,
});
// pages/api/basic/[...path].ts
import { createProxyHandler, apiConfig } from "lib/api/proxyHandler";
export const config = apiConfig;
export default createProxyHandler({
pathPrefix: "/api/basic",
logPrefix: "[api/basic]",
allowedMethods: ["GET", "POST"],
multipart: true,
});
๋จ 2๊ฐ์ ํ์ผ๋ก ๊ธฐ์กด Express ์๋ฒ์ ์๋ฐฑ ์ค ๋ผ์ฐํ ์ฝ๋๋ฅผ ๋์ฒดํ์ต๋๋ค. ์๋ก์ด ํ๋ก์ ๊ฒฝ๋ก๊ฐ ํ์ํ๋ค๋ฉด ์ค์ ๊ฐ์ฒด๋ง ์์ฑํ์ฌ ์ ํ์ผ์ ๋ง๋ค๋ฉด ๋ฉ๋๋ค. 10์ค ์ด๋ด์ ์ฝ๋๋ก ์์ ํ ๋์ํ๋ ํ๋ก์๊ฐ ์์ฑ๋ฉ๋๋ค.
Step 3: Middleware๋ก ์ธ์ ์ฒดํฌ
Next.js Middleware๋ฅผ ํ์ฉํด ํ์ด์ง ์ ๊ทผ ์ ์ธ์ ์ฟ ํค๋ฅผ ํ์ธํ๋๋ก ํ์ต๋๋ค.
// middleware.ts
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// ๋ก๊ทธ์ธ ํ์ด์ง๋ ํต๊ณผ
if (PUBLIC_PATHS.some((path) => pathname.startsWith(path))) {
return NextResponse.next();
}
// ์ธ์
์ฟ ํค๊ฐ ์์ผ๋ฉด ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธ
const cookieHeader = request.headers.get("cookie") || "";
if (!cookieHeader.includes("CSESSIONID=")) {
const loginUrl = new URL("/auth/login", request.url);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
}
Step 4: ํธํ์ฑ์ ์ํ rewrites ์ค์
๊ธฐ์กด์ /health-check, /session-check ๊ฒฝ๋ก๋ก ์ง์ ์ ๊ทผํ๋ ํด๋ผ์ด์ธํธ/์ธํ๋ผ ์ฝ๋๊ฐ ์์ผ๋ฏ๋ก, next.config.js์ rewrites๋ฅผ ์ถ๊ฐํ์ฌ ๊ธฐ์กด ๊ฒฝ๋ก๋ฅผ ์ ์งํ์ต๋๋ค.
// next.config.js
async rewrites() {
return [
{ source: "/health-check", destination: "/api/health-check" },
{ source: "/session-check", destination: "/api/session-check" },
];
},
Step 5: Webpack ์ค์ ์์
๊ธฐ์กด์ ์๋ฒ/ํด๋ผ์ด์ธํธ ๋น๋๋ฅผ ๊ตฌ๋ถํ์ง ์๊ณ splitChunks ์ต์ ํ๋ฅผ ์ ์ฉํ๊ณ ์์๋๋ฐ, Express ์๋ฒ๊ฐ ์ฌ๋ผ์ง๋ฉด์ ์๋ฒ ๋น๋์์ ๋ธ๋ผ์ฐ์ ์ ์ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(self ์ฐธ์กฐ)๊ฐ ํฌํจ๋์ด ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค. ํด๋ผ์ด์ธํธ ๋น๋์์๋ง ์ ์ฉํ๋๋ก ์์ ํ์ต๋๋ค.
// ๋ณ๊ฒฝ ์
if (!dev) {
config.optimization = { ... };
}
// ๋ณ๊ฒฝ ํ
if (!dev && !isServer) {
config.optimization = { ... };
}
Step 6: ์คํ ์คํฌ๋ฆฝํธ ์ ํ
// package.json
{
"scripts": {
"dev": "NODE_ENV=development next dev", // ๋ณ๊ฒฝ: node server/index.js → next dev
"dev:https": "NODE_ENV=development node scripts/dev-server.js", // ์ ๊ท: HTTPS ๊ฐ๋ฐ์ฉ
"start": "NODE_ENV=production PORT=8080 next start -p 8080" // ๋ณ๊ฒฝ: node server/index.js → next start
}
}
๋ก์ปฌ HTTPS ๊ฐ๋ฐ์ด ํ์ํ ๊ฒฝ์ฐ๋ฅผ ์ํด scripts/dev-server.js๋ฅผ ๋ณ๋๋ก ๋ถ๋ฆฌํ์ต๋๋ค.
Step 7: Dockerfile ๋ฐ ์์กด์ฑ ์ ๋ฆฌ
# ๋ณ๊ฒฝ ์
COPY --from=build /app/server ./server # Express ์๋ฒ ๋๋ ํ ๋ฆฌ ๋ณต์ฌ
CMD ["npm", "start"] # node server/index.js ์คํ
# ๋ณ๊ฒฝ ํ
COPY --from=build /app/.next ./.next
COPY --from=build /app/public ./public
COPY --from=build /app/next.config.js . # rewrites ๋ฑ ๋ฐํ์์ ํ์
CMD ["npm", "start"] # next start ์คํ
๋ถํ์ํด์ง ์์กด์ฑ๋ ์ ๊ฑฐํ์ต๋๋ค:
- express ์ ๊ฑฐ
- multer ์ ๊ฑฐ → formidable๋ก ๋์ฒด
package-lock.json์์ 645์ค์ด ์ค์์ต๋๋ค.
๋ฐฐํฌ ํ ๋ฐ์ํ ๋ฌธ์ ์ ๋์ฒ
๋ง์ด๊ทธ๋ ์ด์ ์ฝ๋๋ฅผ ๋จธ์งํ๊ณ ๋ฐฐํฌํ ๋ค, ์์์น ๋ชปํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค. ๋ก์ปฌ ํ๊ฒฝ์์๋ ์๋ฒฝํ๊ฒ ๋์ํ์ง๋ง, ํ๋ก๋์ ์์๋ ์ปจํ ์ด๋๊ฐ ์ ์์ ์ผ๋ก ์์๋์ง ์์์ต๋๋ค.
health-check ์๋ํฌ์ธํธ ์ ๊ทผ ๋ถ๊ฐ
์ฆ์: ์ปจํ ์ด๋ ๋ฐฐํฌ ํ health-check๊ฐ ์คํจํ์ฌ ์๋น์ค๊ฐ ์ ์ ๊ฐ๋๋์ง ์์์ต๋๋ค.
์์ธ ๋ถ์: middleware.ts์์ ์ธ์ ์ฟ ํค๊ฐ ์๋ ์์ฒญ์ ๋ก๊ทธ์ธ ํ์ด์ง๋ก ๋ฆฌ๋ค์ด๋ ํธํ๋๋ฐ, /health-check ๊ฒฝ๋ก๊ฐ middleware matcher์ ๊ฑธ๋ฆฌ๊ณ ์์์ต๋๋ค. health-check ์์ฒญ์ ์ฟ ํค ์์ด ๋ค์ด์ค๋ฏ๋ก ๋งค๋ฒ ๋ฆฌ๋ค์ด๋ ํธ๋์ด 200 ์๋ต์ ๋ฐ์ง ๋ชปํ์ต๋๋ค.
๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์๋ health-check๋ฅผ ํ ์คํธํ์ง ์์๊ธฐ ๋๋ฌธ์ ์ด ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ์ง ๋ชปํ๊ณ , ํ๋ก๋์ ๋ฐฐํฌ ํ์์ผ ์๊ฒ ๋์์ต๋๋ค.
์์ : middleware์ matcher ํจํด์ health-check๋ฅผ ์ ์ธ ๊ฒฝ๋ก๋ก ์ถ๊ฐํ์ต๋๋ค.
// middleware.ts
export const config = {
matcher: [
"/((?!api|_next/static|_next/image|favicon.ico|images|fonts|service-worker.js|manifest.json|health-check).*)",
// ^^^^^^^^^^^^^ ์ถ๊ฐ
],
};
๋ํ Dockerfile์์ next.config.js๋ฅผ ํ๋ก๋์ ๋ฐํ์์ ํฌํจํ๋๋ก ์ถ๊ฐํ์ต๋๋ค. rewrites ์ค์ ์ด ๋ฐํ์์ ํ์ํ๋ฐ ๋น๋ ์์ ์๋ง ํฌํจ๋์ด ์์์ต๋๋ค.
COPY --from=build /app/next.config.js .
- ์ธํ๋ผ ๋ ๋ฒจ์ ์๋ํฌ์ธํธ(health-check, metrics ๋ฑ)๋ ์ธ์ฆ ๋ก์ง์์ ๋ช ์์ ์ผ๋ก ์ ์ธํด์ผ ํฉ๋๋ค.
- ํ๋ก๋์ ํ๊ฒฝ๊ณผ ๋์ผํ ์กฐ๊ฑด์์ ํ ์คํธํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค. ํนํ Docker ์ปจํ ์ด๋๋ก ๋ฐฐํฌํ๋ค๋ฉด ๋ก์ปฌ์์๋ ์ปจํ ์ด๋๋ก ํ ์คํธํด์ผ ํฉ๋๋ค.
๊ฒฐ๊ณผ ์์ฝ
ํญ๋ชฉ ๋ณ๊ฒฝ ์ ๋ณ๊ฒฝ ํ
| ์๋ฒ ์ํคํ ์ฒ | Express ์ปค์คํ ์๋ฒ | Next.js ๋ค์ดํฐ๋ธ |
| ํ๋ก์ ์ฝ๋๋ | 1,056์ค (๋จ์ผ ํ์ผ) | ~550์ค (6๊ฐ ๋ชจ๋) |
| HTTP ๋ฉ์๋๋ณ ์ฝ๋ | ๋ณต์ฌ-๋ถ์ฌ๋ฃ๊ธฐ ๋ฐ๋ณต | ํฉํ ๋ฆฌ ํจํด์ผ๋ก ํตํฉ |
| ํ์ผ ์ ๋ก๋ | multer (Express ์ ์ฉ) | formidable (ํ๋ ์์ํฌ ๋ฌด๊ด) |
| ์คํ ๋ฐฉ์ | node server/index.js | next start |
| ์ ๊ฑฐ๋ ์์กด์ฑ | - | express, multer |
| package-lock.json | - | 645์ค ๊ฐ์ |
| ๋ฐฐํฌ ํ ํซํฝ์ค | - | health-check ์ ์ ๋์ |
๋ง์น๋ฉฐ
1. ์ฝ๋์ ๋ช ํ์ฑ
- 1,056์ค์ ๋จ์ผ ํ์ผ → ์ญํ ๋ณ๋ก ๋ถ๋ฆฌ๋ 6๊ฐ ๋ชจ๋
- "์ด ์ฝ๋๊ฐ ๋ญํ๋ ๊ฑฐ์ง?"๋ผ๋ ์ง๋ฌธ์ด ์ฌ๋ผ์ก์ต๋๋ค
2. ๊ฐ๋ฐ์ ๊ฒฝํ ๊ฐ์
- Next.js HMR์ด ์ ์์ ์ผ๋ก ์๋
- ํ์ค Next.js ๋ช ๋ น์ด๋ก ํต์ผ (next dev, next start)
- ์ ๊ท ํ์ ์จ๋ณด๋ฉ ์ ์ค๋ช ํด์ผ ํ ๋ด์ฉ์ด ์ค์์ต๋๋ค
3. ์ ์ง๋ณด์์ฑ ํฅ์
- ์๋ก์ด ํ๋ก์ ์๋ํฌ์ธํธ ์ถ๊ฐ: 10์ค ์ด๋ด๋ก ๊ฐ๋ฅ
- ๋ฒ๊ทธ ์์ : ํ ๊ณณ๋ง ๊ณ ์น๋ฉด ๋ชจ๋ ๋ฉ์๋์ ๋ฐ์
- Next.js ์ ๊ทธ๋ ์ด๋ ์ ํธํ์ฑ ๊ฑฑ์ ๊ฐ์
4. ๊ธฐ์ ๋ถ์ฑ ํด์
- ๋ถํ์ํ ์์กด์ฑ 645์ค ์ ๊ฑฐ
- ํ๋ ์์ํฌ ์ด์ค ์์กด ๋ฌธ์ ํด๊ฒฐ
- Dockerfile ๋จ์ํ
๋ ๊ฑฐ์ ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋งํ๋ ๊ฒ์ ํญ์ ๋๋ ค์ด ์ผ์ ๋๋ค.
"ํน์ ๋ด๊ฐ ๋ชจ๋ฅด๋ ์ค์ํ ๋ก์ง์ด ์๋ ๊ฑด ์๋๊น?", "๋ฐฐํฌ ํ ๋ฌธ์ ๊ฐ ์๊ธฐ๋ฉด ์ด๋กํ์ง?" ๊ฐ์ ๋ง์ฐํ ๊ฑฑ์ ์ด ์์ญ๋๋ค.
ํ์ง๋ง ์ด๋ฒ ๊ฒฝํ์ ํตํด ๋ฐฐ์ด ๊ฒ์ ํ์ฌ ์์คํ ์ด ์ค์ ๋ก ๋ฌด์์ ํ๋์ง ์ ํํ ํ์ ํ๋ฉด ์๊ฐ๋ณด๋ค ๋จ์ํ๊ฒ ๊ฐ์ ํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค. 1,056์ค์ ์ฝ๋๊ฐ ์ฌ์ค์ "ํ๋ก์, ์ธ์ ์ฒดํฌ, ํฌ์ค ์ฒดํฌ" ์ธ ๊ฐ์ง๋ง ํ๊ณ ์์๊ณ , ์ด๋ ๋ชจ๋ Next.js๋ก ๋ ๊ฐ๊ฒฐํ๊ฒ ๊ตฌํ ๊ฐ๋ฅํ์ต๋๋ค. ํ๋ ์์ํฌ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ์ต๋ํ ํ์ฉํ๋ ๊ฒ์ด ์ฅ๊ธฐ์ ์ผ๋ก ์ ์ง๋ณด์ ๊ฐ๋ฅํ ์ฝ๋๋ฅผ ๋ง๋๋ ์ง๋ฆ๊ธธ์ด๋ผ๋ ๊ฒ์ ๋ค์ ํ๋ฒ ๋๊ผ์ต๋๋ค.
'Front-end' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| Next.js ๋น๋ ์ต์ ํ : Babel โ SWC ์ ํ (1) | 2025.08.17 |
|---|---|
| Monorepo ๋์ ๊ธฐ: ํ๋ก ํธ์๋ ๋ง์ด๊ทธ๋ ์ด์ ์ค๊ณ ์ ๋ฆฌ (0) | 2025.08.17 |
| Next.js ์ด๊ธฐ ๋ ๋๋ง ์ ๋ค๊ตญ์ด ๋ฌธ๊ตฌ๊ฐ ๋น์ด ๋ณด์ด๋ ๋ฌธ์ ํด๊ฒฐํ๊ธฐ (1) | 2025.06.15 |
| useState์ ๋น๋๊ธฐ์ฑ (0) | 2025.04.13 |
| React key prop๊ณผ ์ํ ์ด๊ธฐํ (0) | 2025.02.23 |