본문 바로가기
JavaScript & TypeScript

Express.js HandlerInterceptor: 개념 소개

by 대박플머 2024. 11. 26.

HandlerInterceptor는 Express.js의 기본 기능은 아니지만, 미들웨어와 핸들러 래퍼를 사용하여 인터셉터와 유사한 동작을 구현할 수 있습니다. 이를 통해 요청이 컨트롤러에 도달하기 전과 응답이 준비된 후에 요청을 가로채는 기능을 구현

할 수 있으며, 전처리, 후처리, 오류 처리 등의 작업을 수행할 수 있습니다.


Middleware와 HandlerInterceptor의 차이점

특징 Middleware HandlerInterceptor
실행 방식 라우트에 걸쳐 순차적으로 실행 전처리와 후처리를 제공
적용 범위 전역 또는 특정 라우트에 적용 가능 특정 핸들러에 밀접하게 연결
사용 사례 로깅, 인증, 파싱 등의 일반 작업 요청/응답 변환, 예외 처리 등

Express.js에서 HandlerInterceptor 구현하기

Express.js에는 기본적으로 인터셉터 기능이 제공되지 않으므로, 전처리, 후처리, 오류 처리를 수행하는 미들웨어를 직접 만들어서 이러한 동작을 흉내 낼 수 있습니다.

1단계: 기본 HandlerInterceptor 만들기

아래는 전처리, 후처리, 오류 처리를 수행하는 간단한 구현 예시입니다.

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

// 전처리 함수
function preProcess(req: Request) {
  console.log(`[전처리] 요청 처리 중: ${req.url}`);
}

// 후처리 함수
function postProcess(req: Request, res: Response) {
  console.log(`[후처리] 응답 상태 코드: ${res.statusCode}`);
}

// 오류 처리 함수
function handleError(err: Error, req: Request, res: Response) {
  console.error(`[오류] ${err.message}`);
  res
    .status(500)
    .json({ message: "오류가 발생했습니다", details: err.message });
}

// Handler Interceptor
export function handlerInterceptor(
  handler: (req: Request, res: Response, next: NextFunction) => Promise<void>
) {
  return async (req: Request, res: Response, next: NextFunction) => {
    try {
      preProcess(req);
      await handler(req, res, next);
      postProcess(req, res);
    } catch (error) {
      handleError(error, req, res);
    }
  };
}

2단계: 라우트에 HandlerInterceptor 적용하기

import express, { Request, Response } from "express";
import { handlerInterceptor } from "./handlerInterceptor";

const app = express();

// 예시 라우트 핸들러
async function exampleHandler(req: Request, res: Response) {
  if (!req.query.name) throw new Error("Name이 필요합니다.");

  res.json({ message: `안녕하세요, ${req.query.name}` });
}

// 인터셉터 적용
app.get("/greet", handlerInterceptor(exampleHandler));

app.listen(3000, () => {
  console.log("서버가 3000번 포트에서 실행 중입니다.");
});

고급 HandlerInterceptor 기능

1. 다중 인터셉터 적용

다중 인터셉터를 사용하여 각 인터셉터를 순차적으로 실행할 수 있습니다. 이를 통해 더욱 유연하고 재사용 가능한 인터셉터 로직을 구축할 수 있습니다.

type Interceptor = (
  req: Request,
  res: Response,
  next: NextFunction
) => Promise<void>;

export function handlerInterceptors(interceptors: Interceptor[]) {
  return (
    handler: (req: Request, res: Response, next: NextFunction) => Promise<void>
  ) => {
    return async (req: Request, res: Response, next: NextFunction) => {
      try {
        for (const interceptor of interceptors) {
          await interceptor(req, res, next);
        }
        await handler(req, res, next);
      } catch (error) {
        next(error);
      }
    };
  };
}

다중 인터셉터 사용하기

const logInterceptor: Interceptor = async (req, res, next) => {
  console.log(`[LogInterceptor] ${req.method} ${req.url}`);
  next();
};

const authInterceptor: Interceptor = async (req, res, next) => {
  if (!req.headers["authorization"]) {
    throw new Error("Unauthorized");
  }
  next();
};

// 다중 인터셉터 적용
app.get(
  "/secure",
  handlerInterceptors([logInterceptor, authInterceptor])(async (req, res) => {
    res.json({ message: "보안 데이터에 접근했습니다." });
  })
);

HandlerInterceptor의 일반적인 사용 사례

  1. 인증/인가: 라우트에 접근하기 전에 사용자 인증 정보를 검증합니다.
  2. 로깅: 요청 및 응답에 대한 정보를 수집하여 감사 또는 디버깅 목적으로 사용합니다.
  3. 데이터 변환: 요청 데이터가 핸들러에 도달하기 전에 변경하거나 응답 데이터를 클라이언트에 전달하기 전에 수정합니다.
  • 관심사 분리: 전처리, 후처리, 오류 처리 로직을 명확하게 분리하여 코드를 유지보수하기 쉽게 만듭니다.
  • 오류 처리: 인터셉터에서 발생하는 오류를 항상 적절하게 처리하여 애플리케이션의 비정상적인 종료를 방지합니다.
  • 체인 가능성: 다중 인터셉터를 사용하여 유연하고 재사용 가능한 로직을 구현합니다.

 


결론

Express.js는 기본적으로 HandlerInterceptor 기능을 제공하지 않지만, 미들웨어와 함수 래퍼를 사용하여 인터셉터와 유사한 동작을 구현할 수 있습니다. 이를 통해 전처리 및 후처리 로직에 대한 더 큰 제어권을 확보하고 더 모듈화되고 유지보수 가능한 애플리케이션을 구축할 수 있습니다.

이러한 방식으로 Express.js에서 HandlerInterceptor를 사용하여 요청 및 응답을 효과적으로 관리하고, 향상된 기능을 갖춘 API를 개발할 수 있습니다.