3 분 소요

 인프런 스프링 MVC 2편 - 백엔드 웹 개발 활용 기술편을 학습하고 정리한 내용 입니다.

타임리프에서 지원하는 입력 폼 기능을 통해 기존 프로젝트를 타임리프로 개선해 보자.

  • th:object : 커멘드 객체를 지정한다.
  • *{...}: 선택 변수 식이라고 한다. th:object에서 선택한 객체에 접근한다.
  • th:field
    • HTML 태그의 id, name, value속성을 자동으로 처리해준다.
1
2
3
4
5
<!-- 렌더링 전 -->
<input type="text" th:field="*{itemName}" />

<!-- 렌더링 후 -->
<input type="text" id="itemName" name="itemName" th:value="*{itemName}" />

등록 폼

th:object를 적용하려면 먼저 해당 오브젝트 정보를 넘겨줘야 한다.

등록 폼이기 때문에 데이터가 비어있는 빈 오브젝트를 만들어서 뷰에 넘겨 준다.

FormItemController

1
2
3
4
5
@GetMapping("/add")  
public String addForm(Model model) {  
    model.addAttribute("item", new Item());  
    return "form/addForm";  
}

이제 타임리프를 사용해 등록폼을 바꿔 보자.

resources/templates/form/addForm.html

1
2
3
4
5
6
7
8
9
10
11
12
13
<form action="item.html" th:action th:object="${item}" method="post">  
    <div>  
        <label for="itemName">상품명</label>  
        <input type="text" id="itemName" th:field="*{itemName}" class="form-control" placeholder="이름을 입력하세요">  
    </div>  
    <div>  
        <label for="price">가격</label>  
        <input type="text" id="price" th:field="*{price}" class="form-control" placeholder="가격을 입력하세요">  
    </div>  
    <div>  
        <label for="quantity">수량</label>  
        <input type="text" id="quantity" th:field="*{quantity}" class="form-control" placeholder="수량을 입력하세요">  
    </div>

태그에 id 항목은 인텔리제이에서 오류 띄워서 그냥 나뒀다. 없애도 상관 없다.

다음과 같이 name, value, id 필드가 자동으로 생성 되었다.

  • th:object="${item}": <form>에서 사용할 객체를 지정한다. 선택 변수 식(*{...})을 적용할 수 있다.
  • th:field="*{itemName}"
    • *{itmeName}은 선택 변수 식을 사용했는데, ${item.itemName}과 같은 의미이다. 앞서 th:objectitem을 선택했기 때문에 선택 변수 식을 적용할 수 있다.
    • th:object의 선택 범위는 현재 코드 상form 내부까지만이다.
    • th:fieldname, value, id속성을 모두 자동으로 만들어 준다.
      • id : th:field에서 지정한 변수 이름과 같다. id="itemName"
      • name : th:field에서 지정한 변수 이름과 같다. name="itemName"
      • value : th:field에서 지정한 변수의 값을 사용한다. value=""

수정 폼

1
2
3
4
5
6
@GetMapping("/{itemId}/edit")  
public String editForm(@PathVariable Long itemId, Model model) {  
    Item item = itemRepository.findById(itemId);  
    model.addAttribute("item", item);  
    return "form/editForm";  
}

다음과 같이 itemId를 넘겨 받아 item객체를 만들어서 폼에 전송한다.

기존 수정 폼 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<form action="item.html" th:action method="post">  
    <div>  
        <label for="id">상품 ID</label>  
        <input type="text" id="id" name="id" class="form-control" value="1" th:value="${item.id}" readonly>  
    </div>  
    <div>  
        <label for="itemName">상품명</label>  
        <input type="text" id="itemName" name="itemName" class="form-control" value="상품A" th:value="${item.itemName}">  
    </div>  
    <div>  
        <label for="price">가격</label>  
        <input type="text" id="price" name="price" class="form-control" value="10000" th:value="${item.price}">  
    </div>  
    <div>  
        <label for="quantity">수량</label>  
        <input type="text" id="quantity" name="quantity" class="form-control" value="10" th:value="${item.quantity}">  
    </div>

