본문 바로가기

웹/Nest

Nestjs를 사용할 때 controller에서 400 Validation failed (numeric string is expected) 에러 발생 시

🟩 문제 상황

예시 코드

@Controller('tasks')
@UseGuards(AuthGuard())
export class TasksController {
  constructor(private tasksService: TasksService) { }

  @Get('/:id')
  getTaskById(@Param('id', ParseIntPipe) id: number): Promise<Task> {
    return this.tasksService.getTaskById(id);
  }

  @Get('/week')
  getTasksByWeek(@GetUser() user: User): Promise<Task[]> {
    return this.tasksService.getTasksByWeek(user);
  }
}

이렇게 컨트롤러를 만들었었습니다.

그리고 /tasks/week를 호출하면
400 Validation failed (numeric string is expected) Validation failed (numeric string is expected)
에러가 계속해서 나옵니다.

이 문제를 해결하는 과정 입니다.

🟩 문제 해결

스택오버 플로우 링크
위 링크를 보고 해답을 얻었습니다.

컨트롤러는 /tasks 컨트롤러에 진입하면 /:id를 첫 번째로 만납니다.
그래서 컨트롤러는 첫 번째로 만난 id에 week를 대입합니다.

그래서 문제가 생긴거였습니다. id는 숫자형태의 문자(numeric string) ex "21"를 줘야 하는데 "week" 문자열이 왔으니깐요.

🟩 해결

제가 생각한 해결방법이 2가지 있습니다.

🟦 순서 바꾸기

@Controller('tasks')
@UseGuards(AuthGuard())
export class TasksController {
  constructor(private tasksService: TasksService) { }

  @Get('/week')
  getTasksByWeek(@GetUser() user: User): Promise<Task[]> {
    return this.tasksService.getTasksByWeek(user);
  }

  @Get('/:id')
  getTaskById(@Param('id', ParseIntPipe) id: number): Promise<Task> {
    return this.tasksService.getTaskById(id);
  }  
}

컨트롤러가 가장 먼저 /week를 만나게끔 합니다.
새로운 컨트롤러를 만들 때 동적 경로 매개변수(패스 매개변수)는 아래쪽에 위치 시킵니다.

🟦 명확하게 라우팅 하기

@Controller('tasks')
@UseGuards(AuthGuard())
export class TasksController {
  constructor(private tasksService: TasksService) { }

  @Get('/week')
  getTasksByWeek(@GetUser() user: User): Promise<Task[]> {
    return this.tasksService.getTasksByWeek(user);
  }

  @Get('/id/:id')
  getTaskById(@Param('id', ParseIntPipe) id: number): Promise<Task> {
    return this.tasksService.getTaskById(id);
  }  
}

패스 매개변수를 받는 컨트롤러에 명확하게 패스 매개변수를 적는 것 입니다.
nestjs에서 경로는 컨트롤러의 선언 순서 보다는 경로 패턴의 구체성이 우선시 됩니다.

id를 찾로 찾고 싶으면 "/id/21" 이렇게 명확하게 분리할 수 있을 것 입니다.

728x90