[Node.js – 기초 강좌] 5-2. 기본 웹 서버 구축(Middleware 편)

이번 포스팅에서는 Node.js로 기본 웹 서버를 구축하는 방법을 알아보겠습니다.

특히 이번 포스팅에서는 Middleware을 중점적으로 다루겠습니다.

앞서 Routing 포스팅([Node.js – 기초 강좌] 5-1. 기본 웹 서버 구축(Routing 편)) 역시 미들웨어(Middleware)를 사용한 것입니다.

middleware-미들웨어-node.js

미들웨어(Middleware) 소개

Middleware 란?

미들웨어(Middleware)는 웹 애플리케이션에서 요청(request)과 응답(response) 사이에서 동작하는 소프트웨어 컴포넌트를 의미합니다.

Node.js에서 미들웨어는 주로 Express와 같은 웹 프레임워크에서 사용되며, 각종 공통 기능을 모듈화하여 코드의 재사용성과 유지보수성을 높이는 데 기여합니다.

기본 구조

미들웨어 함수는 기본적으로 세 가지 매개변수를 받습니다:

  • req(요청 객체)
  • res(응답 객체)
  • next(다음 미들웨어 함수를 호출하는 콜백 함수)

기본적인 미들웨어 함수의 형태는 다음과 같습니다:

import { Request, Response, NextFunction } from 'express';

const middlewareFunction = (req: Request, res: Response, next: NextFunction): void => {
  // 작업 수행
  next(); // 다음 미들웨어 호출
};

미들웨어의 역할

미들웨어는 다양한 역할을 수행할 수 있습니다. 주로 다음과 같은 기능을 담당합니다:

  1. 요청 로깅 (Request Logging): 모든 요청에 대한 로그를 남겨 서버의 활동을 기록합니다.
  2. 인증 및 권한 관리 (Authentication and Authorization): 사용자가 요청한 자원에 접근할 권한이 있는지 확인합니다.
  3. 에러 처리 (Error Handling): 애플리케이션에서 발생하는 에러를 중앙에서 관리하고, 적절한 응답을 반환합니다.
  4. 데이터 파싱 (Data Parsing): 요청 본문을 파싱하여 JSON이나 URL-encoded 데이터 형식으로 변환합니다.
  5. 라우팅 (Routing): 요청 URL에 따라 적절한 핸들러로 요청을 전달합니다.

Middleware 사용하기

다음 위에서 설명한 Middleware를 사용해 보도록 하겠습니다.

폴더 구조

Node.js, Typescript, Express를 사용하는 환경에서 위 Middleware들을 사용할 예정이며,

다음과 같은 구조로 프로젝트를 구성할 예정입니다.

05_02_middleware/
├── src/
│   ├── middlewares/
│   │   ├── requestLogger.ts
│   │   ├── checkAuth.ts
│   │   ├── errorHandler.ts
│   │   ├── dataParsers.ts
│   ├── routes/
│   │   └── index.ts
│   ├── app.ts
│   └── server.ts
├── tsconfig.json
└── package.json

1. 요청 로깅 (Request Logging)

요청 로깅 미들웨어는 모든 요청에 대한 로그를 남겨 서버의 활동을 기록합니다.

이는 디버깅과 모니터링에 유용합니다.

import { NextFunction, Request, Response } from "express";

// Request logger middleware
const requestLogger = (
  req: Request,
  res: Response,
  next: NextFunction
): void => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
};

export default requestLogger;

2. 인증 및 권한 관리 (Authentication and Authorization)

인증 및 권한 관리 미들웨어는 사용자가 요청한 자원에 접근할 권한이 있는지 확인합니다.

Client가 요청 시, 인증 헤더 (Authorization)에 포함된 req.headers.authorization를 통해 서버는 Client가 보낸 토큰을 추출하고,

이를 검사합니다. 아래 예제에서는 Devitworld로 시작하는지 확인하고, Devitworld 다음에 오는 실제 토큰 값을 추출하여 토큰값의 유효성을 검증합니다.

인증이 성공하면 next() 실패하면 res.status(401).json({ message: "Unauthorized" });를 반환합니다.

import { NextFunction, Request, Response } from "express";

// Auth middleware
const checkAuth = (req: Request, res: Response, next: NextFunction): void => {
  const authHeader = req.headers.authorization;

  if (authHeader && authHeader.startsWith("Devitworld ")) {
    const token = authHeader.split(" ")[1];

    // Logic to validate token
    if (token === "your_valid_token") {
      return next();
    }
  }

  res.status(401).json({ message: "Unauthorized" });
};

export default checkAuth;
  • 단 실제 Application에서는 JWT를 사용하여 인증을 처리하는 경우가 많습니다.

JWT에 대한 간략한 예제는 다음과 같습니다.

import { NextFunction, Request, Response } from "express";
import jwt from "jsonwebtoken";

const SECRET_KEY = "your_secret_key";

// JWT 인증 미들웨어
const checkAuth = (req: Request, res: Response, next: NextFunction): void => {
  const authHeader = req.headers.authorization;

  if (authHeader && authHeader.startsWith("Bearer ")) {
    const token = authHeader.split(" ")[1];

    jwt.verify(token, SECRET_KEY, (err, decoded) => {
      if (err) {
        return res.status(401).json({ message: "Unauthorized" });
      }

      // 토큰이 유효하면 decoded에 디코딩된 정보가 담깁니다.
      req.user = decoded;  // TypeScript에서는 req.user 타입을 선언해야 합니다.
      next();
    });
  } else {
    res.status(401).json({ message: "Unauthorized" });
  }
};

export default checkAuth;

3. 에러 처리 (Error Handling)

에러 처리 미들웨어는 애플리케이션에서 발생하는 모든 에러를 중앙에서 관리하고, 적절한 응답을 반환합니다.

import { NextFunction, Request, Response } from "express";

// error handler middleware
const errorHandler = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
): void => {
  console.error(err.stack);
  res.status(500).json({ message: "Something broke!", error: err.message });
};

export default errorHandler;

4. 데이터 파싱 (Data Parsing)

데이터 파싱 미들웨어는 요청 본문을 파싱하여 JSON이나 URL-encoded 데이터 형식으로 변환합니다.

import express from "express";

// JSON Parser Middleware
const jsonParser = express.json();

// URL-encoded Parser Middleware
const urlEncodedParser = express.urlencoded({ extended: true });

export { jsonParser, urlEncodedParser };

5. 라우팅 (Routing)

라우팅 미들웨어는 요청 URL에 따라 적절한 핸들러로 요청을 전달합니다.

라우팅 미들웨어에 대해서는 앞선 포스팅에서 자세히 소개했습니다.

import { Request, Response, Router } from "express";

const router = Router();

// Basic route
router.get("/", (req: Request, res: Response) => {
  res.send("Hello, World!");
});

router.get("/secure/data", (req: Request, res: Response) => {
  res.send("Secure data");
});

export default router;

참고 링크

Leave a Comment