Day 100

ChangWoo·2023년 1월 26일
0

중앙 HTA

목록 보기
44/51

폼 입력값 유효성 검증하기

1. 프론트엔드 폼 입력값 유효성 검증

* 폼에서 submit 이벤트 발생시 폼입력값을 검증하는 것

2. 벡엔드 폼 입력값 유효성 검증

* 폼 입력값을 저장하는 Form 객체에 각 멤버변수별로 입력값의 유효성응ㄹ 체크하는 어노테이션을 추가한다.
* 컨트롤러의 요청핸들러 메소드에서 @Valid 어노테이션을 이용해서 Form객체에 저장된 폼 입력값의 유효성을 검사한다.

* 폼 입력값 유효성 검증 절차
	1. 폼 입력값 유효성 체크를 지원하는 라이브러리 의존성을 추가한다.
  	<dependency>
     	<groupId>org.springframework.boot</groupId>
     	<artifactId>spring-boot-starter-validation</artifactId>
  	</dependency>

  • hibernate-validate라는 라이브러리가 추가된다.

  • validation-api = 표준이 정해져 있다.

  • hibernate-validate = 표준의 구현 중 하나다.

        * javax.validation.constraints에는 여러 어노테이션들이 정의되어 있다.
      	Ex) Email / NotBlank / Future / Past 등 필수로 들어갔으면 하는 어노테이션이 정의되어 있고 필요한 것을 추가하면 된다.
          
    		2. 입력폼에서 spring의 form 태그를 사용해서 입력폼과 입력필드를 구성한다.
    			* form 태그 = <form:form>태그, <form:input>태그, <form:password>태그, <form:errors>태그 등을 이용해서 입력폼과 입력필드를 구성한다.
      		3. 폼 입력값을 저장하는 Form 클래스의 멤버면수에 유효성 체크 규칙을 어노테이션을 이용해서 지정한다.
      			@NotNull, @NotEmpty, @NotBlank, @Email 등의 어노테이션을 이용해서 유효성 체크 규칙을 멤버변수별로 지정한다.
      		4. 요청핸들러 메소드에서 Form 객체에 저장된 폼 입력값에 대한 유효성 검사를 수행하고, 검사결과를 전달받기
      			@PostMapping("/register")
          		public String saveUser(@Valid UserRegisterForm userRegisterForm, BindingResult errors) {
          			if(errors.hasErrors()) {
              	// 유효성 검사를 통과하지 못한 경우(true가 나올 경우),  입력화면으로 내부이동시킨다.
                  	return"register-form";  //WEB-INF/views/register-form.jsp로 내부이동시킨다.
              }
              // 유효성 검사를 통과한 경우(false가 나올 경우),회원가입 업무로직을 호출한다.
              userService.registerUser(userRegisterForm);
          }
          * @Valid 어노테이션은 UserRegisterForm 객체에 저장된 폼 입력값에 대한 유효성 검사를 수행하게 하는 어노테이션이다.
          * userRegisterForm 과 BindingResult 사이에는 무엇이든 있으면 안된다.
          * BindingResult객체는 유효성 검사결과를 전달받는 객체다.

    폼입력값 검증에 활용되는 어노테이션

      @NotNull
      null 값을 허용하지 않는다.
      @NotEmpty
      	null 값을 허용하지 않으며, 최소 한 개 이상의 글자를 포함해야 한다.
      @NotBlank
      	null 값을 허용하지 않으며, 최소 한 개 이상의 글자(공백문자를 제외한)를 포함해야 한다.
      @Size(min=숫자, max=숫자)
      	문자열 혹은 배열의 최소 길이, 최대 길이를 지정한다.
      @Length(min=숫자, max=숫자)
      	문자열의 최소 길이, 최대 길이를 지정한다.
      @Min
      	최소 정수값을 지정한다.
      @Max
      	최대 정수값을 지정한다.
      @Pattern(regexp=정규표현식, flag={"i", "g", "m"})
      	문자열이 지정된 정규표현식과 일치해야 한다.
      @Email
      	문자열이 이메일 형식인지 체크한다.
      @URL
      	문자열이 URL 형식인지 체크한다.
      @Past
      	날짜가 현재시간보다 과거인지 체크한다.
      @Future
      	날짜가 현재시간보다 미래인지 체크한다.
      
register-fom.jsp
			<form:form modelAttribute="userRegisterForm" id="form-register" class="border bg-light p-3" method="post" action="register">
				<div class="mb-3">
					<label class="form-label">접속 권한</label>
					<div>
						<div class="form-check form-check-inline">
							<form:checkbox class="form-check-input" path="roleName" value="ROLE_GUEST" />
							<label class="form-check-label">게스트</label>
						</div>
						<div class="form-check form-check-inline">
							<form:checkbox class="form-check-input" path="roleName" value="ROLE_USER" />
							<label class="form-check-label">사용자</label>
						</div>
					</div>
					<form:errors path="roleName" cssClass="text-danger"/>
				</div>
				<div class="mb-3">
					<label class="form-label">아이디</label>
					<form:input class="form-control form-control-sm" path="id" />
					<form:errors path="id" cssClass="text-danger"/>
				</div>
				<div class="mb-3">
					<label class="form-label">비밀번호</label>
					<form:password class="form-control form-control-sm" path="password" />
					<form:errors path="password" cssClass="text-danger"/>
				</div>
				<div class="mb-3">
					<label class="form-label">이름</label>
					<form:input class="form-control form-control-sm" path="name" />
					<form:errors path="name" cssClass="text-danger"/>
				</div>
				<div class="mb-3">
					<label class="form-label">이메일</label>
					<form:input class="form-control form-control-sm" path="email" />
					<form:errors path="email" cssClass="text-danger"/>
				</div>
				<div class="mb-3">
					<label class="form-label">전화번호</label>
					<form:input class="form-control form-control-sm" path="tel" />
					<form:errors path="tel" cssClass="text-danger"/>
				</div>
				<div class="text-end">
					<a href="/home" class="btn btn-secondary btn-sm">취소</a>
					<button type="submit" class="btn btn-primary btn-sm">가입</button>
				</div>
			</form:form>
