[2024.06.20 TIL] 내일배움캠프 46일차 (팀프로젝트, API 테스트 및 에러 해결, bcrypt, Joi, 발표자 선정, 리드미 작성)

My_Code·2024년 6월 21일
0

TIL

목록 보기
59/112
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.


💻 TIL(Today I Learned)

📌 Today I Done

✏️ API 테스트 및 에러 해결

  • 팀원들이 각자 맡은 부분을 API 기능별로 테스트 진행함

  • API 테스트를 통해서 유효성 검사나 오타로 인한 에러들을 해결함

  • 대부분의 에러가 그저 오타로 인한 문제라서 따로 기록하기 어려움

  • 그리고 아직 Controller - Service - Repository의 흐름을 이해하지 못한 팀원들이 각 계층의 메서드의 매개변수를 잘 못 적는 경우도 많이 발생했음


✏️ bcrypt.compare(token, hashedToken)의 로직

  • 팀원분 중 Refresh Token 인증 미들웨어에서 에러를 해결하지 못한 케이스가 발생했음

  • 결국 문제는 해결하지 못했지만, 해결 과정 중 bcrypt.compare()의 로직에 대해 알지 못했던 내용이 있어서 정리하려고 함

  • 일단, 기존에 알고 있던 bcrypt.compare(token, hashedToken)의 로직은 bcrypt.compare가 입력받은 token을 해싱해서 이미 해싱된 hashedToken과 비교해서 같은지 확인하는 메서드라고 알고 있었음

  • 근데 문득 compare() 메서드 안에서도 해싱이 이뤄지고 있다면 이게 분명 시간이 달라서 값이 다를 텐데 어떻게 같다고 비교할 수 있을까 라고 생각했음

  • 그래서 찾아보니 입력받은 token을 그냥 해싱하는 게 아니었음

  • bcrypt.compare()의 로직은 다음과 같음

    • 데이터베이스에 저장된 해시된 비밀번호에서 솔트를 추출함
    • bcrypt 해시 형식은 2b$12saltsaltsaltsaltsaltsa$hashedpassword와 유사하며, 여기서 saltsaltsaltsaltsaltsa 부분이 솔트라고 함
    • 사용자가 입력한 평문 토큰에 추출된 솔트를 사용하여 해싱을 진행함
    • 이때 bcrypt 알고리즘은 같은 솔트를 사용하여 동일한 결과를 생성함
  • 즉, 그냥 일반적인 해싱을 하는 게 아니라 해싱된 데이터에서 솔트를 추출해서 동일하게 해싱하는 것임



📌 Tomorrow's Goal

✏️ 펫시터 팀프로젝트 발표날

  • API 명세서 완성하기

  • 리드미 완성하기

  • API 테스트 사진 첨부하기



📌 Today's Goal I Done

✔️ API 테스트 및 에러 해결

  • 내일이 발표하는 날이기 때문에 구현한 내용들을 마무리하고 테스트를 진행했음

  • 테스트를 진행하니 생각보다 메서드와 매개변수의 오타로 인해 발생하는 오류가 많았음

  • 그래서 팀원들에게 메서드와 매개변수를 작성할 때의 주의할 점을 설명했음

    • 메서드명은 직관적으로 이 메서드가 무엇은 하는 메서드인지 알아야 함
    • Controller - Service - Repository 계층에서 메서드가 서로 호출할 때는 같은 기능을 위한 메서드면 메서드 명을 일치시켜주는 게 흐름을 따라 가기도 좋음
    • 매개변수를 작성할 때는 선언하는 곳, 호출하는 곳의 메서드를 잘 확인해야 함
    • 매개변수는 개수와 순서가 중요함


⚠️ 구현 시 발생한 문제

✔️ Joi 유효성 검사에서 문자열 숫자를 검사하지 못하는 문제

  • 사용자에게 입력을 받을 때 입력값의 유효성을 검사하는 Joi 미들웨어를 구현했음

  • 구현이 끝나고 테스트하는 과정에서 원래는 정수가 들어가 자리에 문자열로 숫자를 넣으면 Joi에서 에러를 필터링하지 못하는 문제가 발생했음

  • 원래는 문자열 자체를 걸러야 하는 데 그냥 통과해 버리는 문제임

  • 찾아보니 나와 똑같은 문제가 발생한 글을 찾았음

  • https://github.com/hapijs/joi/issues/2638

  • 기존에 작성한 코드는 다음과 같음

import Joi from 'joi';
import { HTTP_STATUS } from '../../constants/http-status.constant.js';
import { RESERVATION_MESSAGES } from '../../constants/reservation.message.constant.js';

const createReservationSchema = Joi.object({
  petsitterId: Joi.number().required().messages({
    'number.base': RESERVATION_MESSAGES.RESERVATION.COMMON.PETSITTER_ID.BASE,
    'any.required': RESERVATION_MESSAGES.RESERVATION.COMMON.PETSITTER_ID.REQUIRED,
  }),
  
  ...
  
  hour: Joi.number().required().messages({
    'number.base': RESERVATION_MESSAGES.RESERVATION.COMMON.HOUR.BASE,
    'any.required': RESERVATION_MESSAGES.RESERVATION.COMMON.HOUR.REQUIRED,
  }),
  etc: Joi.string().messages({
    'string.base': RESERVATION_MESSAGES.RESERVATION.COMMON.ETC.BASE,
  }),
});

export const createReservationValidator = async (req, res, next) => {
  try {
    await createReservationSchema.validateAsync(req.body);
    next();
  } catch (error) {
    return res.status(HTTP_STATUS.BAD_REQUEST).json({
      status: HTTP_STATUS.BAD_REQUEST,
      message: error.details[0].message,
    });
  }
};
  • 아래와 같이 그냥 통과해버리고 서버에서 오류가 발생했음

  • 찾아보니 일반적으로 number()를 사용하면 그냥 문자열 숫자도 정수로 인식해서 통과한다고 함

  • 그래서 사용한 방법은 아래와 같이 Joi에 새로운 옵션을 사용했음

import Joi from 'joi';
import { HTTP_STATUS } from '../../constants/http-status.constant.js';
import { RESERVATION_MESSAGES } from '../../constants/reservation.message.constant.js';

const createReservationSchema = Joi.object({
  petsitterId: Joi.number()..integer().options({ convert: false }).required().messages({
    'number.base': RESERVATION_MESSAGES.RESERVATION.COMMON.PETSITTER_ID.BASE,
    'any.required': RESERVATION_MESSAGES.RESERVATION.COMMON.PETSITTER_ID.REQUIRED,
  }),
  
  ...
  
  hour: Joi.number()..integer().options({ convert: false }).required().messages({
    'number.base': RESERVATION_MESSAGES.RESERVATION.COMMON.HOUR.BASE,
    'any.required': RESERVATION_MESSAGES.RESERVATION.COMMON.HOUR.REQUIRED,
  }),
  etc: Joi.string().messages({
    'string.base': RESERVATION_MESSAGES.RESERVATION.COMMON.ETC.BASE,
  }),
});

export const createReservationValidator = async (req, res, next) => {
  try {
    await createReservationSchema.validateAsync(req.body);
    next();
  } catch (error) {
    return res.status(HTTP_STATUS.BAD_REQUEST).json({
      status: HTTP_STATUS.BAD_REQUEST,
      message: error.details[0].message,
    });
  }
};

profile
조금씩 정리하자!!!

0개의 댓글