@RequestParam에서의 예외처리 방법을 알아보자

Terror·2024년 11월 10일

최종 프로젝트

목록 보기
23/28

Overivew

  • RequestParam에서의 잘못된 인자값에대한 예외처리를 수행 할 수 있습니다

시나리오

  • 자신의 북마크를 조회하는 컨트롤러에서, 최대 10페이지까지만 조회가능할때 11페이지가 인자값으로 들어온 시나리오

기존 DTO에서의 해결방법

    @Operation(summary = "북마크 토글", description = "북마크 존재시 -> 삭제 / 없을시 -> 생성")
    @PostMapping
    public ResponseEntity<ApiResponse<Void>> togleBookmark(
            @Valid @RequestBody BookmarkRequest.BookmarkRequestCreate reqDto,
            @AuthenticationPrincipal AuthUser authUser
    ){
        ApiResponse<Void> apiResponse = bookmarkService.togleBookmark(reqDto,authUser);
        return ApiResponse.of(apiResponse);
    }
  • 기존에는 이런식으로 @Valid 어노테이션을 넣어주고, 실제 BookmarkRequestCreate DTO안에 @NotNull 같은어노테이션을 넣어준다음
  • GlobalException에서 DTO Exception을 잡아주는 형식으로 처리하였다
    // DTO exception custom
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> dtoException(MethodArgumentNotValidException e) {
        FieldError fe = e.getBindingResult().getFieldError();
        ApiResponse<Void> apiResponse = new ApiResponse<>(HttpStatus.BAD_REQUEST.value(), fe.getDefaultMessage(),null);
        return ApiResponse.of(apiResponse);
    }

하지만 ReqeustParam은?

  • RequestParam 같은경우 DTO를 사용하지않고, 인자값으로 하나하나 받다보니 다른 방식을 취해줘야한다

    • 기존 컨트롤러위에 @Validated 어노테이션을 추가해준다

          @Operation(summary = "자신의 모든 북마크들 가져오기", description = "자신의 북마크 다건 조회 API")
          @GetMapping
          public ResponseEntity<ApiResponse<Page<BookmarkResponse>>> getAllBookmarks(
                  @Max(value = 10, message = "페이지 최댓값이 10페이지를 넘을 수 없습니다") @RequestParam(defaultValue = "0", required = false) int page,
                  @RequestParam(defaultValue = "10", required = false) int size,
                  @RequestParam(defaultValue = "desc", required = false) String sort,
                  @RequestParam(required = false) BookmarkTargetType target,
                  @AuthenticationPrincipal AuthUser authUser
          ){
              Sort.Direction direction = Sort.Direction.fromString(sort);
              Pageable pageable = PageRequest.of(page, size, direction,"createdAt");
              ApiResponse<Page<BookmarkResponse>> apiResponse = bookmarkService.getAllBookmarksByUser(pageable, authUser, target);
              return ApiResponse.of(apiResponse);
          }
    • @RequestParam에 직접 Validate를 체크하는 어노테이션을 추가해준다

    • GlbalException에 아래의 코드를 추가해준다

          @ExceptionHandler(ConstraintViolationException.class)
          public ResponseEntity<ApiResponse<Void>> handleConstraintViolation(ConstraintViolationException e) {
              String errorMessage = e.getConstraintViolations().stream()
                      .map(violation -> violation.getMessage())
                      .findFirst()
                      .orElse("Invalid input");
      
              ApiResponse<Void> apiResponse = new ApiResponse<>(HttpStatus.BAD_REQUEST.value(), errorMessage, null);
              return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(apiResponse);
          }
    • ApiResponse는 내가 만든 반환타입이니 적당히 수정해주길바란다 !

    • 그렇다면 Postman으로 11페이지를 요청해보자

결과

  • 우리가 책정한 대로의 결과가 잘 나오는것을 볼 수 있다 (굳)

근데 @Validated 어노테이션은 무슨 역할을 하는거지 ?

  • 메서드나, 파라미터에서 직접 Validate를 처리해주어야할때 필요한 어노테이션 이라고한다
  • 만약 저게없었다면, 기존의 @Max 어노테이션은 제기능을 하지못했을것이다
  • ... 과연그럴까? 한번 빼보고 해보자

오 진짜네

  • 빼고 했더니, 아무것도 나오지않는 모습이다

근데 RequestParam도 DTO형태로 받을 수 없을까?

  • 물론 가능하다 나와같이 알아보자

수정 전 컨트롤러코드

    @Operation(summary = "자신의 모든 북마크들 가져오기", description = "자신의 북마크 다건 조회 API")
    @GetMapping
    public ResponseEntity<ApiResponse<Page<BookmarkResponse>>> getAllBookmarks(
            @Max(value = 10, message = "페이지 최댓값이 10페이지를 넘을 수 없습니다") @RequestParam(defaultValue = "0", required = false) int page,
            @RequestParam(defaultValue = "10", required = false) int size,
            @RequestParam(defaultValue = "desc", required = false) String sort,
            @RequestParam(required = false) BookmarkTargetType target,
            @AuthenticationPrincipal AuthUser authUser
    ){
        Sort.Direction direction = Sort.Direction.fromString(sort);
        Pageable pageable = PageRequest.of(page, size, direction,"createdAt");
        ApiResponse<Page<BookmarkResponse>> apiResponse = bookmarkService.getAllBookmarksByUser(pageable, authUser, target);
        return ApiResponse.of(apiResponse);
    }

수정 후 컨트롤러코드

    /**
     * 로그인 되있는 자신의 북마크들 목록 확인
     */
    @Operation(summary = "자신의 모든 북마크들 가져오기", description = "자신의 북마크 다건 조회 API")
    @GetMapping
    public ResponseEntity<ApiResponse<Page<BookmarkResponse>>> getAllBookmarks(
            @ModelAttribute BookmarkRequestSearch request,
            @AuthenticationPrincipal AuthUser authUser
    ){
        Sort.Direction direction = Sort.Direction.fromString(request.getSort());
        Pageable pageable = PageRequest.of(request.getPage(), request.getSize(), direction,"createdAt");
        ApiResponse<Page<BookmarkResponse>> apiResponse = bookmarkService.getAllBookmarksByUser(pageable, authUser, request.getTarget());
        return ApiResponse.of(apiResponse);
    }
  • 저기서 유효성검증을 하고싶다면, ModelAttribute앞에 @Valid를 붙이고, 똑같이 DTO앞에 어노테이션을 달아주면 된다!

ModelAttribute란?

  1. 요청파라미터 바인딩
    • 요청에서 전달된 파라미터 값들을 자동으로 객체의 필드에 바인딩합니다, 예를들어 GET요청의 쿼리파라미터나 POST요청의 폼 데이터를 객체에 매핑해줍니다

끝!

profile
테러대응전문가

0개의 댓글