본문 바로가기
[study]이론정리/Spring Boot

Spring MVC - 폼유효성체크

by yoon9i 2024. 7. 3.

17> 폼 유효성 체크
(1) 개요
- 사용자 입력 폼의 데이터 유효성 검사 의미.
ex>
    필수사항(null허용 유무,...), 날짜(미래날짜만 가능), 값의 길이 n 개 이상, ....

(2) 구현 방식
- html 속성 이용
- JS 이용
==> html 및 JS 는 전부 클라이언트에서 처리하는 방법이다.
    클라이언트에서 JS 비활성화 시키거나 보안 이슈 발생 가능성이 높음.

- Spring Boot 이용
==> 서버에서 유효성 체크하는 방법이다.

(3) 적용
가. 의존성 설정

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
	http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.18</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.exam</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>11</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-devtools</artifactId>
		</dependency>
	<dependency>
		<groupId>org.apache.tomcat.embed</groupId>
		<artifactId>tomcat-embed-jasper</artifactId>
		<scope>provided</scope>
	</dependency> 
	<dependency>
		<groupId>javax.servlet</groupId>
		<artifactId>jstl</artifactId>
	</dependency>
    <dependency>
	    <groupId>org.webjars</groupId>
	    <artifactId>jquery</artifactId>
	    <version>3.7.1</version>
	</dependency>
        <!-- https://mvnrepository.com/artifact/org.webjars/bootstrap -->
	<dependency>
	    <groupId>org.webjars</groupId>
	    <artifactId>bootstrap</artifactId>
	    <version>5.3.3</version>
	</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>



나. DTO 생성 (Command Bean 이라고 하거나 form backing object:양식보조객체 라고도 함)
==> 변수에 어노테이션으로 유효성 조건을 지정한다.

  public class MemberDTO {

    String userid;
    String username;
    
    LocalDate targetDate;

    // 생성자
    // getter & setter
    // toString
  }

  유효성 조건 지정

  // Command Bean, Form backing object
  public class MemberDTO {
    
    @NotNull
    String userid;
    
    @Size(min = 3, message = "3글자 이상입니다.")
    String username;
    
    // 010-1234-4567 휴대폰 정규 표현식
  // @Pattern(regexp = "/^\\d{3}-\\d{3,4}-\\d{4}$/")
  // String phone;
    
    @NotNull // 혼합도 가능(Not null 이면서 @FutureOrPresent 인 조건)
  // @Future // 현재시간기준으로 미래날짜만설정
    @FutureOrPresent(message = "현재 또는 미래의 날짜만 가능합니다.")
    LocalDate targetDate;

    // 생성자
    // getter & setter
    // toString
  }

package com.exam.dto;

import java.time.LocalDate;


import javax.validation.constraints.FutureOrPresent;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;

// Command Bean, Form backing Object
public class MemberDTO {

	//@NotNull  // null 금지 
	@NotEmpty(message = "userid 필수입니다.") // null, 빈문자열 금지
	//@NotBlank  // null 빈문자열 space 금지
	String userid;
	
	@Size(min = 3, message = "3글자 이상입니다.")
	String username;
	
	// 010-1234-4567
	//@Pattern(regexp = "/^\\d{3}-\\d{3,4}-\\d{4}$/")
	//String phone;
	
	@NotNull
	@FutureOrPresent(message = "현재 또는 미래의 날짜만 가능합니다.")
	LocalDate targetDate;

	public MemberDTO() {}

	public MemberDTO(String userid, String username, LocalDate targetDate) {
		this.userid = userid;
		this.username = username;
		this.targetDate = targetDate;
	}

	public String getUserid() {
		return userid;
	}

	public void setUserid(String userid) {
		this.userid = userid;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public LocalDate getTargetDate() {
		return targetDate;
	}

	public void setTargetDate(LocalDate targetDate) {
		this.targetDate = targetDate;
	}

	@Override
	public String toString() {
		return "MemberDTO [userid=" + userid + ", username=" + username + ", targetDate=" + targetDate + "]";
	}
}



다. Controller 작업
https://carrotweb.tistory.com/256 : Controller -> jsp 관련해서 참고했음.
- 양방향 적용(two-way binding)

