Validation

사나이장대산·2024년 11월 13일

Spring

목록 보기
22/26

검증(Validation)

특정 데이터(주로 클라이언트의 요청 데이터)의 값이 유효한지 확인하는 단계를 의미한다.

Controller의 주요한 역할 중 하나는 Validation 이다. HTTP 요청이 정상인지 검증한다.

  • Validation이란?

    • 시스템이 미리 정의한 사양(specification)에 부합하고 있는지 검증하는 것
  • 식당 메뉴판(specification)에 있는 메뉴만 주문이 가능하다.

  • 안내를 통해 제대로된 주문을 받을 수 있다.

  • Validation을 사용하는 이유

  • 주문서 작성 페이지에서 잘못된 입력값으로 인해 서버에 오류가 발생한다면?
    ex) 휴대폰 번호에 숫자가 아닌 문자가 들어간 경우

  • 서버의 문제로 인해 작성 페이지에서 Error 페이지로 이동된다면?

  • Error 페이지로 이동되어 작성중인 폼이 모두 리셋되어 다시 작성해야 한다면?

  • 이러한 서비스의 유저는 굉장한 불편함을 겪게된다.

  • Validation의 역할

    1. 검증을 통해 적절한 메세지를 유저에게 보여주어야 한다.
    2. 검증 오류로 인해 정상적인 동작을 하지 못하는 경우는 없어야 한다.
    3. 사용자가 입력한 데이터는 유지된 상태여야 한다.

실제 서버를 운영하다보면 기능의 의도와 다른 다양한 사용 방법들을 보게된다. ex) Enter로 입력이 완료되도록 만들었지만 누군가는 Click, Tab + Enter를 누르듯

  • 검증의 종류

    1. 프론트 검증
      • 해당 검증은 유저가 조작할 수 있음으로 보안에 취약하다.
      • 보안에 취약하지만 그럼에도 꼭 필요하다 ex) 비밀번호에 특수문자가 포함되어야 한다면 즉각적인 alert 가능 → 유저 사용성 증가
    2. 서버 검증
      • 프론트 검증없이 서버에서만 검증한다면 유저 사용성이 떨어진다.
      • API Spec을 정의해서 Validation 오류를 Response 예시에 남겨주어야 한다.
        • API 명세서를 잘 만들어야 그에 맞는 대응을 할 수 있다.
      • 서버 검증은 선택이 아닌 필수이다.
    3. 데이터베이스 검증
      • Not Null, Default와 같은 제약조건을 설정한다.
      • 최종 방어선의 역할을 수행한다.

    기본적으로 프론트, 서버, 데이터베이스 모두 검증을 꼼꼼하게 하는것이 바람직하다. Validation으로 수많은 Error와 문제들을 방지할 수 있다.

BindingResult

Spring에서 기본적으로 제공되는 Validation 오류를 보관하는 객체이다. 주로 사용자 입력 폼을 검증할 때 많이 쓰이고 Field Error와 ObjectError를 보관한다.

  • BindingResult

  • Errors 인터페이스를 상속받은 인터페이스이다.

  • Errors 인터페이스는 에러의 저장과 조회 기능을 제공한다.

  • BindingResult는 addError() 와 같은 추가적인 기능을 제공한다.

    • Spring이 기본적으로 사용하는 구현체는 BeanPropertyBindingResult 이다.
  • 파라미터에 BindingResult가 없는 경우

    • 예시코드
      @Data
      public class MemberCreateRequestDto {
          private Long point;
          private String name;
          private Integer age;
      }
      // View 반환
      @Controller
      public class BingdingResultController {
      	
          @PostMapping("/v1/member")
          public String createMemberV1(@ModelAttribute MemberCreateRequestDto request, Model model) {
              // Model에 저장
              System.out.println("/V1/member API가 호출되었습니다.");
              model.addAttribute("point", request.getPoint());
              model.addAttribute("name", request.getName());
              model.addAttribute("age", request.getAge());
      
              // Thymeleaf Template Engine View Name
              return "complete";
          }
      
      }
      <!DOCTYPE html>
      <html xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
          <h1>Member 생성이 완료되었습니다!</h1>
          <ul>
              <li><span th:text="${point}">포인트</span></li>
              <li><span th:text="${name}">이름</span></li>
              <li><span th:text="${age}">나이</span></li>
          </ul>
      </body>
      </html>
  • Postman
    - 정상 요청

  • 정상 적인 응답이 온다.
  • viewName인 complete페이지로 이동한다.
  • 잘못된 요청(Long point 필드에 문자열 데이터)

  • 검증 오류(400 Bad Request)가 발생하고 Controller가 호출되지 않는다.

  • 파라미터에 BindingResult가 있는 경우

    주의사항 : BindingResult 파라미터는 검증대상 파라미터 뒤에 위치해야만 한다.

    • 예시코드
      @Controller
      public class BindingResultController {
      
          @PostMapping("/v2/member")
          public String createMemberV2(
                  // 1. @ModelAttribute 뒤에 2. BindingResult가 위치한다.
                  @ModelAttribute MemberCreateRequestDto request,
                  BindingResult bindingResult,
                  Model model
          ) {
      
              System.out.println("/V2/member API가 호출되었습니다.");
      
              // BindingResult의 에러 출력
              List<ObjectError> allErrors = bindingResult.getAllErrors();
              System.out.println("allErrors = " + allErrors);
      
              // Model에 저장
              model.addAttribute("point", request.getPoint());
              model.addAttribute("name", request.getName());
              model.addAttribute("age", request.getAge());
      
              return "complete";
          }
          
      }
  • Postman

  • 정상 요청

  • 정상 응답이 온다.
  • 잘못된 요청

  • @ModelAttribute 필드 or 객체에 파라미터 바인딩 오류가 발생
    - BindingResult에 오류가 보관되고 Controller는 정상적으로 호출된다.

@ModelAttribute는 파라미터를 필드 하나하나에 바인딩한다. 파라미터에 Binding Result가 함께 있는 경우 만약 그중 하나의 필드에 오류가 발생하면 해당 필드를 제외하고 나머지 필드들만 바인딩 된 후 Controller가 호출된다.

profile
사나이 張大山 포기란 없다.

0개의 댓글