HomeController
// 회원가입 요청
	@PostMapping("/register")
	public String register(@ModelAttribute("userRegisterForm") @Valid UserRegisterForm userRegisterForm, BindingResult errors) { 
		//@Valid는 유효성 체크를 한다.(이 객체에 저장되어 있는 폼 입력값이 전달되면 유효성 체크를 한다.) / BindingResult는 유효성 검사 결과가 저장되는 객체다.
		
		if(errors.hasErrors()) { // 유효성 체크를 통과하지 못하면(true가 나오면), register-form을 반환한다.
			System.out.println(errors);
			return "register-form";
		}
		
		try {
			userService.registerUser(userRegisterForm);
		} catch (AleradeyRegisteredUserIdException ex) {
			errors.rejectValue("id", null, "이미 사용중인 아이디입니다.");
			return "register-form";
		} catch (AleradeyRegisteredEmailException ex) {
			errors.rejectValue("email", null, "이미 사용중인 이메일입니다.");
			return "register-form";
			
		}
		
		return "redirect:registered";
	}
UserRegisterForm.java
public class UserRegisterForm {

	@Size(min = 1, message = "접속 권한은 하나 이상 체크하세요.")
	private List<String> roleName;
	
	@NotBlank(message = "아이디는 필수입력값입니다.") // 공백일 때 표시될 문자
	@Length(min = 4, max = 20, message="아이디는 4글자이상 20글자 이하로 입력하세요.")  // 최소 4글자, 최대 20글자
	private String id;
	
	@NotBlank(message = "비밀번호는 필수입력값입니다.")
	@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "비밀번호는 최소 8 자, 대문자 하나 이상, 소문자 하나 및 숫자 하나 이상 포함해야 합니다.")
	private String password;
	
	@NotBlank(message = "이름은 필수입력값입니다.") // 공백일 때 표시될 문자
	@Pattern(regexp = "^[가-힣]{2,}$", message = "이름을 한글 2글자 이상으로 입력하세요.")
	private String name;
	
	@NotBlank(message = "이메일은 필수입력값입니다.") // 공백일 때 표시될 문자
	@Email(message = "유효한 이메일 형식이 아닙니다.")
	private String email;
	
	@NotBlank(message = "전화번호는 필수입력값입니다.") // 공백일 때 표시될 문자
	@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "유효한 전화번호 형식이 아닙니다.") // "\\는 숫자를 의미한다."
	private String tel;
}
AleradeyRegisteredUserIdException.java
public class AleradeyRegisteredUserIdException extends ApplicationException	
	private static final long serialVersionUID = 6006220286040629384L;
	public AleradeyRegisteredUserIdException(String message) {
		super(message);
	}
}
AleradeyRegisteredEmailException.java
public class AleradeyRegisteredEmailException extends ApplicationException {
	private static final long serialVersionUID = 8098332857060144740L;
	public AleradeyRegisteredEmailException(String message) {
		super(message);
	}
}
UserService.java
public void registerUser(UserRegisterForm userRegisterForm) {
		User savedUser = userMapper.getUserById(userRegisterForm.getId());
		if (savedUser != null) {
			throw new AleradeyRegisteredUserIdException("["+userRegisterForm.getId()+"] 사용할 수 없는 아이디입니다.");
		}
		savedUser = userMapper.getUserByEmail(userRegisterForm.getEmail());
		if (savedUser != null) {
			throw new AleradeyRegisteredEmailException("["+userRegisterForm.getEmail()+"] 사용할 수 없는 이메일입니다.");
		}

실행결과(지정된 조건에 맞지 않을 경우)

ModelAttribute

  • model에 userRegisterForm이라는 것을 만들어 값을 담아놓는다.
  • modelAttribute를 통해 userRegisterForm를 토대로 입력필드를 채운다.
  • path = 입력필드의 이름이다. (name과 id가 path와 같은 이름으로 지정된다.)

CSRF

loginform.jsp
	<div class="row mb-3">
		<div class="col-12">
			<p>아이디와 비밀번호를 입력하고 로그인 버튼을 클릭하세요</p>
			<form id="form-register" class="border bg-light p-3" method="post" action="login">
			<!-- csrf 토큰값을 히든필드로 추가한다. -->
				<sec:csrfInput />
				<div class="mb-3">
					<label class="form-label">아이디</label>
					<input type="text" class="form-control form-control-sm" name="id" />
				</div>
				<div class="mb-3">
					<label class="form-label">비밀번호</label>
					<input type="password" class="form-control form-control-sm" name="password" />
				</div>
				<div class="text-end">
					<a href="/home" class="btn btn-secondary btn-sm">취소</a>
					<button type="submit" class="btn btn-primary btn-sm">로그인</button>
				</div>
			</form>
		</div>
	</div>
navbar.jsp
<li class="nav-item"><a class="nav-link" href="/logout" onclick="logout(event)">로그아웃</a></li>
<form id="form-logout" method="post" action="logout">
	<sec:csrfInput />
</form>
<script>
	function logout(event) {
		event.preventDefault();
		document.getElementById("form-logout").submit();
	}
</script>
profile
한 걸음 한 걸음 나아가는 개발자

0개의 댓글