TIL

에러 핸들러와 미들웨어에 관하여 & 데이터 유효성 검증 라이브러리 Joi

추운날_너를_기다리며 2024. 9. 10. 21:13

게임 서버 개발을 하면서 미들웨어와 에러 핸들러는 너무나도 중요한 개념인 것 같아서 다시 공부하게 되었습니다.

1. 미들웨어 (Middleware)

 -> 1) 미들웨어 기본 개념

      -> 미들웨어란 웹 서버에서 요청을 받을때, 모든 요청에 대한 공통적인 처리를 하고 싶을 수 있습니다. 이 때 필요한 것이 바로 미들 웨어 입니다. 여기서 미들웨어는 서버의 요청 - 응답 과정에서 중간에 위치하여 특정 기능을 수행하는 함수라고 보면 됩니다. 예를 들어, 모든 요청에 대해서 로그를 남기거나, 특정 사용자만 API를 접근(Authentication & Authorization)하게 하고 싶을 때도 미들 웨어를 사용합니다. 또한, 사용자가 웹 페이지에서 Form을 통해 전송한 데이터를 서버에서 쉽게 파싱(Body Parser)하여 사용할 수 있게 해주는 미들웨어도 존재합니다.

     -> Express.js의 미들웨어에는 어떤게 있나? 여기서 Express.js의 특징 중 하나인 미들웨어의 지원이 있습니다. 미들 웨어는 Express.js의 핵심 기능 중 하나로, 여러가지 기능을 제공합니다. 예를 들어 Body Parser는 클라이언트의 요청 본문 데이터인 body를 쉽게 파싱할 수 있게 해주는 미들웨어입니다.

app.use(express.urlencoded({ extended: false }));
app.use(express.json());

     -> 위에서 urlencoded는 form-urlencoded 라는 규격의 body를 손쉽게 코드에서 사용할 수 있게 도와주는 미들웨어입니다.

     -> json: JSON 이라는 규격의 body 데이터를 손쉽게 코드에서 사용할 수 있게 도와주는 미들웨어입니다.

 

 -> 2) 미들웨어는 어떤 경우에 사용하는게 적합한가?

     -> 인증 미들웨어로 예를 들면 사용자가 로그인 상태인지 확인하고, 로깅 미들웨어는 클라이언트의 요청에 대한 정보를 기록하며, 에러 핸들링 미들웨어는 에러를 처리하게됩니다.

 

 -> 3) Router와 미들웨어의 차이

     -> Router와 미들웨어는 서로 다른 방식처럼 보이지만 Router는 미들웨어 기반으로 구현된 객체이므로 미들웨어와 동일한 방식으로 작동됩니다.

     -> 즉, Router는 미들웨어 함수를 특정 경로에 바인딩하는 역할을 하며, 요청이 들어온 URL 경로에 따라 서로 다른 미들웨어를 실행시킬 수 있게 도와줍니다.

 

 -> 4) 자주쓰이는 미들웨어 사용 해석

     -> app.use(Middleware) : 모든 요청에서 미들웨어가 실행된다.

     -> app.use('/api', Middleware) : /api로 시작하는 모든 요청에서 미들웨어를 실행한다.

     -> app.post('/api', Middleware, (req, res, next) => {} ) : /api로 시작하는 POST 요청에서 미들웨어를 실행한다.

 

2. 데이터 유효성 검증 라이브러리 Joi

 -> Joi란?

     -> Joi는 JavaScript 유효성 검증을 위한 라이브러리입니다. Joi는 여러 타입과 규칙을 이용해 유효성을 검증할 수 있으며, 유효성 검증에 실패하면 오류를 발생시킵니다.

 

 -> 실제로 요구사항을 토대로 코드를 보면 이해가 쉽습니다.

1. Joi 설치하기
2. Joi를 이용한 Validation 시작하기
3. 문자열 길이 검증하기
4. 이메일 검증하기
5. 비동기로 문자열 길이 검증하기
6. try/catch 설정하기

👉 **할 일 생성 API 유효성 검사 요구사항**

1. `value` 데이터는 **필수적으로 존재**해야한다.
2. `value` 데이터는 **문자열 타입**이어야한다.
3. `value` 데이터는 **최소 1글자 이상**이어야한다.
4. `value` 데이터는 **최대 50글자 이하**여야한다.
5. 유효성 검사에 실패했을 때, 에러가 발생해야한다.
# yarn을 이용해 Joi를 설치합니다.
yarn add joi
import Joi from 'joi';

// 할 일 생성 API의 요청 데이터 검증을 위한 Joi 스키마를 정의합니다.
const createTodoSchema = Joi.object({
  value: Joi.string().min(1).max(50).required(),
});

router.post('/todos', async (req, res) => {
  try {
    // 클라이언트에게 전달받은 데이터를 검증합니다.
    const validateBody = await createTodoSchema.validateAsync(req.body);

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

    // Todo모델을 사용해, MongoDB에서 'order' 값이 가장 높은 '해야할 일'을 찾습니다.
    const todoMaxOrder = await Todo.findOne().sort('-order').exec();

    // 'order' 값이 가장 높은 도큐멘트의 1을 추가하거나 없다면, 1을 할당합니다.
    const order = todoMaxOrder ? todoMaxOrder.order + 1 : 1;

    // Todo모델을 이용해, 새로운 '해야할 일'을 생성합니다.
    const todo = new Todo({ value, order });

    // 생성한 '해야할 일'을 MongoDB에 저장합니다.
    await todo.save();

    return res.status(201).json({ todo });
} catch (error) {
    console.error(error);
    // Joi 검증에서 에러가 발생하면, 클라이언트에게 에러 메시지를 전달합니다.
    if (error.name === 'ValidationError') {
      return res.status(400).json({ errorMessage: error.message });
    }

    // 그 외의 에러가 발생하면, 서버 에러로 처리합니다.
    return res
      .status(500)
      .json({ errorMessage: '서버에서 에러가 발생하였습니다.' });
  }
});

 

폴더 구조를 위한 간단한 용어 정리

  • app.js
    • 전체 어플리케이션의 시작점입니다.
    • 미들웨어(Middleware)와 라우터(Router)를 등록하며, 서버를 시작하는 역할을 담당합니다.
  • middlewares
    • 미들웨어를 정의하기 위해 사용합니다.
    • 에러 핸들러, 로깅, 사용자 인증과 같은 미들웨어를 이 폴더에서 관리합니다.
  • routes
    • Express.js의 라우터(Router)를 관리하기 위해 사용합니다.
    • API 경로를 정의하며, 해당 경로에서 실행될 함수를 관리하는 역할을 담당합니다.
  • schemas
    • MongoDB를 사용하기 위한 mongoose의 스키마(Schema) 및 **모델(Model)**을 정의하기 위해 사용합니다.
    • MongoDB 데이터의 구조데이터를 처리할 메서드를 정의하는 역할을 담당합니다.
  • assets
    • 프론트엔드 파일을 서빙하기 위해 사용하는 폴더입니다.
    • 웹페이지를 구성하는 HTML, CSS, JavaScript 파일, 이미지 등 여러 파일들이 이 폴더에 위치하게 됩니다.