REST API 에서 유효성 검증하기

Soo·2024년 3월 24일
post-thumbnail

이번에는 잘못된 데이터 요청이 왔을 경우 검증을 하는 법을 알아보겠습니다.

예를들어, 이름이 없고 현재 2024년이지만 생일이 2030년인 사람의 데이터를 생성하려고 할 때 생성이 되면 안되겠죠?? 이런 경우를 대비해서 정확한 데이터의 요청이 들어오는지 검증하는 법을 배우겠습니다.

먼저 포스트맨을 사용해서 데이터 요청을 해보겠습니다.

이름이 빈칸이고, 생일이 먼 미래이지만 201을 반환하면서 정상적으로 생성이됩니다. 이러면 안됩니다. 수정해 보겠습니다.

먼저, 유효성 검증을 위해서 의존성이 필요합니다.

Pom.xml

<!-- validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

UserResource

  • createUser() 파라미터 앞에 @Valid를 붙여줍니다.
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
    User savedUser = service.save(user);
    URI location = ServletUriComponentsBuilder.fromCurrentRequest()
            .path("{id}")
            .buildAndExpand(savedUser.getId())
            .toUri();
    return ResponseEntity.created(location).build();
}

@Valid : 주로 Spring Framework 등의 프레임워크에서 폼 입력값이나 API 요청의 데이터를 검증할 때 활용됩니다.

실행

검증이 잘 되어서 400이 반환됩니다. 사용자의 어떤 문제 때문에 400번이 응답된지는 알수가 없습니다. REST API는 사용자가 사용하기 편하게 만드는 것이 가장 중요합니다.

더 자세한 내용을 출력하기 위해 CustomizedResponseEntityExceptionHandler를 사용해서 메서드를 정의하겠습니다.

CustomizedResponseEntityExceptionHandler

  • 일반적으로 HTTP 요청이 컨트롤러의 핸들러 메서드에 도달할 때, Spring은 해당 요청의 파라미터를 검증하고, 검증에 실패한 경우 MethodArgumentNotValidException을 발생시킵니다.
  • 이떄 MethodArgumentNotValidException을 처리할 수 있는 메서드를 오버라이딩해서 BAD_REQUEST(400) 응답을 보내겠습니다.
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), ex.getMessage(),
            request.getDescription(false));
    return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}

실행

앞에서 만든 ErrorDetails 클래스를 사용해서 예쁘게 예외를 처리하는 모습입니다.
하지만 message를 보면 너무 많은 정보를 보여주고 있습니다.
User에 가서 Valid message를 변경해 보겠습니다.

User

  • name 속성에 message 추가하기
package study.rest.webservices.restfulwebservices.user;

import jakarta.validation.constraints.Past;
import jakarta.validation.constraints.Size;

import java.time.LocalDate;

public class User {

    private Integer id;

    @Size(min = 2, message = "이름은 최소 2글자 이상이어야 합니다.")
    private String name;

    @Past(message = "생일의 날짜는 과거여야 합니다.")
    
    private LocalDate birthDate;

    public User(Integer id, String name, LocalDate birthDate) {
        this.id = id;
        this.name = name;
        this.birthDate = birthDate;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public LocalDate getBirthDate() {
        return birthDate;
    }

    public void setBirthDate(LocalDate birthDate) {
        this.birthDate = birthDate;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", birthDate=" + birthDate +
                '}';
    }
}

CustomizedResponseEntityExceptionHandler

  • handleMethodArgumentNotValid() 수정
//수정 전
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), ex.getMessage(),
            request.getDescription(false));
    return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}

//수정 후
@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
    ErrorDetails errorDetails = new ErrorDetails(LocalDateTime.now(), ex.getFieldError().getDefaultMessage(),
            request.getDescription(false));
    return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}

수정 전 실행

  • User 클래스에서 작성한 message는 그대로 나오지만 아직도 message는 길게 나옵니다.

수정 후 실행

  • 사용자가 필요로 하는 예외 메세지만 출력됩니다.

0개의 댓글