문자열을 Date 객체로 변환하기

Oneik·2024년 7월 25일
0

개요

클라이언트로부터 퀘스트 시작 날짜와 종료 날짜를 2024-07-25와 같은 문자열 형태로 받을 때, 이를 데이터베이스에 저장하기 위해 Date 객체로 변환해줄 필요가 있었다.

이 과정에서 class-transformer@Transform 데코레이터를 사용하여 데이터 변환을 시도했지만, 문제가 발생했다.

초기 접근 방식

처음에는 다음과 같이 @Transform 데코레이터를 사용하여 날짜 변환을 시도했다.
startDateendDateproperty에 담아서 넘겨주기 때문에, value를 추출해서 원하는 형태로 변환하도록 설정하였다.

// create-quest.request.ts
export class CreateQuestRequest {
 
 ...

  @IsDateString()
  @IsNotEmpty()
  @Transform((property) => {
    return toUTCStartOfDay(property.value);
  })
  startDate: Date;

  @IsDateString()
  @IsNotEmpty()
  @Transform((property) => {
    return toUTCEndOfDay(property.value);
  })
  endDate: Date;
}

검증 에러 발생

위와 같은 방식은 다음과 같은 검증 오류를 발생시켰다.

{
  "statusCode": 400,
  "timestamp": "2024-07-24T14:08:12.825Z",
  "path": "/quests",
  "method": "POST",
  "message": "Bad Request Exception",
  "details": [
    "startDate: startDate must be a valid ISO 8601 date string",
    "endDate: endDate must be a valid ISO 8601 date string"
  ]
}

상세 메세지만 봤을 때는 전달받은 valueISO 8601 형태가 아니기 때문에, 발생한 문제로 생각했지만, 디버깅 과정에서 class-transformerclass-validator동작 순서가 예상과 달랐음을 발견했다.

@IsDateString() 데코레이터를 통해 value를 검증 후, @Transform 데코레이터를 이용하여 변환될 것으로 예상했는데, 먼저 @Transform 데코레이터를 통해 Date 객체로 변환되었다.

그 후 @IsDateString() 데코레이터를 통해 검증하기 때문에, 오류가 발생한 것이다.

class-transformer와 class-validator의 동작 순서

위 문제의 핵심은 NestJS에서 class-transformerclass-validator의 동작 순서에 있다.

클라이언트로부터 HTTP 요청이 서버로 전송되고, class-transformer를 이용해 요청의 JSON 형태인 body 값을 지정된 클래스의 인스턴스로 변환한다.

그 후, class-validator를 이용하여 생성된 인스턴스에 대해 유효성 검사를 수행하게 된다.

따라서, class-transformer를 이용하여 인스턴스로 변환하는 과정에서 @Transform 데코레이터가 먼저 동작하게 되는 것이다

해결책: 커스텀 데코레이터 생성

이 문제를 해결하기 위해 커스텀 데코레이터 @TransformDateToUTC를 만들었다.
이 데코레이터는 전달받은 option에 따라 value를 원하는 형태로 변환하는 데코레이터다.

export type TransformDateOptions = {
  option: 'start' | 'end';
};

  

export function TransformDateToUTC({ option }: TransformDateOptions): PropertyDecorator {
	return Transform(({ value }) => {
		const regex = /^\d{4}-\d{2}-\d{2}$/;
		if (typeof value !== 'string' && !regex.test(value) && !dayjs(value).isValid()) {
		throw new BadRequestException(`Please provide only date like 'YYYY-MM-DD'`);
		}
		return option === 'start' ? toUTCStartOfDay(value) : toUTCEndOfDay(value);
});
}

ts
// create-qeust.request.ts

export class CreateQuestRequest {
 
 ...

	@TransformDateToUTC({ option: 'start' })
	@IsNotEmpty()
	@IsDate()
	startDate: Date;
	
	@IsNotEmpty()
	@TransformDateToUTC({ option: 'end' })
	@IsNotEmpty()
	@IsDate()
	endDate: Date;
}

참고 자료

NestJS에서 응답/요청 객체 직렬화 (Serialization) 하기
NestJS class-validator와 transformer가 데코레이터와 다른점
[TS] class-validator의 활용과 검증 옵션

profile
초보 개발자의 블로그입니다

0개의 댓글

관련 채용 정보