[Class Flix] EP 21. Bean Validator 적용
Project/ClassFlix

[Class Flix] EP 21. Bean Validator 적용

 

지금까지는 회원가입이나 강의등록시 올바른 데이터인지 검증하지 않았습니다.

 

사실 기능은 몇개 넣었는데 잘 모르고 사용했습니다.

 

Validation을 학습하고 실무에서 많이쓰는 방법으로 제 프로젝트에 적용해 보겠습니다.

 

2021.10.10 - [Web/MVC2] - EP3. 검증1 - Validation

 

EP3. 검증1 - Validation

지금까지 만든 웹 어플리케이션은 사용자가 입력하는 모든 상황을 대비할 수 없습니다. 예를들어, 사용자가 가격을 입력하는 곳에 알파벳을 입력하거나 그냥 입력 창에 공백을 넣게되면 에러페

ksabs.tistory.com

2021.10.14 - [Web/MVC2] - EP4. 검증2 - Bean Validation

 

EP4. 검증2 - Bean Validation

EP3. 검증1 에서 검증로직 구현은 개발자가 직접 작성했습니다. 사실 이 검증 로직은 여러 프로젝트에서 공통적으로 쓰이는 로직입니다. 그래서 검증 로직을 모든 프로젝트에서 공통적으로 쓰일

ksabs.tistory.com

 

 

 

 

검증 적용 계획

 

회원가입

필드 검증
이름 @NotBlank
나이 @NotNull
성별 @NotNull -> enum 타입은 NotNull로 판단한다.
직업 @NotBlank

 

 

강의등록

필드 검증
강의제목 @NotBlank
강의자이름 @NotBlank
강의 내용 소개 필요없음
강의 대표 사진 이미지파일만 -> 추후 파일업로드 수정시 반영
강의 사이트 이름 @NotBlank
강의 사이트 주소 @URL -> URI타입에 적용불가 -> String타입에 적용후 변환필요

 

 

 

 

Bean Validation 적용순서

  1. implementation 'org.springframework.boot:spring-boot-starter-validation' 추가
  2. Form 객체에서 검증 어노테이션 적용하기
  3. 컨트롤러에서 @ModelAttribute 앞에 @Validated 넣어주고 뒤에 BindingResult 넣어주기
  4. 오브젝트 오류검증은 컨트롤러 안에서 자바코드로 넣어주기
  5. templates 수정하기

 

1. 의존성 추가

저번에 따라친게 있어서 적용되어있습니다.

dependencies {
	...
	implementation 'org.springframework.boot:spring-boot-starter-validation'
    ...
}

 

 

2. Form객체에서 검증 어노테이션 적용하기

 

MemberForm

package dongho.classflix.controller;

import dongho.classflix.domain.Gender;
import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

@Getter
@Setter
public class MemberForm {

    @NotBlank(message = "회원 이름을 입력해 주세요.")
    private String name;

    @NotNull(message = "나이를 입력해 주세요.")
    private Integer age;

    @NotNull(message = "성별을 선택해 주세요.")
    private Gender gender;

    @NotBlank(message = "직업을 입력해 주세요.")
    private String career;
}

 

LectureForm

@URL로 uri를 검증하기 위해서는 String타입이어야 합니다.

그래서 form객체에서는 uri를 String타입으로 선언해 검증한 후, 실제 lecture객체에 넣어줄 때는 convertStrToUri 메서드를 이용해 URI타입으로 넣어주도록 구현했습니다.

package dongho.classflix.controller;

import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.URL;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotBlank;
import java.net.URI;

@Getter
@Setter
public class LectureForm {
    @NotBlank(message = "강의 제목을 입력해 주세요.")
    private String lectureName;

    @NotBlank(message = "강의자 이름을 입력해 주세요.")
    private String teacherName;

    private String content;
    private MultipartFile image;

    @NotBlank(message = "강의가 올려져 있는 사이트를 입력해 주세요.")
    private String siteName;

    @NotBlank(message = "URL을 입력해주세요.")
    @URL(message = "URL을 제대로 입력했는지 확인해 주세요.")
    private String uri;

    URI convertStrToUri(String url) {
        return URI.create(url);
    }
}

 

 

3. 컨트롤러에서 @ModelAttribute 앞에 @Validated 넣어주고 뒤에 BindingResult 넣어주기

 

 

 

4. 오브젝트 오류는 없어서 안넣었습니다.

 

 

5. templates 수정하기

 

memberForm.html

성별오류는 bord를 빨간색으로 만들 부분이 없기 때문에 빨간글자만 추가되도록 바꿨습니다.

