티스토리 뷰
지난 웹 페이지 만들기 - 1 (복습)에 이어서 상품 상세 페이지와 상품 등록(add) 그리고 상품 수정(edit) 페이지를 만들어봤다. 코드를 다시 들여다보면서 강의를 제대로 이해했는지(?) 나름대로 어떤 과정이 되는지 이해한대로 작성했다.. ^^..
6. 상품 상세 페이지와 상세 등록 폼의 컨트롤러와 뷰 템플릿을 작성한다.
컨트롤러 전체 코드
→ web/item/ItemController.java
package magazink.re.web.item;
import lombok.RequiredArgsConstructor;
import magazink.re.domain.item.Item;
import magazink.re.domain.item.ItemRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.*;
@Controller
@RequiredArgsConstructor
@RequestMapping("/items")
public class ItemController {
private final ItemRepository itemRepository;
// 아이템 리스트 화면
@GetMapping
public String items(Model model) {
List<Item> items = itemRepository.findAll();
model.addAttribute("items", items);
return "items/items";
}
// 복습 2 - 코드 추가
// 아이템 상세 페이지
@GetMapping("/{itemId}")
public String item(@PathVariable Long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "items/item";
}
// 상품 등록 폼
@GetMapping("/add")
public String addForm(Model model) {
//로그인 여부 체크
model.addAttribute("item", new Item());
return "items/addForm";
}
// 상품 추가 후 저장
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes){
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/items/{itemId}";
}
}
각 컨트롤러를 자세히 살펴보자..(크게 자세히 볼 건 없지만 ^^..)
상품(Item) 상세 페이지
- @GetMapping 사용하여 itemId 값으로 지정
- 자원 자체를 나타낼 때는 @PathVariable으로 itemId을 받아오고, 모델(Model)에 담아둔다.
- 받아온 itemId값으로 itemRepository에서 item 값을 찾아와 변수에 저장한다.
- addAttribute("key", "value") 메서드를 이용해 view에 전달할 데이터를 "item" (key) item (value)로 전달한다.
// 아이템 상세 페이지
@GetMapping("/{itemId}")
public String item(@PathVariable Long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "items/item";
}
상품 등록 폼
- 상품 등록 폼으로 이동할 수 있게 @GetMapping 사용하여 ulr 경로를 지정
- 등록할 상품을 정하고 저장할 수 있게 @PostMapping 사용하여 동일한 경로 지정
- 기존에 Model model로 객체를 담아두고 addAttribute()를 사용해서 전달했다면, @ModelAttribute을 사용하면 addAttribute() 메서드를 사용하지 않고도 객체를 전달할 수 있다.
- RedirectAttributes를 사용하여 상품이 저장되었을 때 화면에 메시지를 출력할 수 있게 설정했다.
// 상품 등록 폼
@GetMapping("/add")
public String addForm(Model model) {
return "items/addForm";
}
// 상품 추가 후 저장
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes){
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/items/{itemId}";
상품 상세 페이지 뷰 템플릿
→ resources/templates/items/item.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
th: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>
<!-- 추가 -->
<h2 th:if="${param.status}" th:text="'저장 완료!'"></h2>
<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='@{/items/{itemId}/edit(itemId=${item.id})}'|"
type="button" th:text="#{page.updateItem}">상품 수정</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/items}'|"
type="button" th:text="#{page.items}">목록으로</button>
</div>
</div>
</div> <!-- /container -->
</body>
</html>
뷰 템플릿에서 th:if= 는 조건이 참(true) 이면 실행된다.
<h2 th:if="${param.status}" th:text="'저장 완료!'"></h2>
상품 등록 컨트롤러에서 사용한 RedirectAttributes의 "status" (key) 값이 true 일 때 위 조건이 실행되므로 상품 등록이 완료되면 화면에 "저장완료"로 표기가 된다.
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
상품 등록 후 나오는 뷰를 살펴보면 url에 status=true 상태값과 페이지에 " 저장 완료! " 가 나오는 것을 볼 수 있다.
상품 등록 폼 뷰 템플릿
→ resources/templates/items/addForm.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
th: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 th:text="#{page.addItem}">상품 등록</h2>
</div>
<h4 class="mb-3">상품 입력</h4>
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" class="form-control"
placeholder="이름을 입력하세요">
</div>
<div>
<label for="price" th:text="#{label.item.price}">가격</label>
<input type="text" id="price" th:field="*{price}" class=" form-control"
placeholder="가격을 입력하세요">
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
<input type="text" id="quantity" th:field="*{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" th:text="#{button.save}">
상품 등록</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='items.html'"
th:onclick="|location.href='@{/items}'|"
type="button" th:text="#{button.cancel}">취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
form 태그를 살펴보면 th:action과 th:object 가 있는데,
th:action은 HTML form에서 acition 값이 없으면 현재 URL에 데이터를 전송한다.
상품 등록 폼과 등록을 하는 URL을 /basic/items/add로 동일하게 사용하여 HTTP 메서드로 기능을 구분하게 했다.
- GET /basic/items/add
- POST /basic/itmes/add
이렇게 동일한 메서드로 보다 깔끔하게 URL을 처리했다.
<form action="item.html" th:action th:object="${item}" method="post">
th:object은 form 태그에서 받은 데이터가 th:object로 설정해준 {item} 객체로 받아진다.
서버 실행 후 상품 상세 페이지와 상품 등록 페이지로 접속하면 아래와 같이 위에서 설정한 URL대로 화면이 나오는 것을 확인할 수 있다.
상품 상세 페이지 상품 등록 페이지
→ http://localhost:8080/items/1 → http://localhost:8080/items/add
7. 등록한 상품을 수정하는 페이지의 컨트롤러와 뷰 템플릿을 작성한다.
컨트롤러 전체 코드
→ web/item/ItemController.java
package magazink.re.web.item;
import lombok.RequiredArgsConstructor;
import magazink.re.domain.item.Item;
import magazink.re.domain.item.ItemRepository;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import java.util.*;
@Controller
@RequiredArgsConstructor
@RequestMapping("/items")
public class ItemController {
private final ItemRepository itemRepository;
// 아이템 리스트 화면
@GetMapping
public String items(Model model) {
List<Item> items = itemRepository.findAll();
model.addAttribute("items", items);
return "items/items";
}
// 아이템 상세 페이지
@GetMapping("/{itemId}")
public String item(@PathVariable Long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "items/item";
}
// 상품 등록 폼
@GetMapping("/add")
public String addForm(Model model) {
//로그인 여부 체크
model.addAttribute("item", new Item());
return "items/addForm";
}
// 상품 추가 후 저장
@PostMapping("/add")
public String addItem(@ModelAttribute Item item, RedirectAttributes redirectAttributes){
Item savedItem = itemRepository.save(item);
redirectAttributes.addAttribute("itemId", savedItem.getId());
redirectAttributes.addAttribute("status", true);
return "redirect:/items/{itemId}";
}
// 상품 수정 폼
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "items/editForm";
}
// 상품 수정 처리
@PostMapping("/{itemId}/edit") // @ModelAttribute("item") 도 동일
public String edit(@PathVariable Long itemId, @ModelAttribute Item item){
itemRepository.update(itemId, item);
return"redirect:/items/{itemId}";
}
}
상품 수정 폼
- 상품 등록 폼과 마찬가지로 동일한 URL주소로 받고, HTTP메서드 구분을 통해 클라이언트 요청을 처리한다.
- 수정할 상품을 @PathVariable으로itemId을 받아오고, 모델(Model)에 담아둔다.
- 받아온 itemId로 itemRepository에서 찾아 model에 담고, addAttribute("key", "value") 메서드를 이용해 view에 전달할 데이터를 "item" (key) item (value)로 전달한다.
// 상품 수정 폼
@GetMapping("/{itemId}/edit")
public String editForm(@PathVariable Long itemId, Model model) {
Item item = itemRepository.findById(itemId);
model.addAttribute("item", item);
return "items/editForm";
}
상품 수정 처리 폼
- @PostMapping 사용하여 수정폼과 동일한 URL경로 지정
- @ModelAttribute을 사용하여 addAttribute() 메서드를 사용하지 않고 객체를 전달한다.
- @PathVariable으로itemId를 itemRepository에 업데이트한다.
- 스프링에서 리다이렉트를 지원하기 때문에 redirect:/로 저장 후 경로를 지정해 주었다.
※ itemRepository.update(itemId, Item) itemId는 @PathVariable으로itemId이고, item 은 model에 저장된 객체다.
// 상품 수정 처리
@PostMapping("/{itemId}/edit") // @ModelAttribute("item") 도 동일
public String edit(@PathVariable Long itemId, @ModelAttribute Item item){
itemRepository.update(itemId, item);
return"redirect:/items/{itemId}";
}
상품 수정 폼 뷰 템플릿
상품 등록 폼 뷰 템플릿과 동일하다.
→ resources/templates/items/editForm.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="utf-8">
<link href="../css/bootstrap.min.css"
th: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 th:text="#{page.updateItem}">상품 수정 폼</h2>
</div>
<form action="item.html" th:action th:object="${item}" method="post">
<div>
<label for="id" th:text="#{label.item.id}">상품 ID</label>
<input type="text" id="id" th:field="*{id}" class="form-control" readonly>
</div>
<div>
<label for="itemName" th:text="#{label.item.itemName}">상품명</label>
<input type="text" id="itemName" th:field="*{itemName}" class="form-control">
</div>
<div>
<div>
<label for="price" th:text="#{label.item.price}">가격</label>
<input type="text" id="price" th:field="*{price}" class="form-control">
</div>
<div>
<label for="quantity" th:text="#{label.item.quantity}">수량</label>
<input type="text" id="quantity" th:field="*{quantity}" class="form-control">
</div>
<hr class="my-4">
<div class="row">
<div class="col">
<button class="w-100 btn btn-primary btn-lg" type="submit" th:text="#{button.save}">
저장</button>
</div>
<div class="col">
<button class="w-100 btn btn-secondary btn-lg"
onclick="location.href='item.html'"
th:onclick="|location.href='@{/items/{itemId}(itemId=${item.id})}'|"
type="button" th:text="#{button.cancel}">>취소</button>
</div>
</div>
</form>
</div> <!-- /container -->
</body>
</html>
서버 실행 후 상품 수정을 누르면 아래와 같이 위에서 설정한 URL대로 화면이 나오는 것을 확인할 수 있다.
상품 수정 페이지
→ http://localhost:8080/items/1/edit
'study > Spring' 카테고리의 다른 글
웹 페이지 만들기 - 1 (복습) (0) | 2023.03.28 |
---|---|
chap 14 - @PathVariable, 익셉션 처리 (0) | 2023.02.27 |
chap 13 - Cookie (0) | 2023.02.23 |
chap 13 - HandlerInterceptor (0) | 2023.02.23 |
chap 13 - HttpSession (0) | 2023.02.21 |
- Total
- Today
- Yesterday
- 배열
- 디자인 패턴
- authenticate()
- django
- 톰캣
- django-environ
- musma
- path variable
- django.contrib.auth
- 회원 로그인
- git
- 웹페이지
- Magazine K
- 면접을 위한 CS 전공 지식 노트
- 혼자 공부하는 파이썬
- 커맨드 객체
- 회원가입
- Python
- Django tutorial
- 검색 결과 내 페이지네이션
- Java
- Spring
- python3
- error: failed to push some refs to 'https://github.com/
- API
- git공부
- 암호화
- 한글 형태소 분석기
- 환경 변수 설정
- git 공부
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |