관리 메뉴

제뉴어리의 모든것

DTO 값 검증 처리 방법 본문

Spring Boot

DTO 값 검증 처리 방법

제뉴어리맨 2021. 4. 21. 20:40

Spring Boot 프로젝트에서 DTO 검증 방법

 

  • 환경

OS : Windows10

IDE : Intellij 2020.03

Spring Boot : 2.4.5

 

  • 프로젝트 구조

 

 

  • Dependency
    implementation 'org.webjars:jquery:3.1.1-1' 
    
    //@Valid, @Size, @NotEmpty 등등 현재 예제에서 소개하는 검증에 대한 디펜던시
    implementation 'org.springframework.boot:spring-boot-starter-validation' 
    
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'

 

  • 예제 프로젝트 흐름도

 

  • 구현

1. 기본 패키지에 controller, dto 패키지 추가

2. controller 패키지에 HomeController, MemberController Java 클래스 생성

package com.january.dto_valid.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;

@Controller
public class HomeController {

    @GetMapping("/")
    public String home(@ModelAttribute("result") String result)
    {
        return "index";
    }
}
package com.january.dto_valid.controller;
import com.january.dto_valid.dto.MemberDTO;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

@RequestMapping("/member")
@Controller
public class MemberController {

    @GetMapping("/register")
    public String register(@ModelAttribute("memberDTO") MemberDTO memberDTO)
    {
        return "/member/register";
    }


    @PostMapping("/register")
    public String register(@Valid MemberDTO memberDTO,
                           Errors errors, RedirectAttributes redirectAttributes, Model model){

        if(errors.hasErrors())
        {
            //기존 데이터 그대로
            model.addAttribute("memberDTO", memberDTO);

            // 유효성 통과 못한 필드와 메시지를 핸들링
            Map<String, String> validatorResult = validateHandling(errors);
            for (String key : validatorResult.keySet()) {
                model.addAttribute(key, validatorResult.get(key));
            }

            return "/member/register";
        }

        redirectAttributes.addAttribute("result", "valid value!");
        return "redirect:/";
    }

    public Map<String, String> validateHandling(Errors errors) {

        Map<String, String> validatorResult = new HashMap<>();

        for(FieldError error : errors.getFieldErrors())
        {
            String fieldName = String.format("valid_%s",error.getField());
            validatorResult.put(fieldName,error.getDefaultMessage());
        }

        return validatorResult;
    }

}

위에 소스에서 주요한 내용은

public String register(@Valid MemberDTO memberDTO,
Errors errors, RedirectAttributes redirectAttributes, Model model)

입니다.

- @Valid :

검증할 DTO에 @Valid 라는 어노테이션을 달아주었습니다. 해당 어노테이션을 달아주어야

DTO 클래스에서 검증을 적용한 변수에 대하여 검증을 실시합니다. 

 

- Errors :

Errors 라는 타입의 변수를 Controller 메소드의 파라미터로 정의 해주어야 검증과정에서 발생된 에러에 대하여 알 수있습니다.

 

그리고 아래의 내용은

회원가입 페이지에서 입력받은 내용을 그래도 다시 Model에 담아서 <input> 에 출력해주려는 의도입니다.그리고 validateHandling 라는 메소드에서는 각 필드(input 항목)에 대한 에러 메세지를 Map형태로 반환해줍니다.그리고 각 key,value를 Model 객체에 넣어서 View로 전달해 줍니다.

    if(errors.hasErrors())
        {
            //기존 데이터 그대로
            model.addAttribute("memberDTO", memberDTO);

            // 유효성 통과 못한 필드와 메시지를 핸들링
            Map<String, String> validatorResult = validateHandling(errors);
            for (String key : validatorResult.keySet()) {
                model.addAttribute(key, validatorResult.get(key));
            }

            return "/member/register";
        }

 

 

3. dto 패키지에 MemberDTO Java 클래스 생성

package com.january.dto_valid.dto;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Setter
@Getter
public class MemberDTO {

