ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Spring MVC] Form 검증
    Spring 2025. 11. 13. 22:10

    Thymeleaf + Validation(jakarta) + BindingResult(spring) 조합으로 폼을 검증하고, 에러를 반환하여 사용자에게 메시지를 보여주는 간단한 작업을 정리해보겠습니다.

    아무것도 입력하지 않고 제출 시, 아래와 같이 안내 메시지를 출력하는 작업입니다.

    build.gradle

    plugins {
        id 'java'
        id 'org.springframework.boot' version '3.5.7'
        id 'io.spring.dependency-management' version '1.1.7'
    }
    
    // ...
    
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        implementation 'org.springframework.boot:spring-boot-starter-validation'
        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'
    }

     


    MemberForm.java

    import jakarta.validation.constraints.Min;
    import jakarta.validation.constraints.NotEmpty;
    import jakarta.validation.constraints.NotNull;
    import lombok.Getter;
    import lombok.Setter;
    
    @Getter
    @Setter
    public class MemberForm {
    
        @NotEmpty(message = "이름을 입력해주세요.")
        private String name;
    
        @NotNull(message = "나이를 입력해주세요.")
        @Min(value = 18, message = "18세 이상")
        private Integer age;
        
        @Pattern(regexp = ".+@.+\\..+", message = "이메일 형식이 올바르지 않습니다.")
        private String email;
    }

    @NotEmpty, @NotNull 등 어노테이션으로 각 필드에 제약조건을 추가하고 메시지를 설정해줄 수 있습니다. 메시지는 검증 실패시 BindingResult 객체에 저장됩니다. 제약조건 종류는 아래와 같습니다.

     

     

    에러 메시지는 messages.properties로 설정해줄 수도 있습니다.

    # 기본 메시지
    NotNull=값을 입력해주세요.
    NotEmpty=값을 입력해주세요.
    NotBlank=공백일 수 없습니다.
    Pattern=형식이 올바르지 않습니다.
    
    # MemberForm
    NotEmpty.memberForm.name=이름을 입력해주세요.
    NotNull.memberForm.age=나이를 입력해주세요.

     

     

    messages.properties 파일로 설정할 때, 한글이 깨진다면 인코딩 설정 문제입니다. 설정 > 에디터 > 파일 인코딩에서 전역 인코딩, 프로젝트 인코딩, 프로퍼티 파일 인코딩 값들을 UTF-8로 설정해주면 정상적으로 메시지가 표시됩니다.

     


    MemberController.java

    import jakarta.validation.Valid;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.validation.BindingResult;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/members")
    @RequiredArgsConstructor
    public class MemberController {
    
        @GetMapping("/join")
        public String join(Model model) {
            model.addAttribute("memberForm", new MemberForm());
            return "members/join";
        }
    
        @PostMapping("/join")
        public String join(@Valid MemberForm memberForm,
                           BindingResult bindingResult) {
            if (bindingResult.hasErrors())
                return "members/join";
    
            // 회원가입 로직
    
            return "redirect:/";
        }
    }

    @Valid 명시 - 폼 객체에 대한 검증을 수행

     

    BindingResult - 검증 오류 관련 데이터를 담고 있으며, 테스트하는 객체이다. 위 메서드에서는 오류가 있으면 MemberForm, BindingResult 객체와 함께 페이지를 반환하고, 아니라면  회원가입 로직을 수행하고 홈으로 리다이렉트한다.


    join.html

    <body>
        <form th:action="@{/members/join}" method="post" th:object="${memberForm}">
            <label th:for="name">
                이름
                <input type="text"
                       th:field="*{name}"
                       placeholder="이름을 입력하세요."/>
            </label>
            <p th:if="${#fields.hasErrors('name')}"
               th:errors="*{name}">Name Error</p>
            <br>
            <label th:for="email">
                이메일
                <input
                        id="email"
                        th:field="*{email}"
                        type="email"
                        placeholder="example@domain.com"/>
            </label>
            <p th:if="${#fields.hasErrors('email')}"
               th:errors="*{email}">Email Error</p>
            <br>
            <label th:for="age">
                나이
                <input type="text"
                       th:field="*{age}"
                       placeholder="나이를 입력하세요."/>
            </label>
            <p th:if="${#fields.hasErrors('age')}"
               th:errors="*{age}">Age Error</p>
            <br>
            <button type="submit">Submit</button>
        </form>
    </body>

    클라이언트에서 수행하는 검증은 생략했습니다.

     

    th:object - MemberForm 객체 바인딩

     

    th:field - input 태그의 name, id값 설정 및 바인딩한 DTO 객체의 필드값 연결. 

    if (bindingResult.hasErrors())
        return "members/join"; // 다시 반환하더라도, 입력했던 값을 그대로 유지할 수 있다.

     

    th:if - 에러 확인

     

    th:errors - BindingResult에서 가져온 에러 메시지 출력

     


    참고

    Spring document

    (https://spring.io/guides/gs/validating-form-input)

     

    Github

    (https://github.com/ehddnr3473/SpringMVC-Form-Validation)

     

    댓글

Designed by Tistory.