Swagger를 Node.js REST API와 쉽게 통합하는 방법

발행: (2025년 12월 11일 오후 09:45 GMT+9)
4 min read
원문: Dev.to

Source: Dev.to

Swagger는 REST API를 문서화하는 가장 간단한 방법 중 하나입니다. 개발자가 엔드포인트를 테스트하고, 입력·출력을 이해하며, API를 더 빠르게 통합할 수 있도록 인터랙티브 UI를 제공합니다.

이 가이드에서는 대규모 Node.js 백엔드에 Swagger를 통합하는 정확한 단계와 개발·프로덕션 환경 모두에서 Swagger UI가 정상적으로 동작하도록 하는 수정 사항을 다룹니다.

1. Install Swagger Libraries

Only two packages are required.

npm install swagger-jsdoc swagger-ui-express

2. Create Swagger Configuration File

Create docs/swagger.config.js.

import swaggerJSDoc from "swagger-jsdoc";

const swaggerDefinition = {
  openapi: "3.0.0",
  info: {
    title: "Dining System API",
    version: "1.0.0",
    description: "API documentation for Dining System",
  },
  servers: [
    { url: process.env.DEV, description: "Development" },
    { url: process.env.PROD, description: "Production" },
  ],
  components: {
    securitySchemes: {
      bearerAuth: {
        type: "http",
        scheme: "bearer",
        bearerFormat: "JWT",
      },
    },
  },
  tags: [
    { name: "Authentication" },
    { name: "Users" },
    { name: "Vendors" },
    { name: "Bills" },
    { name: "Meal" },
  ],
};

const options = {
  swaggerDefinition,
  apis: ["./docs/routes/*.js"], // Load swagger route docs
};

export default swaggerJSDoc(options);

3. Integrate Swagger UI into app.js

Import Swagger UI and the spec:

import swaggerUi from "swagger-ui-express";
import swaggerSpec from "./docs/swagger.config.js";

Add the Swagger UI route:

const swaggerUiOptions = {
  customCss: ".swagger-ui .topbar { display: none }",
  customSiteTitle: "Dining System API Docs",
  swaggerOptions: {
    persistAuthorization: true,
    displayRequestDuration: true,
    filter: true,
  },
};

app.use(
  "/api-docs",
  swaggerUi.serve,
  swaggerUi.setup(swaggerSpec, swaggerUiOptions)
);

app.get("/api-docs.json", (req, res) => {
  res.setHeader("Content-Type", "application/json");
  res.send(swaggerSpec);
});

4. app.js Reference

import express from "express";
import cors from "cors";
import helmet from "helmet";
import cookieParser from "cookie-parser";
import authRoutes from "./routes/authRoutes.js";
import swaggerUi from "swagger-ui-express";
import swaggerSpec from "./docs/swagger.config.js";
import rateLimit from "express-rate-limit";
import dotenv from "dotenv";

dotenv.config();

const app = express();

// Swagger should load before Helmet
app.use(
  "/api-docs",
  swaggerUi.serve,
  swaggerUi.setup(swaggerSpec, {
    customCss: ".swagger-ui .topbar { display: none }",
    swaggerOptions: { persistAuthorization: true },
  })
);

// JSON endpoint for tools
app.get("/api-docs.json", (req, res) => {
  res.setHeader("Content-Type", "application/json");
  res.send(swaggerSpec);
});

// Helmet applied after Swagger
app.use(
  helmet({
    contentSecurityPolicy: false,
  })
);

app.use(cors());
app.use(express.json());
app.use(cookieParser());

const globalLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 300,
  message: { message: "Too many requests, please try again later." },
});
app.use(globalLimiter);

app.get("/", (req, res) => {
  res.send("Dining System API is running...");
});

app.use("/api/auth", authRoutes);
// ... other routes

app.use((err, req, res, next) => {
  console.error("Uncaught Error:", err);
  const payload = { message: err.message || "Internal Server Error" };
  if (process.env.NODE_ENV !== "production") payload.stack = err.stack;
  res.status(err.status || 500).json(payload);
});

export default app;

Why Swagger must be loaded before Helmet

Helmet blocks inline scripts, which Swagger UI relies on. Loading Swagger first avoids blank UI issues in production.

5. Create Separate Swagger Files for Each API Module

Instead of mixing Swagger comments inside Express route files, create a dedicated folder:

docs/routes/

Example structure:

docs/routes/auth.swagger.js
docs/routes/users.swagger.js
docs/routes/vendors.swagger.js
docs/routes/bills.swagger.js
docs/routes/meal.swagger.js

Example Swagger file (auth.swagger.js):

/**
 * @swagger
 * /api/account-types/list:
 *   get:
 *     summary: Get all account types
 *     tags: [Account Types]
 *     security: [{ bearerAuth: [] }]
 *     responses:
 *       200:
 *         description: OK
 */

Swagger automatically loads these files through the apis configuration:

apis: ["./docs/routes/*.js"]

6. Restart and Test Swagger

Development

npm run dev

Open Swagger UI:

http://localhost:5000/api-docs

Production (PM2)

git pull
npm install
pm2 restart all

Open Swagger UI:

http://your-production-url/api-docs

7. Issues Faced and How They Were Solved

1. Swagger UI blank in production

  • Cause: Helmet blocks inline scripts.
  • Fix: Disable CSP for Swagger as shown above (contentSecurityPolicy: false).

2. Swagger did not update after changes

  • Cause: PM2 was running an old build.
  • Fix: Restart the server.
pm2 restart all

3. Swagger route files were not loading

  • Cause: Wrong folder path in the configuration.
  • Fix: Use the correct path.
apis: ["./docs/routes/*.js"]
docs/
  swagger.config.js
  routes/
    auth.swagger.js
    users.swagger.js
    vendors.swagger.js
    bills.swagger.js
    meal.swagger.js

routes/
controllers/
models/
app.js
server.js
Back to Blog

관련 글

더 보기 »

첫 번째 MCP 앱 만들기

TL;DR MCP Apps는 대화형 에이전트와 기타 MCP 클라이언트에 인터랙티브 UI를 제공합니다. 이 튜토리얼에서는 간단하면서도 강력한 앱 소스 코드를 만드는 방법을 보여줍니다.

실험적인 Hono auth npm 패키지

제가 만들고 있는 것은 개발자들이 일반적인 보일러플레이트인 login, register, JWT, email verification 등을 작성하지 않고도 앱에 바로 넣을 수 있는 auth 패키지입니다.