스프링 MVC - 1편 - 웹 페이지 만들기 - (4) 상품 상세, 등록
인프런 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술편을 학습하고 정리한 내용 입니다.
상품 상세
상품 상세 컨트롤러와 뷰를 개발하자.
hello.itemservice.web.basic.BasicItemController
1
2
3
4
5
6
@GetMapping("/{itemId}")
public String item(Model model, @PathVariable("itemId") Long itemId) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "basic/item";
}
컨트롤러에 item 메서드를 추가했다.
PathVariable로 넘어온 상품 ID로 상품을 조회하고, 모델에 담아둔다. 그리고 뷰 템플릿을 호출한다.
상품 상세 뷰
정적 HTML을 뷰 템플릿(templates) 영역으로 복사하고 다음과 같이 수정하자.
/resources/static/item.html → 복사 → /resources/templates/basic/item.html
/resources/templates/basic/item.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>상품 상세</h2>
</div>
<div>
<label for="itemId">상품 ID</label>
<input type="text" id="itemId" name="itemId" 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}" readonly>
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" name="price" class="form-control"
value="10000" th:value="${item.price}" readonly>
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" name="quantity" class="form-control"
value="10" th:value="${item.quantity}" readonly>
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg"
onclick="location.href='editForm.html'"
th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|"
type="button" >상품 수정</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/basic/items}'|"
type="button">목록으로</button>
</div>
</div>
</div> <!-- /container -->
</body>
</html>
속성 변경 - th:value
th:value="${item.id}"
- 모델에 있는 item 정보를 획득하고 프로퍼티 접근법으로 출력한다. (
item.getId()) value속성을th:value속성으로 변경한다.

상품수정 링크
th:onclick="|location.href='@{/basic/items/{itemId}/edit(itemId=${item.id})}'|"

아직 못 만들었지만, url을 보면 잘 동작한다.
목록으로 링크
th:onclick="|location.href='@{/basic/items}'|"

상품 등록 폼
BasicItemController에 추가
1
2
3
4
@GetMapping("/add")
public String addForm() {
return "basic/addForm";
}
상품 등록 폼은 단순히 뷰 템플릿만 호출한다.
상품 등록 폼 뷰
정적 HTML을 뷰 템플릿(templates) 영역으로 복사하고 다음과 같이 수정하자.
/resources/static/addForm.html → 복사 → /resources/templates/basic/addForm.html
/resources/templates/basic/addForm.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link th:href="@{/css/bootstrap.min.css}"
href="../css/bootstrap.min.css" rel="stylesheet">
<style>
.container {
max-width: 560px;
}
</style>
</head>
<body>
<div class="container">
<div class="py-5 text-center">
<h2>상품 등록 폼</h2>
</div>
<h4 class="mb-3">상품 입력</h4>
<form action="item.html" th:action method="post" >
<div>
<label for="itemName">상품명</label>
<input type="text" id="itemName" name="itemName" class="form-control" placeholder="이름을 입력하세요">
</div>
<div>
<label for="price">가격</label>
<input type="text" id="price" name="price" class="form-control"
placeholder="가격을 입력하세요">
</div>
<div>
<label for="quantity">수량</label>
<input type="text" id="quantity" name="quantity" class="form-control" placeholder="수량을 입력하세요">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit">상품 등
록</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/basic/items}'|"
type="button">취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>

속성 변경 - th:action
th:action- HTML form에서
action에 값이 없으면 현재 URL에 데이터를 전송한다. - 상품 등록 폼의 URL과 실제 상품 등록을 처리하는 URL을 똑같이 맞추고, HTTP 메서드로 두 기능을 구분한다.
- 상품 등록 폼 : GET
/basic/items/add - 상품 등록 처리 : POST
/basic/items/add
- 상품 등록 폼 : GET
- 이렇게 하면 하나의 URL로 등록 폼과, 등록 처리를 깔끔하게 처리할 수 있다.