th:object 사용 수정 폼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<form action="item.html" th:action th:object="${item}" method="post">  
    <div>  
        <label for="id">상품 ID</label>  
        <input type="text" id="id" th:field="*{id}" class="form-control" readonly>  
    </div>  
    <div>  
        <label for="itemName">상품명</label>  
        <input type="text" id="itemName" th:field="*{itemName}" class="form-control" >  
    </div>  
    <div>  
        <label for="price">가격</label>  
        <input type="text" id="price" th:field="*{price}" class="form-control" >  
    </div>  
    <div>  
        <label for="quantity">수량</label>  
        <input type="text" id="quantity" th:field="*{quantity}" class="form-control">  
    </div>

자 name이랑 value 를 뺏는데 (id도 생략 가능), 잘 렌더링 되었다.

수정 폼은 앞서 설명한 내용과 같다. 수정 폼의 경우 id, name, value를 모두 신경 써야 했는데,

많은 부분이 th:field덕분에 자동으로 처리되는 것을 확인할 수 있다.

th:object, th:field 덕분에 폼을 개발할 때 약간의 편리함을 얻었다.

사실 이 기능의 위력은 검증(Validation)에서 나타난다고 한다.

요구 사항 추가

타임리프를 사용해서 폼에서 체크박스, 라디오 버튼, 셀렉트 박스를 편리하게 사용하는 방법을 학습해보자.

기존 상품 서비스에서 다음 요구 사항이 추가되었다.

  • 판매 여부
    • 판매 오픈 여부
    • 체크 박스로 선택할 수 있다.
  • 등록 지역
    • 서울, 부산, 제주
    • 체크 박스로 다중 선택 할 수 있다.
  • 상품 종류
    • 도서, 식품, 기타
    • 라디오 버튼으로 하나만 선택할 수 있다.
  • 배송 방식
    • 빠른 배송
    • 일반 배송
    • 느린 배송
    • 셀렉트 박스로 하나만 선택할 수 있다.

이제 클래스나 ENUM을 이용해서 만들어 보자.

ItemType - 상품 종류

hello.itemservice.domain.item.ItemType

1
2
3
4
5
6
7
8
9
public enum ItemType {  
    BOOK("도서"), FOOD("음식"), ETC("기타");  
  
    private final String description;  
  
    ItemType(String description) {  
        this.description = description;  
    }  
}

상품 종류는 ENUM을 사용한다. 설명을 위해 description필드를 추가했다. (생성자)

배송 방식 - DeliveryCode

hello.itemservice.domain.item.DeliveryCode

1
2
3
4
5
6
7
8
9
10
11
12
/**  
 * FAST : 빠른 배송  
 * NORMAL : 일반 배송  
 * SLOW : 느린 배송  
 * */  
@Data  
@AllArgsConstructor  
public class DeliveryCode {  
  
    private String code;  
    private String displayName;  
}

배송 방식은 DeliveryCode라는 클래스를 사용한다. codeFAST같은 시스템에서 전달하는 값이고, displayName은 빠른 배송 같은 고객에게 보여주는 값이다.

Item - 상품 (추가)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Data  
public class Item {  
  
    private Long id;  
    private String itemName;  
    private Integer price;  
    private Integer quantity;  
  
    private Boolean open;       // 판매 여부  
    private List<String> regions;   // 등록 지역  
    private ItemType itemType;      // 상품 종류  
    private String deliveryCode;    // 배송 방식  
  
    public Item() {  
    }  
    public Item(String itemName, Integer price, Integer quantity) {  
        this.itemName = itemName;  
        this.price = price;  
        this.quantity = quantity;  
    }  
}

다음과 같이 판매 여부, 지역, 종류, 방식이 추가 되었다.

이제 다음 강의부터 이걸 사용해 보자.

댓글남기기