ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring Boot] 2-6. Validation을 이용한 회원가입 구현 (jsp)
    프로젝트/도서 관리 시스템 2023. 7. 5. 20:53

     

     

     

     

     

    검증 기능을 매번 코드로 작성하는 것은 상당히 번거롭다. 특히 특정 필드에 대한 검증 로직은 대부분 빈 값인지 아닌지, 특정 크기를 넘는지 아닌지와 같이 매우 일반적인 로직이다. 

     

    이런 검증 로직을 모든 프로젝트에 적용할 수 있게 공통화하고, 표준화한 것이 바로 Bean Validation이다. 이전 세션을 이용한 회원가입에서는 js 정규표현식을 이용해 유효성검사를 구현했었다. 이번엔 더 편리한 방법으로 유효성 검사를 진행해보려고 한다. 

     

     

    build.gradle에 아래 의존관계를 추가하면 라이버러리가 추가된다. 

    implementation 'org.springframework.boot:spring-boot-starter-validation

     

     

     

    📑 검증 어노테이션 (domain)

     

    package com.wish.library.security.domain;
    
    import lombok.*;
    
    import javax.validation.constraints.Digits;
    import javax.validation.constraints.NotBlank;
    import javax.validation.constraints.Pattern;
    import javax.validation.constraints.Size;
    import java.util.Date;
    
    @Getter
    @Setter
    @ToString
    @AllArgsConstructor
    @NoArgsConstructor //기본 생성자를 생성해준다.
    @Builder
    public class MemberSaveForm {
    
        @NotBlank(message = "아이디는 필수 입력 값입니다.")
        @Email(message = "이메일 형식이 올바르지 않습니다.")
        private String email; // 회원 아이디
    
        @NotBlank(message = "비밀번호는 필수 입력 값입니다.")
        @Pattern(regexp = "(?=.*[0-9])(?=.*[a-zA-Z])(?=.*\\W)(?=\\S+$).{8,16}", message = "비밀번호는 8~16자 영문 대 소문자, 숫자, 특수문자를 사용하세요.")
        private String password; //비밀번호
    
        @NotBlank(message = "이름은 필수 입력 값입니다.")
        @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-z0-9-_]{2,20}$", message = "이름은 특수문자를 제외한 2~20자리여야 합니다.")
        private String name;
    
        @NotBlank(message = "닉네임은 필수 입력 값입니다.")
        @Pattern(regexp = "^[ㄱ-ㅎ가-힣a-z0-9-_]{2,30}$", message = "닉네임은 특수문자를 제외한 2~10자리여야 합니다.")
        private String nickname;
        private Sex mfCode; //성별 (men, female)
    
        @NotBlank(message = "핸드폰 번호는 필수 입력 값입니다.")
        @Pattern(regexp = "^[0-9]*$", message = "정수만 가능")
        @Size(min = 9, max = 11, message = "전화번호 길이에 부합해야함.")
        private String cellNo;
    
    }

     

     

    📌 NotBlank : 빈값 + 공백만 있는 경우를 허용하지 않는다.

    📌 NotNull : null을 허용하지 않는다. 

    📌 Pattern : 문자열이 지정한 패턴에 일치하는지 검사한다.

    📌 Size : min부터 max 사이에 있는지 검사한다. 값이 null인 경우 유효한 것으로 판단한다. 

    📌 Email : 값이 email 주소인지 검사 

     

     

     

     

    📑 Controller 수정

     

    package com.wish.library.security.controller;
    
    import com.wish.library.security.domain.MemberSaveForm;
    import com.wish.library.security.domain.UserDTO;
    import com.wish.library.security.service.CustomUserService;
    import com.wish.library.security.service.MemberService;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.validation.BindingResult;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.bind.annotation.*;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.support.RedirectAttributes;
    
    /**
     * GET 방식, 화면 조회시
     */
    @Controller
    @RequiredArgsConstructor
    @Slf4j
    public class UserController {
    
        private final CustomUserService customUserService;
        private final MemberService memberService;
    
    
        @GetMapping("/join")
        public String join(){
            return "join";
        }
    
        @PostMapping("/join")
        public String join(@Validated @ModelAttribute("memberSaveForm") MemberSaveForm memberSaveForm
                            , BindingResult bindingResult
                            , RedirectAttributes rttr) {
            log.info("=================join Controller");
    
            if(bindingResult.hasErrors()){
                log.info("errors={}", bindingResult);
                return "join";
            }
    
            boolean saveResult = memberService.save(memberSaveForm);
            if(saveResult){
                rttr.addFlashAttribute("result", "회원 가입 완료");
                return "redirect:/login";
            }
                return "join";
    
    
        }
    
    }

     

     

    스프링 부트가 spring-boot-starter-validation 라이브러리를 넣으면 자동으로 Bean Validator를 인지하고 스프링에 통합한다. 따라서 컨트롤러 객체 앞에 @Valid, @Validated만 적용해주면 된다. 

     

    @Validated는 반드시 BindingResult 앞에 적어준다. 만약 bindingResult에 에러값이 있는 경우 log를 출력하고 다시 join 페이지로 이동한다. 

     

     

     

    📑 join.jsp

     

    <%@ page language="java" contentType="text/html; charset=UTF-8"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    
    
    <!doctype html>
    <html lang="en" data-bs-theme="auto">
    <head>
    
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="description" content="">
        <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors">
        <meta name="generator" content="Hugo 0.111.3">
       <meta name="_csrf" content="${_csrf.token}"/>
       <meta name="_csrf_header" content="${_csrf.headerName}"/>
        <title>회원가입</title>
    
        <link rel="canonical" href="https://getbootstrap.com/docs/5.3/examples/sign-in/">
        <link href="assets/dist/css/sign-in.css" rel="stylesheet">
    
        <jsp:include page="./includes/common_includes.jsp"></jsp:include>
    
     <%--   <script type="text/javascript" src="assets/dist/js/commonAjax.js"></script>
        <script type="text/javascript" src="assets/dist/js/member.js"></script>--%>
    
        <style>
    
    
            @media (min-width: 768px) {
                .bd-placeholder-img-lg {
                    font-size: 3.5rem;
                }
            }
    
    
            .nav-scroller .nav {
                display: flex;
                flex-wrap: nowrap;
                padding-bottom: 1rem;
                margin-top: -1px;
                overflow-x: auto;
                text-align: center;
                white-space: nowrap;
                -webkit-overflow-scrolling: touch;
            }
    
    
        </style>
    
    
    
    </head>
    <script>
    
    </script>
    <body class="text-center">
    <main class="form-signin w-100 m-auto">
        <form:form  name="join_form" action="/join" method="post" modelAttribute="memberSaveForm">
            <%--<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />--%>
    
            <img class="mb-4" src="assets/brand/bootstrap-logo.svg" alt="" width="72" height="57"
                 onclick="location.href='/'">
            <h1 class="h3 mb-3 fw-normal">Please join in</h1>
            <div class="form-floating">
                <input type="email" class="form-control" name="email" id="email" placeholder="name@example.com">
                <label for="email">Email address</label><%--<button class="btn btn-light rounded-pill px-3" type="button" name="btn" id="btnEmailCheck">Duplicate check</button>--%>
                <%--<span id="id-check"></span>--%>
                <span class="text-danger"><form:errors path="email" /></span>
            </div>
            <div class="form-floating">
                <input type="password" class="form-control" name="password" id="password" placeholder="Password" autoComplete="off">
                <label for="password">Password</label>
                <span class="text-danger"><form:errors path="password" /></span>
    
            </div>
            <div class="form-floating">
                <input type="text" class="form-control" name="name" id="name" placeholder="name">
                <label for="name">name</label>
                <span class="text-danger"><form:errors path="name" /></span>
            </div>
            <div class="form-floating">
                <input type="text" class="form-control" name="nickname" id="nickname" placeholder="nickname">
                <label for="nickname">nickname</label>
                <%--<button class="btn btn-light rounded-pill px-3" type="button" name="btn" id="btnNicknameCheck">Duplicate check</button>--%>
                <%--<span id="nickname-check"></span>--%>
                <span class="text-danger"><form:errors path="nickname" /></span>
            </div>
            <div class="form-floating">
                <div class="form-check">
                    <input id="men" name="mfCode"  value="MEN" type="radio" class="form-check-input" checked>
                    <label class="form-check-label" for="men">Men</label>
                </div>
                <div class="form-check">
                    <input id="female" name="mfCode" value="FEMALE" type="radio" class="form-check-input">
                    <label class="form-check-label" for="female">Female</label>
                </div>
            </div>
            <div class="form-floating">
                <input type="text" class="form-control" name="cellNo" id="cellNo" placeholder="cellNo">
                <label for="cellNo">cellNo</label>
                <span class="text-danger"><form:errors path="cellNo" /></span>
            </div>
            <button class="w-50 btn btn-lg btn-dark" type="button" onclick="history.back();" >이전페이지로가기</button>
            <%--<button class="w-50 btn btn-lg btn-primary" type="button" name="btn" id="btnSingUp" >Sign up</button>--%>
            <button type="submit" class="w-50 btn btn-lg btn-primary">Sign up</button>
        </form:form>
    </main>
    
    
    
    </body>
    </html>

     

     

    jsp 페이지에서 중요한 점은 다음과 같다.

     

    1. <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %> 추가 
    2. <form:form name="join_form" action="/join" method="post" modelAttribute="memberSaveForm">
    form 태그 형식을 다음과 같이 수정한다. 마지막에 modelAttribute에서 지정한 객체의 이름을 입력해줘야한다.
    3. <span class="text-danger"><form:errors path="email" /></span> 출력할 에러 메시지 태그를 요소 입력값 아래 위치한다. 

     

     

    출력 결과는 아래와 같다. 간단하게 끝!! 

     

     

     

    반응형

    댓글

Designed by Tistory.