지금 컨트롤러가 없어서 안되지만, 데이터는 잘 넘어갔다.
취소
- 취소시 상품 목록으로 이동한다.
th:onclick="|location.href='@{/basic/items}'|"
상품 등록 처리 - @ModelAttribute
이제 상품 등록 폼에서 전달된 데이터로 실제 상품을 등록 처리해보자.
상품 등록 폼은 다음 방식으로 서버에 데이터를 전달한다.
- POST - HTML Form
content-type: application/x-www-form-urlencoded- 메시지 바디에 쿼리 파리미터 형식으로 전달
itemName=itemA&price=10000&quantity=10 - 예) 회원 가입, 상품 주문, HTML Form 사용
요청 파라미터 형식을 처리해야 하므로 @RequestParam을 사용하자.
상품 등록 처리 V1 - @RequestParam
addItemV1 - BasicItemController에 추가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@PostMapping("/add")
public String save(@RequestParam("itemName") String itemName,
@RequestParam("price") int price,
@RequestParam("quantity") Integer quantity,
Model model) {
Item item = new Item();
item.setItemName(itemName);
item.setPrice(price);
item.setQuantity(quantity);
itemRepository.save(item);
model.addAttribute("item", item);
return "basic/item";
}
- 먼저
@RequestParam("itemName") String itemName: itemName 요청 파라미터 데이터를 해당 변수에 받는다. Item객체를 생성하고itemRepository를 통해서 저장한다.- 저장된
item을 모델에 담아서 뷰에 전달한다.
중요: 여기서는 상품 상세에서 사용한 item.html 뷰 템플릿을 그대로 재활용한다.
실행해서 상품이 잘 저장되는지 확인하자.


리다이렉트 처리를 해줘야 할꺼 같은데 아무튼 잘 됐다.
상품 등록 처리 V2 - @ModelAttribute
@RequestParam으로 변수를 하나하나 받아서 Item 을 생성하는 과정은 불편했다.
이번에는 @ModelAttribute를 사용해서 한번에 처리해보자.
addItemV2 - 상품 등록 처리 - ModelAttribute
이전 코드는 주석 처리하고 새로 만들자.
1
2
3
4
5
6
@PostMapping("/add")
public String addItemV2(@ModelAttribute("item") Item item, Model model) {
itemRepository.save(item);
// model.addAttribute("item", item); // 이거 자동 추가임. 생략 가능
return "basic/item";
}

@ModelAttribute - 요청 파라미터 처리
@ModelAttribute는 Item객체를 생성하고, 요청 파라미터의 값을 프로퍼티 접근법(setXxx)으로 입력해준다.
@ModelAttribute - Model 추가
@ModelAttribute는 중요한 한 가지 기능이 더 있는데, 바로 모델(Model)에 @ModelAttribute로 지정한 객체를 자동으로 넣어준다.
지금 코드를 보면 model.addAttribute("item", item); 을 주석 처리 해버렸는데, 잘 동작하는 걸 볼 수 있다.
모델에 데이터를 담을 때는 이름이 필요하다. 이름은 @ModelAttribute에 지정한 name(value)속성을 사용한다. 만약 다음과 같이 @ModelAttribute의 이름을 다르게 지정하면 다른 이름으로 모델에 포함된다.
@ModelAttribute("hello") Item item → 이름을 hello로 지정
model.addAttribute("hello", item); → 모델에 hello 이름으로 저장
상품 등록 처리 V3 - ModelAttribute 이름 생략
1
2
3
4
5
@PostMapping("/add")
public String addItemV3(@ModelAttribute Item item) {
itemRepository.save(item);
return "basic/item";
}
이렇게 @ModelAttribute 생략이 가능하다.
@ModelAttribute의 이름을 생략하면 모델에 저장될 때 클래스명을 사용한다.
이때 클래스의 첫글자만 소문자로 변경해서 등록한다.
- 예)
@ModelAttribute 클래스명→ 모델에 자동 추가되는 이름Item→itemHelloWorld→helloWorld
상품 등록 처리 V4 - ModelAttribute 전체 생략
자 이제 극한으로 줄여보자.
1
2
3
4
5
@PostMapping("/add")
public String addItemV4(Item item) {
itemRepository.save(item);
return "basic/item";
}

@ModelAttribute자체도 생략 가능하다. 대상 객체는 모델에 자동 등록된다.
나머지 사항은 기존과 동일하다.
댓글남기기