안녕하세요 코이킹입니다.
이번 포스트는 유효성 검사에 대한 내용입니다.
1. 목표
- 유효성 검사 / 정규표현식 등의 키워드를 알기
- 스프링 부트의 유효성 검사기능을 사용하여 컨트롤러에서 유효성 검사를 할 수 있다.
2. 유효성 검사란?
유효성 검사는 데이터가 사양대로 입력되었는지 확인하여, 어플리케이션의 이상 동작을 미연에 방지해주는 것을 말합니다.
웹 어플리케이션구현시에는 요청 파라미터 / 요청 바디의 데이터에 대해서 주로 유효성 검사를 실시합니다.
유효성 검사는 조건문등으로 Null체크나 미리 지정한 상수를 비교하거나 ・ 정규표현식으로 정해진 패턴에 해당하는 데이터인지 데이터의 패턴을 확인하는 방식으로 이루어집니다.
스프링 부트에서는 유효성 검사를 쉽게 구현할 수 있도록 해주는 기능을 제공하고 있으므로, 스프링 부트의 기능을 사용해서 유효성 검사를 구현해 보겠습니다.
3. 유효성 검사 구현
1) 의존성 라이브러리 추가 : /template-springboot/build.gradle
plugins {
id 'org.springframework.boot' version '2.6.8'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.sb.template'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
all*.exclude module : 'spring-boot-starter-logging'
}
repositories {
mavenCentral()
}
dependencies {
// For develop
developmentOnly 'org.springframework.boot:spring-boot-devtools'
// Web
implementation 'org.springframework.boot:spring-boot-starter-web'
// thymeleaf
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// DB
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'
// lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// Log4j
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
// Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
tasks.named('test') {
useJUnitPlatform()
}
- 48행 : 스프링부트가 제공하는 유효성 검사를 사용하기 위한 의존 라이브러리 추가 설정
2) 폼 클래스 : /template-springboot/src/main/java/com/sb/template/forms/BoardForm.java
- 스프링 부트에서 요청 바디의 유효성 검사를 위해선, 요청 폼 클래스에서 어노테이션을 선언해주는 것으로 유효성 검사의 조건을 지정할 수 있습니다.
package com.sb.template.forms;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import com.sb.template.entity.Board;
import lombok.Data;
@Data
public class BoardForm {
private Integer boardNo;
@NotNull(message = "Please input type")
private Integer type;
@NotBlank(message = "Please input title")
private String title;
@NotBlank(message = "Please input contents")
private String contents;
@Pattern(regexp = "^[a-zA-Z0-9+-_.]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$", message = "Unsuitable inputed ID")
private String memberId;
public Board toEntity() {
Board board = new Board();
board.setType(this.type);
board.setTitle(this.title);
board.setContents(this.contents);
board.setMemberId(((this.memberId == null || (this.memberId.equals(""))) ? "" : this.memberId));
return board;
}
}
- 16행 : Null 허용 금지 유효성 검사 설정
- 19, 22행 : 공백 허용금지 유효성검사 설정
- 25행 : 패턴에 매칭 되는 데이터만 허용하는 유효성 검사 설정, 이메일 형식의 데이터인지를 확인하는 정규표현식을 사용하여 패턴 매칭 유효성 검사를 실시하는 설정.
이메일과 같이 자주 사용되는 패턴의 경우 @Email어노테이션과 같은 스프링 부트가 지원해주는 기능을 사용해서 구현하면 더 효율적인 개발이 가능합니다.
3) 컨트롤러/template-springboot/src/main/java/com/sb/template/controller/BoardController.java
package com.sb.template.controller;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
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.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.sb.template.entity.Board;
import com.sb.template.enums.BoardType;
import com.sb.template.forms.BoardForm;
import com.sb.template.service.BoardService;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Controller
@RequestMapping(path = "/board")
public class BoardController {
@Autowired
private BoardService boardService;
@RequestMapping(method = RequestMethod.GET, path = "list")
public String viewBoardList(Model model) {
List<Board> boardList = boardService.getAllBoard();
model.addAttribute("boardList", boardList);
return "board/list";
}
@RequestMapping(method = RequestMethod.GET, path = "write")
public String writeBoard(Model model) {
model.addAttribute("boardTypes", BoardType.getBoardTypes());
return "board/write";
}
@RequestMapping(method = RequestMethod.POST, path = "write")
public String writeCompleteBoard(@Validated BoardForm form,
BindingResult bindingResult, Model model) {
if (validation(bindingResult)) {
return "redirect:/board/list";
}
Integer boardNo = null;
boardNo = boardService.createBoard(form.toEntity()).getBoardNo();
return "redirect:/board/list";
}
@RequestMapping(method = RequestMethod.GET, path = "read/{boardNo}")
public String viewBoardOne(@PathVariable(required = true) int boardNo, Model model) {
model.addAttribute("board", boardService.getBoardOne(boardNo));
return "board/read";
}
@RequestMapping(method = RequestMethod.GET, path = "update/{boardNo}")
public String updateBoard(@PathVariable(required = true) int boardNo, Model model) {
boardService.updateBoardForm(boardNo, model);
return "board/update";
}
@RequestMapping(method = RequestMethod.POST, path = "update/{boardNo}")
public String updateCompleteBoard(
@PathVariable(required = true) int boardNo,
@Validated BoardForm form,
BindingResult bindingResult, Model model) {
if (validation(bindingResult)) {
return "redirect:/board/list";
}
boardService.updateBoard(boardNo, form.toEntity());
model.addAttribute("message", "Update Success");
return "redirect:/board/read/"+boardNo;
}
@RequestMapping(method = RequestMethod.GET, path = "delete/{boardNo}")
public String deleteBoard(@PathVariable int boardNo, Model model) {
model.addAttribute("board", boardService.getBoardOne(boardNo));
return "board/delete";
}
@RequestMapping(method = RequestMethod.POST, path = "delete")
public String deleteCompleteBoard(
@RequestParam(name = "boardNo", required = true) int boardNo,
Model model) {
boardService.deleteBoard(boardNo);
model.addAttribute("message", "Delete Success");
return "redirect:/board/list";
}
/**
* If validation check result is OK, return false.
* In the opposite case return true.
*
* @param bindingResult
* @return
*/
private boolean validation(BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
log.error("Validation-NG : {} ", bindingResult.getAllErrors()
.stream()
.map(e -> e.getDefaultMessage())
.collect(Collectors.toList())
);
return true;
} else {
log.info("Validation-OK");
return false;
}
}
}
- 51~52행, 85~87행 : @Validated 어노테이션을 폼 클래스의 매개변수 앞에 선언해 주면, 유효성 검사가 정상 동작할 겁니다.
매개 변수 BindingResult는 유효성 검사의 결과를 확인할 수 있는 변수로서, 반드시 폼 클래스의 매개변수 다음에 설정해 주어야 합니다. (안 해주면 에러 발생)
- 54~56행, 89~91행 : 유효성 검사를 통과하지 못한 경우 에러 내용을 로그로 출력하고 글 목록 페이지로 리다이렉트 합니다.
- 66, 75, 85행 : @PathVariable의 required속성 값을 true로 설정하여 빈 값을 받지 않도록 설정했습니다.
4. 동작확인
- 글 내용을 입력하지 않은 채로 글 작성 요청을 보낸 결과, 글 목록으로 리턴하도록 동작하고 있습니다.
5. 전체 소스코드
https://github.com/leeyoungseung/template-springboot/tree/feature/validation
유효성 검사에 대한 포스트는 이상으로 마치겠습니다.
다음 포스트는 예외처리에 대한 내용이 되겠습니다.
'프로그래밍 > Springboot-토이프로젝트' 카테고리의 다른 글
【게시판-07】페이징 처리 (1) | 2022.09.16 |
---|---|
【게시판-번외06】예외처리 (0) | 2022.09.16 |
【게시판-번외04】AOP를 적용한 로그출력 (0) | 2022.09.14 |
【게시판-번외03】필수기능 설정 & Log4j설정 (0) | 2022.09.14 |
【게시판-06】글 삭제 (0) | 2022.09.13 |
댓글