๐Ÿ”™๋’ค๋กœ๊ฐ€๊ธฐ

ํผ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ํ™”๋ฉด ๊ณ„์ธต๊ณผ ์„œ๋น„์Šค ๊ณ„์ธต์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•œ๋‹ค.

MemberForm

package jpabook.jpashop.controller;

import lombok.Getter;
import lombok.Setter;

import javax.validation.constraints.NotEmpty;

@Getter @Setter
public class MemberForm {

    @NotEmpty(message = "ํšŒ์› ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค. ")
    private String name;

    private String city;
    private String street;
    private String zipcode;

}

MemberController

package jpabook.jpashop.controller;

import jpabook.jpashop.domain.Address;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.service.MemberService;
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 javax.validation.Valid;

@Controller
@RequiredArgsConstructor
public class MemberController {

    private final MemberService memberService;

    @GetMapping("/members/new")
    public String createForm(Model model){
        model.addAttribute("memberForm", new MemberForm());
        return "members/createMemberForm";
    }

    @PostMapping("/members/new")
    public String create(@Valid MemberForm form, BindingResult result){

        if(result.hasErrors()){
            return "members/createMemberForm";
        }
        Address address = new Address(form.getCity(), form.getStreet(), form.getZipcode());
        Member member = new Member();
        member.setName(form.getName());
        member.setAddress(address);

        memberService.join(member);
        return "redirect:/";
    }

}

@Valid ๋’ค์— BindingResult result๊ฐ€ ์žˆ์œผ๋ฉด, ์˜ค๋ฅ˜ ๋ฐœ์ƒ ์‹œ ์˜ค๋ฅ˜๋ฅผ ๋‹ด์€ ์ฑ„๋กœ ๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚จ๋‹ค.

ํšŒ์› ๋“ฑ๋ก ํผ ํ™”๋ฉด templates/members/createMemberForm.html

<!DOCTYPE HTML>
<html xmlns:th="<http://www.thymeleaf.org>">
<head th:replace="fragments/header :: header" />
<style>
 .fieldError {
 border-color: #bd2130;
 }
</style>
<body>
<div class="container">
  <div th:replace="fragments/bodyHeader :: bodyHeader"/>
  <form role="form" action="/members/new" th:object="${memberForm}"
        method="post">
    <div class="form-group">
      <label th:for="name">์ด๋ฆ„</label>
      <input type="text" th:field="*{name}" class="form-control"
             placeholder="์ด๋ฆ„์„ ์ž…๋ ฅํ•˜์„ธ์š”"
             th:class="${#fields.hasErrors('name')}? 'form-control
fieldError' : 'form-control'">
      <p th:if="${#fields.hasErrors('name')}"
         th:errors="*{name}">Incorrect date</p>
    </div>
    <div class="form-group">
      <label th:for="city">๋„์‹œ</label>
      <input type="text" th:field="*{city}" class="form-control"
             placeholder="๋„์‹œ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”">
    </div>
    <div class="form-group">
      <label th:for="street">๊ฑฐ๋ฆฌ</label>
      <input type="text" th:field="*{street}" class="form-control"
             placeholder="๊ฑฐ๋ฆฌ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”">
    </div>
    <div class="form-group">
      <label th:for="zipcode">์šฐํŽธ๋ฒˆํ˜ธ</label>
      <input type="text" th:field="*{zipcode}" class="form-control"
             placeholder="์šฐํŽธ๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”">
    </div>
    <button type="submit" class="btn btn-primary">Submit</button>
  </form>
  <br/>
  <div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>

ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

/* Model ๊ฐ์ฒด ํ…œํ”Œ๋ฆฟ ํŒŒ์‹ฑ ์˜ค๋ฅ˜ */
org.thymeleaf.exceptions.TemplateInputException: 
	An error happened during template parsing (
	template: "class path resource [templates/members/createMemberForm.html]"
)
@GetMapping("/members/new")
    public String createForm(Model model){
        model.addAttribute("memberForm", new MemberForm());
/* ํ…œํ”Œ๋ฆฟ์—์„œ ์ฐธ์กฐํ•˜๋Š” ์†์„ฑ ์ด๋ฆ„๊ณผ ์ปจํŠธ๋กค๋Ÿฌ์—์„œ Model ๊ฐ์ฒด์— ์ถ”๊ฐ€ํ•œ
์†์„ฑ ์ด๋ฆ„์€ ์ •ํ™•ํ•˜๊ฒŒ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค. ๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„์ด ์ค‘์š”ํ•˜๋ฏ€๋กœ ํ…œํ”Œ๋ฆฟ์—์„œ 
${memberForm}์ด๋ผ๊ณ  ์ ์—ˆ๋‹ค๋ฉด, ์ปจํŠธ๋กค๋Ÿฌ์—์„œ
model.addAttribute("memberForm", ...)๋กœ ์†์„ฑ ์ด๋ฆ„์„ ์ผ์น˜์‹œ์ผœ์•ผ ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•ด์•ผ Thymeleaf ํ…œํ”Œ๋ฆฟ ์—”์ง„์ด Model ๊ฐ์ฒด์—์„œ ์˜ฌ๋ฐ”๋ฅธ ์†์„ฑ์„ ์ฐพ์•„
์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์†์„ฑ ์ด๋ฆ„์ด ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜์ง€ ์•Š์œผ๋ฉด, Thymeleaf๋Š” ํ•ด๋‹น ์†์„ฑ์„
์ฐพ์„ ์ˆ˜ ์—†์–ด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.*/
        return "members/createMemberForm";
    }