Node 강의/입문

2-5 에러 처리 미들웨어(실습에서 추가 및 수정)

kagan-draca 2024. 9. 4. 12:22

1. 에러 처리 미들웨어

 

에러 처리 미들웨어는 여러 API에서 발생할 수 있는 에러를

통합적으로 관리할 수 있도록 도와주는 미들웨어 입니다.

 

그래서 개발자가 커스텀한 에러 발생 및 처리가 가능해지고

에러를 한 .js 파일에서 통합적으로 관리하는게 가능해집니다.

 

2. Express.js의 에러 처리 미들웨어

 

 Express.js에서 공식적으로 제공하는 기능으로,

 

app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).send('Something broke!')
});

 

에러 처리 미들웨어에서 err, req, res, next는 각각 에러, 요청, 응답, 다음 미들웨어 호출하는 함수 입니다.

 

err : 이전 미들웨어에서 발생한 에러전달받은 객체입니다.

 

req, res : 는 HTTP 요청과 응답을 관리하는 객체입니다.

 

next : 다음 미들웨어를 실행하는 함수입니다.

 

에러를 인자로 전달받아 클라이언트에게 에러 응답을 반환하거나,

다음 미들웨어로 에러를 전달하는 역할을 담당합니다.

 

에러 처리 미들웨어는 Nest.js 프레임워크에서 Exception Filter라는 이름으로 불립니다.

 

Express.js에서는 미들웨어라우터에서 에러가 발생하면,

해당 에러를 next함수를 통해 다음 미들웨어로 전달합니다.

그리고, Express.js는 등록된 미들웨어 중에서 에러를 매개변수로

받는 미들웨어(에러 처리 미들웨어)를 찾아 실행하게 됩니다.

 

3. 에러 발생 시 에러를 처리하는 미들웨어 작성하기

 

try/catch 문으로 에러 처리를 하면, 매번 try/catch문을 사용하여 에러 처리를 위한 메시지를 작성해야 합니다.

비효율적이고, 중복된 코드가 추가되는 문제 발생

 

해결 방법 : 에러 처리 미들웨어로 에러 처리를 통합적으로 관리한다!

 

1) 폴더 파일 생성

middlewares라는 폴더를 만들고, error-handler.middleware.js 라는 파일을 만든다.

 

2) 에러 처리 미들웨어 구현하기

 

error-handler.middleware.js에서 아래와 같은 코드를 작성해 에러 처리 미들웨어를 구현한다.

export default (err, req, res, next) => {
  console.log("에러처리 미들웨어가 실행 됐습니다");
  console.error(err);

  //Joi에서 발생한 유효성 에러일 경우
  if (err.name === "ValidationError")
    return res.status(400).json({ errorMessage: err.message });

  // 그 밖의 서버 오류일 경우
  return res
    .status(500)
    .json({ errorMessage: "서버에서 에러가 발생하였습니다!" });
};

 

3) 에러 처리 미들웨어 등록하기

 

app.js 파일에

 

import ErrorHander from "./middlewares/error-handler.middleware.js";

 

으로 에러 처리 미들웨어 함수를 가져옵니다.

app.use("/api", router);

에서

app.use("/api", router);
{
  // 에러 처리 핸들링 미들웨어 등록
  app.use(ErrorHander);
}

으로 todoRouter를 사용하는 use안에

 

app.use(ErrorHander)

 

라고 에러 처리 핸들러를 추가해줍니다.

 

만약, 다른 곳에서도 필요하다면 app.use(ErrorHander)로 사용 가능 합니다.

 

그럼 왜, 에러 처리 미들웨어를 Router 하단에 등록하는걸까요?

 

미들웨어는 등록된 순서대로 실행되기 때문에

TodoRouter에서 비즈니스 로직을 수행한 후

발생한 에러는 다음 미들웨어로 전달 됩니다.

 

이때, 에러 처리 미들웨어가 라우터 이후에 등록 돼 있으면,

에러를 잡아 처리할 수 있게 됩니다.

 

만약, todoRouter 이전에 에러처리 미들웨어를 등록하면

라우터에서 발생한 에러를 처리할 수 없습니다.

 

4) todo.router.js 수정하기

// 할일 등록 API
// (중요) async를 줘서 비동기 함수로 구현한다.
router.post("/todos", async (req, res, next) => {
  try {
    // 쿨라이언트에게 전달받은 데이터를 검증합니다.
    const validation = await createTodoSchema.validateAsync(req.body);

    // 클라이언트에게 전달받은 value 데이터를 변수에 저장합니다.
    const { value } = validation;

    const todoMaxOrder = await Todo.findOne().sort("-order").exec();

    const order = todoMaxOrder ? todoMaxOrder.order + 1 : 1;

    const todo = new Todo({ value, order });

    await todo.save();

    return res.status(201).json({ todo: todo });
  } catch (error) {
    // Router 다음에 있는 에러 처리 미들웨어를 실행한다.
    next(error);
  }
});

 

catch문 안에 있던 내용을 지우고,

error-handler.middleware.js에 작성해주었기 때문에

next(error)로 todoRouter가 모두 동작 한 뒤,

error 정보를 app.use(ErrorHander)를 통해

export default (err, req, res, next) => {
  console.log("에러처리 미들웨어가 실행 됐습니다");
  console.error(err);

  //Joi에서 발생한 유효성 에러일 경우
  if (err.name === "ValidationError")
    return res.status(400).json({ errorMessage: err.message });

  // 그 밖의 서버 오류일 경우
  return res
    .status(500)
    .json({ errorMessage: "서버에서 에러가 발생하였습니다!" });
};

에러 처리 Hander가 동작하게 만들어줍니다.

 

5) Insomnia 실제 에러 발생시켜 확인하기

 

- value 값이 없을 경우

 

- value 값 공백일 경우

 

- value 숫자 타입일 경우

 

- value 문자열 길이가 50을 초과할 경우

 

모두 정상적인 Error 문구가 Client에게 응답된 것을 확인할 수 있습니다.

 

실습 내용 복습하기

 

app.js 

 

- 전체 어플리케이션의 시작점

- 미들웨어(Middleware)와 라우터(Router)를 등록하며,

  서버를 시작하는 역할을 담당합니다.

 

middlewares

 

- 미들웨어를 정의하기 위해 사용합니다.

- 에러 핸들러, 로깅, 사용자 인증과 같은 미들웨어를 이 폴더에서 관리합니다.

 

routes

 

- Express.js의 라우터(Router)를 관리하기 위해 사용합니다.

- 각 API 경로를 정의하며, 해당 경로에서 실행될 함수를 관리하는 역할

 

schemas

 

- MongoDB를 사용하기 위한 mongoose의 스키마(Schema) 및  모델(Model)을 정의

- MongoDB 데이터의 구조데이터를 처리할 메서드를 정의

 

assets

 

- 프론트엔드 파일을 서빙하기 위해 사용하는 폴더

- 웹 페이지를 구성하는 HTML, CSS, JavaScript 파일, 이미지 등이 위치

 

 

 

(중요)

폴더 구조는 각각의 파일이 가지는 역할을 명확하게 분류하여 구성하는 것이 기본

프로젝트가 커질수록 Layered Architecture, DDD(Domain Driven Development),

Clean Architecture 와 같은 여러가지 아키텍처 패턴을 통해 더 효율적으로 관리할 수 있습니다.