nest.js @Body() 인자값 전부 통과하는 문제

이건선·2023년 4월 8일
0

해결

목록 보기
20/48

문제점

  • 아래와같이 클래스로 정의된 Dto <"Data Transfer Object"> 가 존재
  • 코드 실행시 Dto로 정의 된 값만 가져오기를 기대함.
  • 그러나 모든 BODY를 가져오는 문제 발생함.
  • Dto가 존재하는데 모든 BODY를 가져오면 지정한 프로퍼티만 가져온다는 Dto의 의미가 희석되지 않나 생각이 들었다.
    export class CardPostsDto {
      @IsNotEmpty()
      maincategory: string;
    
      @IsNotEmpty()
      category: string;
    
      @IsNotEmpty()
      @IsNumber()
      splitNumber: number;
    
      @IsNotEmpty()
      @IsNumber()
      splitPageNumber: number;
    }
    
    export class CardPostsPageNation extends PickType(CardPostsDto, [
      'category',
      'maincategory',
    ]) {}
    
    export class UpdateCatAgeDto extends PickType(CardPostsDto, [
      'splitPageNumber',
    ]) {}
  • 아래는 위 Dto를 적용한 코드와 실행 결과입니다.
    @Post('test')
      @UsePipes(ValidationPipe)
      async testPageNation(
        @Query()
        cardPostsPageNation: CardPostsPageNation,
        @Body()
        updateCatAgeDto: UpdateCatAgeDto,
      ) {
    		// return 값은 Dto로 지정한 프로퍼티만 가져오기를 기대함
        return { updateCatAgeDto, cardPostsPageNation };
      }
    // 실행 결과
    {
        "updateCatAgeDto": {
            "maincategory": "maincategory111",
            "category": "sgsdgsd",
            "splitNumber": "11", // 의도하지 않은 값
            "splitPageNumber": 11, // 의도하지 않은 값
            "badprop": "11" // 의도하지 않은 값
        },
        "cardPostsPageNation": {
            "maincategory": "유머", // 의도하지 않은 값
            "category": "스포츠", // 의도하지 않은 값
            "splitNumber": "3", // 의도하지 않은 값
            "splitPageNumber": "2",
            "badquery": "242" // 의도하지 않은 값
        }
    }

시도 해본 것

파라미터는 어떻게 동작하고 있을까?

  1. 문제의 파라미터를 가지고 와서 프로퍼티를 찍어보면 내가 정의한 프로퍼티 2개가 들어있었다.

  2. 의도 하지않은 값 badprop을 출력해 보려고 하니 오류가 발생한다.


파라미터를 이용해서 실제로 배열을 생성한다면 결과가 다르지 않을까?

  1. 배열에 직접 입력해서 create

  2. 결과에 변화가 없었다.


혹시 내가 개념을 잘못 알고 있는 것일까?

  1. 타입 체크는 아니지만 express에서 비슷하게 테스트 해보았다.

  2. 일반 클래스와 빈 클래스 생성

  3. 결과

지금 겪고 있는 문제와 완전히 동일해 보였다.

  • 클래스로 정의된 값만 출력 되기를 기대함
  • 그러나 생성자를 만들 때 모든 req.body 인자를 입력 받는다.
  • 생성자의 프로퍼티를 확인해 보면 내가 의도한 프로퍼티만 확인 된다.
  1. 가설

    1. @Body()는 모든 req.Body를 인자로 가지고 오는 것이다.
    2. Dto는 근본적으로 class로 구성되어 있다.
    3. 따라서 Body 인자를 타입으로 정의했어도. 정의 되지 않은 프로퍼티의 입력은 자유로울 것이다.
      1. 의문 : 그렇다면 런타임 중에 입력된 프로퍼티는 참조 할 수 없는걸까? class에 정의 되지 않아서 메모리에 식별자가 등록되지 않았기 때문에 제외 되는 것인가?
    4. 그렇다면 파라미터로 인자가 들어올 때, 인자의 타입체크 및 class-validator는 작동하지 않는걸까?
    • 가설 d. 테스트
      // 테스트를 위한 설정
      ...
      @IsNotEmpty()
        maincategory: string;
      @IsNotEmpty()
        @IsNumber()
        splitPageNumber: number;
      ...
      
      // 테스트를 위해서 maincategory 프로퍼티 추가
      export class UpdateCatAgeDto extends PickType(CardPostsDto, [
        'maincategory',
        'splitPageNumber',
      ]) {}
      // splitPageNumber 제거 후 입력 
      {
        "maincategory": "maincategory111",
        "category": "sgsdgsd",
        "splitNumber": "dgd",
      
         "badprop" : "11"
      }
      
      // 출력
      "message": [
              "splitPageNumber must be a number conforming to the specified constraints",
              "splitPageNumber should not be empty"
          ],
      // splitPageNumber에 String 입력 
      {
        "maincategory": "maincategory111",
        "category": "sgsdgsd",
        "splitNumber": "dgd",
        "splitPageNumber": "not Num",
         "badprop" : "11"
      }
      
      // 출력
      "message": [
              "splitPageNumber must be a number conforming to the specified constraints"
          ],
      // @IsNumber() 제거 후 입력
      {
        "maincategory": 121,
        "category": "sgsdgsd",
        "splitNumber": "dgd",
        "splitPageNumber": "sgsdg",
          "badprop" : "11"
      }
      
      // 출력
      "updateCatAgeDto": {
              "maincategory": 121,
              "category": "sgsdgsd",
              "splitNumber": "dgd",
              "splitPageNumber": "sgsdg",
              "badprop": "11"
          },

    테스트 결과

    • class안에서 선언된 타입은 class가 type을 체크하지 못하고 전부 받아들였다.
    • 그러나 class-validator를 사용한 부분은 인자를 확인하고 틀릴 경우 오류를 내보냈다.

    가설 결론

    1. class 내의 프로퍼티 타입지정은 의미가 희미하다.
    2. 인자의 정확한 타입을 체크하기 위해서는 class-validator를 사용해서 올바른 타입을 지정해야 한다.
    3. @Body()를 인자로 받는 파라미터를 바로 쓰는 방법은 올바른 방법이 아니다?????

해결???

@Body()
    updateCatAgeDto: UpdateCatAgeDto,
  ) {
    const { splitPageNumber } = updateCatAgeDto;
    return { splitPageNumber };
  1. class-validator를 사용해서 class의 프로퍼티를 명확하게 지정하자
  2. @Body()의 인자를 가져오기 위해서는 해당 파라미터를 return값으로 바로 주는 것은 잘못이다????
  3. 1.의 문제를 해결하기 위해서 변수를 객체 구조 분해 할당 후에 사용해야 한다????

그렇다면 처음으로 돌아가서 아래와 같은 문제가 발생한다.

  • Dto가 존재하는데 지정한 프로퍼티만 가져오고 싶다는 Dto의 의미가 희석되지 않나 생각이 들었다.
  • Dto를 사용 함으로써, 객체 구조분해 할당을 하지 않고 파라미터를 바로 사용해서 코드를 간결하게 표현하고 싶었다.

어이없는 진짜 해결


app.useGlobalPipes(
  new ValidationPipe({
    transform: true,
    whitelist: true, // dto에서 명시한 데이터아니면 무시
  }),
);

그냥 화이트 리스트가 true가 아니어서 문제였음

profile
멋지게 기록하자

0개의 댓글