<form role="form" action="/members/new" th:object="${memberForm}" method="post">
        <div class="form-group">
            <label th:for="name" th:text="#{label.member.name}">이름</label>
            <input type="text" th:field="*{name}"
                   th:errorclass="fieldError" class="form-control" placeholder="이름을 입력하세요">
            <div class="fieldError" th:errors="*{name}">
                이름 오류
            </div>
        </div>
        <div class="form-group">
            <label th:for="age" th:text="#{label.member.age}">나이</label>
            <input type="text" th:field="*{age}"
                   th:errorclass="fieldError" class="form-control" placeholder="나이를 입력하세요">
            <div class="fieldError" th:errors="*{age}">
                나이 오류
            </div>
        </div>
        <div>
            <div><b th:text="#{label.member.gender}">성별</b></div>
            <div class="radio" th:each="gender : ${genderTypes}">
                <input type="radio" th:field="*{gender}" th:value="${gender.name()}">
                <label th:for="${#ids.prev('gender')}" th:text="${gender.description}"></label>
            </div>
            <div class="fieldError" th:errors="*{gender}">
                성별 오류
            </div>
        </div>

        <div class="form-group">
            <label th:for="career" th:text="#{label.member.career}">직업</label>
            <input type="text" th:field="*{career}"
                   th:errorclass="fieldError" class="form-control" placeholder="이름을 입력하세요">
            <div class="fieldError" th:errors="*{career}">
                직업 오류
            </div>
        </div>
        <div class="text-center">
            <button type="submit" class="btn btn-primary" th:text="#{button.save}">등록하기</button>
        </div>
    </form>

 

 

lectureForm.html

URL검증은 @URL을 이용하려면 필드의 타입이 String이어야 했습니다.

 

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<style>
    .fieldError {
        border-color: #bd2130;
        color: #bd2130;
    }
    .container-center {
        max-width: 560px;
    }
</style>
<body>
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<div class="container container-center">
    <div class="page-header">
        <h1 class="text-center" th:text="#{page.addLecture}">강의등록</h1>
    </div>
    <form role="form" action="/lectures/new" th:object="${lectureForm}"
          method="post", enctype="multipart/form-data">
        <div class="form-group">
            <label th:for="lectureName" th:text="#{label.lecture.name}">강의제목</label>
            <input type="text" th:field="*{lectureName}"
                   th:errorclass="fieldError" class="form-control" placeholder="강의제목을 입력하세요.">
            <div class="fieldError" th:errors="*{lectureName}">
                강의명 오류
            </div>
        </div>
        <div class="form-group">
            <label th:for="teacherName" th:text="#{label.lecture.teacher}">강의자 이름</label>
            <input type="text" th:field="*{teacherName}"
                   th:errorclass="fieldError" class="form-control" placeholder="강의자를 입력하세요.">
            <div class="fieldError" th:errors="*{teacherName}">
                강의자명 오류
            </div>
        </div>
        <div class="form-group">
            <label th:for="content" th:text="#{label.lecture.content}">강의 내용 소개</label>
            <textarea th:field="*{content}" class="form-control" placeholder="강의내용을 입력하세요"> </textarea>
        </div>
        <div class="form-group">
            <label th:for="representImage" th:text="#{label.lecture.representPicture}">강의 대표사진 업로드</label>
            <input type="file" multiple="true" th:field="*{image}" accept="image/*" class="form-control" placeholder="강의 대표사진을 업로드해주세요">
        </div>
        <div class="form-group">
            <label th:for="siteName" th:text="#{label.lecture.siteName}">강의 사이트 이름</label>
            <input type="text" th:field="*{siteName}"
                   th:errorclass="fieldError" class="form-control" placeholder="강의 사이트의 이름을 입력해주세요.">
            <div class="fieldError" th:errors="*{siteName}">
                강의 사이트명 오류
            </div>
        </div>
        <div class="form-group">
            <label th:for="uri" th:text="#{label.lecture.siteUrl}">강의 사이트 주소</label>
            <input type="text" th:field="*{uri}"
                   th:errorclass="fieldError" class="form-control" placeholder="강의 사이트의 주소를 입력해주세요.">
            <div class="fieldError" th:errors="*{uri}">
                강의 사이트 url 오류
            </div>
        </div>
        <div class="text-center">
            <button type="submit" class="btn btn-primary" th:text="#{button.save}">등록하기</button>
        </div>

    </form>
    <br/>
</div> <!-- /container -->
<div th:replace="fragments/footer :: footer" />
</body>
</html>

검증 완성