class-transformer, class-validator

nn·2022년 12월 7일
0

Request.params은 string 타입으로 밖에 받아올 수 없다.

params로 받아온 postId는 number타입으로 사용하기 위해선 어떻게 해야할까?

시도1 .

이 자료 를 참고해보면 리퀘스트 타입을 커스텀해서 사용하는 것이 맞는 것처럼 보였다.

아래처럼 커스텀 해보았는데.. 🤔 뭔가 이상하다. 일일히 커스텀 하지 않아도 될 방법을 찾아보자

시도 2 .

해결 방법은 class-transformer에 있었다!

이를 알기위해선 역직렬화에 대해서 먼저 알고 있어어한다.

클라이언트에서 서버에서 데이터 요청을 보낼땐 보통 Json 객체가 들어온다. (그렇기 때문에 리퀘스트 파람이나 쿼리는 무조건 스트링이 들어 올 수 밖에 없음!)

이 제이슨 객체의 타입을 변경하거나 하는 등의 가공을 하기위해선 제니슨 객체를 클래스의 인스턴스로 바꿔 전달을 받아야한다.
이를 역직렬화라고한다.

반대로 클래스를 json객체로 바꾸는 것은 직렬화라고 한다.
ex) 쿼리 결과를 그대로 클라이언트에 보내주면 디비 구조가 노출 될 가능성이 있다. (sql Injection 방지를 위해 가공할 필요성이 있음)

하지만 일일히 제이슨 객체를 가공하려면 굉장히 번거로울 것이고
그 코드는 상태와 행위가 따로 노는 응집력이 떨어지는 코드가 될 것이다.

아래처럼 클래스 내부에 가공 로직 두어 응집력 있는 코드를 만들 수 있다.

export class Post {
// 상태
  id: number
  userId: string
  content: string

// 행위
  getId() {
    return parseInt(id);
  }
}

제이슨 객체로 id, userId, content가 온다면 이를 클래스 내부의 멤버 변수로 두고 id의 타입을 변경해주는 함수를 생성하자.

이렇게 상태와 행위를 한 공간에 둘 수 있는 것이 클래스의 장점이다.
이로서 다른 곳에서 불필요하게 데이터를 가공하는 로직을 줄게 할 수 있다.

2-1.

그럼 이걸 어떻게 적용해야할까?

class-transformer는 제이슨 객체를 자동으로 클래스로 변환해준다!

그럼 제이슨 객체를 사용하는 것이아니라 class-transformer가 변환해준 인스턴스를 꺼내 사용하면 된다.

const jsonData = req.params;
const taransformer  = plainToInstance(Post, jsonData)

plainToInstance 함수를 통해 jsonData를 Post클래스의 인스턴스로 쉽게 변환할 수 있다.


나는 class-validator를 함께 사용하여 미들웨어에서 직렬화를 사용했다.

export const DtoValidatorMiddleware = (
 type: any,
 skipMissingProperties = false
) => {
 return (req: Request, res: Response, next: NextFunction) => {
   const dto = plainToInstance(type, req.body);
   validateOrReject(dto, { skipMissingProperties })
 	...

plainToInstance는 클래스에@Expose()를 기준으로 변환 해주지만 특별히 선언 해주지 않아도 클래스 속성에 정의되어있다면 변환해준다. (나의 경우엔 일단 모두 지정해주었다.)

하지만 이름/컨벤션이 다른 경우엔 필수로 지정해주어어야한다.

반대로 @Exclude()는 변환대상에서 제외해준다.

또한 @Type() 을 이용해 무슨 클래스 타입인지 명시할 수 있으므로 아래와같이 타입 변환이 필요한 속성에 지정해주었다.

  export class PostDto implements Post {
  @Expose()
  @IsNumber()
  @Type(() => Number)
  public id: number;

  @Expose()
  @IsNumber()
  @Type(() => Number)
  public userId: number;

  @Expose()
  @IsString()
  public content: string;

  @Expose()
  public status?: postStatus;

  @Expose()
  paramToNumber(param: string): number {
    return parseInt(param);
  }
...

참고자료

profile
내가 될 거라고 했잖아

0개의 댓글