  Model 생성
  Controller ----------------------------> jsp
             <----------------------------

- GET 방식과 POST 방식 요청에 전부 Command Bean(DTO) 을 모델로 사용해야 된다.

                    화면요청(GET 방식)
  Controller ----------------------------> jsp
             <----------------------------
                    실제요청(POST 방식)

  GET 방식

    // 화면요청(get)
    @GetMapping("/member")
    public String memberForm(ModelMap m) {
      
      MemberDTO dto = new MemberDTO();
      // MemberDTO dto = new MemberDTO("aaa", "홍길동", LocalDate.now());
      // m.addAttribute("memX", dto); // 임의의 값 지정하면 에러 발생
      m.addAttribute("memberDTO", dto); // Command bean 으로 반드시 설정한다.
      
      
      return "memberForm"; 
    }

  POST 방식

    // 처리해주는 post 요청
    @PostMapping("/member") 
    public String member(@Valid MemberDTO dto, BindingResult result) {
      // @Valid 붙여야지만 유효성체크한것이 적용됨.
      // BindingResult 가 에러발생 확인
      
      System.out.println("MemberDTO" + dto);
      
      if(result.hasErrors()) { // 에러가 발생시 실행
        return "memberForm";
      }
      
      return "memberInfo"; 
    }

package com.exam.controller;

import java.time.LocalDate;

import javax.validation.Valid;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.exam.dto.MemberDTO;

@Controller
public class MainController {
	
	Logger logger = LoggerFactory.getLogger(getClass());
	
	@GetMapping("/member")  // 화면요청
	public String memberForm(ModelMap m) {
		
		MemberDTO dto = new MemberDTO();
//		MemberDTO dto = new MemberDTO("aaa","홍길동", LocalDate.now());
//		m.addAttribute("memX", dto);  // 임의의 값 지정하면 에러 발생
		m.addAttribute("memberDTO", dto);  // command bean으로 반드시 설정한다.
		
		return "memberForm"; 
	}
	
	@PostMapping("/member") // 로직요청
	public String member(@Valid  MemberDTO dto, BindingResult result) {
		System.out.println("MemberDTO" + dto);
		if(result.hasErrors()) {
			return "memberForm"; 
		}
		
		return "memberInfo"; 
	}
	
	
}



  memberForm.jsp

    <%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>

    <h2>회원가입폼</h2>
    <form:form method="post" modelAttribute="memberDTO">
      아이디: <form:input type="text" path="userid" />
          <form:errors path="userid"></form:errors>
      <br>
      이름: <form:input type="text" path="username" />
        <form:errors path="username"></form:errors>
      <br>
      날짜: <form:input type="date" path="targetDate" />
        <form:errors path="targetDate"></form:errors>
      <br>
      <input type="submit" value="가입">
    </form:form>

<%@ page 
         contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"        
%>
<%@ taglib uri="http://www.springframework.org/tags/form" 
    prefix="form"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>회원가입폼</h2>
<form:form  method="post" 
           modelAttribute="memberDTO">
 아이디:<form:input type="text" path="userid" />
      <form:errors path="userid"></form:errors>
 <br>
 이름:<form:input type="text" path="username" />
      <form:errors path="username"></form:errors>
 <br>
 날짜:<form:input type="date" path="targetDate" />
      <form:errors path="targetDate"></form:errors>
 <br>
 <input type="submit" value="가입">
</form:form>
</body>
</html>

 

package com.exam;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}
<%@ page 
         contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"        
%>
<%@ taglib uri="http://www.springframework.org/tags/form" 
    prefix="form"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>memberInfo</h2>

</body>
</html>
# application.properties
logging.level.org.springframework=info

# tomcat port 번호 변경
server.port=8090

# context 명 변경
server.servlet.context-path=/app

# jsp의 경로와 확장자 지정
spring.mvc.view.prefix=/WEB-INF/views/
spring.mvc.view.suffix=.jsp

# LocalDate 타입의 날짜 포맷 지정
spring.mvc.format.date=yyyy-MM-dd