2025년 1월 21일

김동환·2025년 1월 21일

타자기 원리

위 사진에서 볼 수 있는 것이 바로 Typewirter 타자기이다.
이 타자기에 종이를 끼워 넣고 원하는 문자를 누르면 활자가 튀어나가 종이에 잉크를 찍어낸다.
글자는 정확히 타자기의 중앙에 찍히며 종이가 정해진 간격만큼 왼쪽으로 움직이게 된다.
이때 이 종이를 움직이게 만드는 것이 바로 Carriage이다.
그리고 종이에 한 줄을 다 채우게 되면 다음 줄을 작성하기 위해서 종이를 위로 올려줘야 하는데 이 작업을 Line Feed라고 하는 것이다. 또한 종이가 단순히 위로 올라가기만 해서는 안된다. 종이를 다시 오른쪽 끝으로 보내줘야 한다. 이를 Carriage Return이라고 한다.

타입스크립트 오류

그래서 타자기에서 볼 수 있듯이 타입스크립트에서 빨간줄이 뜨는 에러는 이러한 코드를 작성해줘야 해결할 수 있다.

rules: {
    'prettier/prettier': [
      'error',
      {
        endOfLine: 'auto'
      }
    ]
  }

오늘의 코드

TIL: 주소 생성 및 수정 시 입력값 검증을 위한 코드 작성

오늘은 주소 생성 및 수정 시 입력값 검증 로직을 작성하였습니다. 이 과정에서 Joi를 활용하여 스키마를 정의하고, 이를 기반으로 요청(Request)의 유효성을 검사하는 기능을 구현하였습니다. 주요 내용은 아래와 같습니다.


1. createSchemaupdateSchema 정의

  • createSchema: 새로운 주소를 생성할 때 필요한 입력값을 검증하는 스키마를 정의하였습니다.

    • 필드:
      • address: 필수 문자열
      • addressName: 필수 문자열
    • 각각 필드가 비어 있거나 입력되지 않았을 때 발생하는 오류 메시지를 MESSAGES 상수에서 가져옵니다.
  • updateSchema: 기존 주소를 수정할 때 필요한 입력값을 검증하는 스키마를 정의하였습니다.

    • 필드:
      • addressId: 필수 숫자
      • address: 필수 문자열
      • addressName: 필수 문자열
    • 오류 메시지는 위와 동일하게 정의됩니다.

2. createAddressValidator 미들웨어

  • 주소 생성 요청에 대해 아래를 검증합니다:
    1. createSchema를 통해 요청 바디(req.body)의 유효성을 검사합니다.
    2. 요청한 유저(req.user?.userId)가 존재하는지 확인합니다.
      유저 ID가 없으면 MESSAGES.ADDRESS.COMMON.NOT_FOUND_USER 메시지를 반환하며 에러를 발생시킵니다.
    3. 해당 유저가 이미 소유한 주소 개수를 확인합니다.
      • 유저의 주소 개수가 5개 이상이면, 400 상태 코드와 함께 오류 메시지를 반환합니다.
    4. 위 조건을 모두 만족하면 next()를 호출하여 다음 미들웨어로 진행합니다.

3. updateAddressValidator 미들웨어

  • 주소 수정 요청에 대해 아래를 검증합니다:
    1. updateSchema를 통해 요청 바디(req.body)와 URL 파라미터(req.params)를 결합한 데이터를 유효성 검사합니다.
    2. 유효성 검사를 통과하면 next()를 호출하여 다음 미들웨어로 진행합니다.

느낀 점

  • 이번 작업에서 Joi를 활용하여 간단하면서도 강력한 검증 로직을 구현할 수 있었습니다.
  • 에러 메시지를 상수화하여 유지보수가 용이하도록 설계했습니다.
  • 추가적으로, 유저별 주소 개수 제한(5개)을 검증하는 로직을 작성하며 비즈니스 로직과 검증 로직의 결합에 대해 고민할 수 있었습니다.

다음 개선 사항

  • 에러 메시지를 사용자 언어에 맞게 동적으로 변환할 수 있도록 다국어 지원을 추가하는 방안을 고려해보겠습니다.
  • 중복된 코드(예: addressaddressName의 검증 로직)를 최소화하여 스키마를 더 간결하게 작성하는 방법을 탐구해야겠습니다.

address-validator.middleware.js

import Joi from 'joi';
import { MESSAGES } from '../../constants/message.constant.js';
import addressRepository from '../../repositories/address.repository.js';

const createSchema = Joi.object({
  address: Joi.string().trim().required().messages({
    'any.required': MESSAGES.ADDRESS.COMMON.ADDRESS.REQUIRED,
    'string.empty': MESSAGES.ADDRESS.COMMON.ADDRESS.REQUIRED,
  }),
  addressName: Joi.string().trim().required().messages({
    'any.required': MESSAGES.ADDRESS.COMMON.ADDRESSNAME.REQUIRED,
    'string.empty': MESSAGES.ADDRESS.COMMON.ADDRESSNAME.REQUIRED,
  }),
});

const updateSchema = Joi.object({
  addressId: Joi.number().required().messages({
    'any.required': MESSAGES.ADDRESS.COMMON.NOT_FOUND_USER,
  }),
  address: Joi.string().trim().required().messages({
    'any.required': MESSAGES.ADDRESS.COMMON.ADDRESS.REQUIRED,
    'string.empty': MESSAGES.ADDRESS.COMMON.ADDRESS.REQUIRED,
  }),
  addressName: Joi.string().trim().required().messages({
    'any.required': MESSAGES.ADDRESS.COMMON.ADDRESSNAME.REQUIRED,
    'string.empty': MESSAGES.ADDRESS.COMMON.ADDRESSNAME.REQUIRED,
  }),
});

export const createAddressValidator = async (req, res, next) => {
  try {
    await createSchema.validateAsync(req.body);

    const userId = req.user?.userId || 0;
    if (!userId) {
      throw new Error(MESSAGES.ADDRESS.COMMON.NOT_FOUND_USER);
    }

    const userAddresses =
      await addressRepository.getAllAddressesByUserId(userId);

    if (userAddresses.length >= 5) {
      return res.status(400).json({
        message: MESSAGES.ADDRESS.CREATE.EXCEED_LIMIT,
      });
    }

    next();
  } catch (error) {
    next(error);
  }
};

export const updateAddressValidator = async (req, res, next) => {
  try {
    await updateSchema.validateAsync({
      ...req.body,
      ...req.params,
    });
    next();
  } catch (error) {
    next(error);
  }
};
profile
Node.js 7기

0개의 댓글