온라인 강좌/유튜브 강의

Spring Boot 6. thymeleaf에서 form 전송하기

범박사 2024. 2. 17. 23:03

Handling Form Submission

<!DOCTYPE HTML>
<html xmlns:th="https://www.thymeleaf.org">
<head> 
    <title>Getting Started: Handling Form Submission</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
	<h1>Form</h1>
    <form action="#" th:action="@{/greeting}" th:object="${greeting}" method="post">
    	<p>Id: <input type="text" th:field="*{id}" /></p>
        <p>Message: <input type="text" th:field="*{content}" /></p>
        <p><input type="submit" value="Submit" /> <input type="reset" value="Reset" /></p>
    </form>
</body>
</html>

<th:action> post 요청으로 form의 내용을 보낼 주소

<th:object> object로 지정하면 controller에서 지정했던 greeting 키 사용 가능

<th:field>에서 "*{id}" 하게 되면 greeting.id 값이 들어옴

 

글 수정하기

list.html

<tbody>
    <tr th:each="board : ${boards}">
        <td th:text="${board.id}">Mark</td>
        <td><a th:text="${board.title}" th:href="@{/board/form(id=${board.id})}">Otto</a></td>
        <td>홍길동</td>
    <tr>
</tbody>

title을 누르면 board의 id 값을 함께 보낸다. (Controlelr에서 RequestParam 추가)

 

BoardController

 @GetMapping("/form")
public String form(Model model, @RequestParam(required = false) Long id){
    if(id == null){
        model.addAttribute("board", new Board());
    }else{
        Board board = boardRepository.findById(id).orElse(null);
        model.addAttribute("board", board);
    }
    return "board/form";
}
@PostMapping("/form")
public String greetingSubmit(@ModelAttribute Board board) {
   boardRepository.save(board);
    return "redirect:/board/list";
}

findById 함수를 이용해서 일치하는 id값을 넘기고 없을 경우(orElse) null 값을 넘긴다.

model에 저장해서 form.html로 이동

 

form.html

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">

<head th:replace="fragments/common :: head('게시판')">
</head>

<body>
    <nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top" th:replace="fragments/common :: menu('board')">
    </nav>
    <div class=" container">
        <h2>게시판</h2>
        <form action="#" th:action="@{/board/form}" th:object="${board}" method="post">
            <input type="hidden" th:field="*{id}">
            <div class="form-group">
                <label for="title">제목</label>
                <input type="text" class="form-control" id="title" th:field="*{title}">
            </div>
            <div class="form-group">
                <label for="content">내용</label>
                <textarea class="form-control" id="content" rows="3" th:field="*{content}"></textarea>
            </div>
            <div class="text-end">
                <button type="submit" class="btn btn-primary">확인</button>
                <a type="button" class="btn btn-primary" th:href="@{/board/list}">취소</a>
            </div>
        </form>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
        integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"
        integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r"
        crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js"
        integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+"
        crossorigin="anonymous"></script>
</body>

</html>

<th:object>로 model에 저장한 board를 명시하고 title과 content에는 내용으로 전달받은 board.title, board.content가 나오게 하기 위해 <th:field>를 사용해서 조회한다.

수정 후 submit을 누르면 hidden과 함께 form 전송 (수정)

 

Validating Form Input

방법 1. 어노테이션 사용

강의에서는 @NotNull 어노테이션을 사용했는데, deprecated 돼서 찾아보니 메소드 인자에서 유효성을 검사하기 위해서는 @Vaild를 사용해야 한다(의존성 추가). lombok에는 @NonNull 어노테이션이 있는데 메서드의 인자에 사용하면 null이 들어올 시 NullPointerException을 발생시킨다.

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>3.2.2</version>
</dependency>

 

Board.java

@Entity
@Data
public class Board {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Valid
    @Size(min=2, max=30)
    private String title;
    private String content;
}

 

BoardController

@PostMapping("/form")
public String greetingSubmit(@Valid Board board, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        return "board/form";
    }
    boardRepository.save(board);
    return "redirect:/board/list";
}

BiningResult가 2글자 이상 30글자 이하를 체크해서 일치하지 않으면 hasErrors 발생

 

 

방법 2. 커스텀 클래스 생성

BoardValidator.java

@Component
public class BoardValidator implements Validator {
    @Override
    public boolean supports(Class<?> clazz) {
        return Board.class.equals(clazz);
    }

    @Override
    public void validate(Object obj, Errors e) {
        Board b = (Board) obj;
        if(StringUtils.isEmpty(b.getContent())){
            e.rejectValue("content", "key", "내용을 입력하세요");
        }
    }
}

 

BoardController

@Autowired
private BoardValidator boardValidator;

@PostMapping("/form")
public String greetingSubmit(@Valid Board board, BindingResult bindingResult) {
    boardValidator.validate(board, bindingResult);
    if (bindingResult.hasErrors()) {
        return "board/form";
    }
    boardRepository.save(board);
    return "redirect:/board/list";
}

 

title의 메세지도 변경하고 싶다면 Board 수정

@Size(min=2, max=30, message = "제목은 2자이상 30자 이하입니다.")
private String title;

 

값을 전부 지우고 submit을 누르면