    @Size(min = 2, max = 20)
    @NotEmpty(message = "id는 빈값 일 수 없습니다")
    @NotNull(message = "id는 Null 일 수 없습니다")
    private String id;

    @Size(min = 8, max = 12)
    @NotEmpty(message = "비밀번호는 빈값 일 수 없습니다")
    @NotNull(message = "비밀번호는 Null 일 수 없습니다")
    private String password;

    @Size(min = 2, max = 8)
    @NotEmpty(message = "이름은 빈값 일 수 없습니다")
    @NotNull(message = "이름은 Null 일 수 없습니다")
    private String name;


    @Email
    @Size(min = 12, max = 20)
    @NotEmpty(message = "이메일은 빈값 일 수 없습니다")
    @NotNull(message = "이메일은 Null 일 수 없습니다")
    private String email;

}

@Size : 최소, 최대 길이에 대하여 정의를합니다

@NotEmpty : ""와 같이 빈값이 되어선 안됨을 의미합니다

@NotNull : Null을 허용하지 않음을 의미합니다.

@Email : Email형식으로 입력 받아야함을 나타냅니다.

 

각 어노테이션마다의 message는 검증에 실패하였을때 발생되는 Error 메세지입니다.

 

4. /template/member 패키지에 register.html와 /template에 웰컴페이지(index.html) 생성

 

register.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">
	<title>register</title>
</head>
<body>
<form th:action="@{/member/register}" method="post">
	<div>
		<label>ID : </label>
		<input type="text" name="id" th:value="${memberDTO.id}">
		<span th:text="${valid_id}" style="color: red"></span>
	</div>

	<div>
		<label>PWD : </label>
		<input type="text" name="password" th:value="${memberDTO.password}">
		<span th:text="${valid_password}" style="color: red"></span>
	</div>

	<div>
		<label>NAME : </label>
		<input type="text" name="name" th:value="${memberDTO.name}">
		<span th:text="${valid_name}" style="color: red"></span>
	</div>

	<div>
		<label>EMAIL : </label>
		<input type="text" name="email" th:value="${memberDTO.email}">
		<span th:text="${valid_email}" style="color: red"></span>
	</div>

	<button type="submit"> 등록 </button>
</form>

</body>
</html>

위 소스에서 주요할점은

<input type="text" name="id" th:value="${memberDTO.id}">와 같이 ${memberDTO.id}값이존재하다면

value로써 정의해주었으므로, 잘못된 값을 입력하여 본 페이지로 다시 돌아왔을때 입력하였던 값을 그대로 다시 input에 출력해 줍니다.

 

그리고 

<span th:text="${valid_id}" style="color: red"></span> 와 같이 ${valid_id} 란 값을 span을 이용하여 나타냅니다.

당연히 최초 가입 페이지 입장시에는 ${valid_id}란 값은 존재하지 않기때문에 아무런 텍스트도 출력되지 않습니다.

 

 

index.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="UTF-8">

	<script th:src="@{/webjars/jquery/3.1.1-1/jquery.min.js}"></script>
	<title>index</title>
</head>
<body>
<h2>웰컴 페이지</h2>

<a th:href="@{/member/register}">
	<h1>가입하기</h1>
</a>

<script th:inline="javascript">
   
    $(document).ready(function () {
	    const result = [[${result}]];
	    if(result != "")
	    {
	        alert(result);
	    }
	})
</script>
</body>
</html>

가입 페이지에서 각 input들이 모두 검증에 통과되었을때 

[[${result}]] 의 값이 넘어오기 때문에 alert창을 띄우고 성공했음을 알립니다.

 

  • 결과

- 웰컴 페이지 입장

- 가입 페이지 입장

- 아무런 값도 입력하지 않고 등록버튼 클릭시

- 유효한 값 입력 상황

- 유효한 값 입력 후 등록 버튼 클릭시

 

 

github url : unool/dto_valid (github.com)