목차
- 📌홈 화면
- 👀 회원 기능
- 👀 회원 등록
- 👀 회원 조회
- 📌상품 기능
- 👀 상품 등록
- 👀 상품 수정
- 👀 상품 조회
- 📌주문 기능
- 👀 상품 주문
- 👀 주문 내역 조회
- 👀 주문 취소
📌홈 화면
홈 컨트롤러
package jpabook.jpashop.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@Slf4j
public class HomeController {
@RequestMapping("/")
public String home(){
log.trace("트레이스 로깅");
log.warn("경고 로깅");
log.info("정보 로깅");
log.debug("디버깅용 로그");
log.error("에러로그");
log.info("HomeController, 테스트 보이나");
return "home";
}
}
@Slf4j를 이용해서 위 와같이 로그를 확인해 볼수 있다.
home.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header">
<title>Hello</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader" />
<div class="jumbotron">
<h1>HELLO SHOP</h1>
<p class="lead">회원 기능</p>
<p>
<a class="btn btn-lg btn-secondary" href="/members/new">회원 가입</a>
<a class="btn btn-lg btn-secondary" href="/members">회원 목록</a>
</p>
<p class="lead">상품 기능</p>
<p>
<a class="btn btn-lg btn-dark" href="/items/new">상품 등록</a>
<a class="btn btn-lg btn-dark" href="/items">상품 목록</a>
</p>
<p class="lead">주문 기능</p>
<p>
<a class="btn btn-lg btn-info" href="/order">상품 주문</a>
<a class="btn btn-lg btn-info" href="/orders">주문 내역</a>
</p>
</div>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
fragments/header
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head th:fragment="header">
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrinkto-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-
ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
crossorigin="anonymous">
<!-- Custom styles for this template -->
<link href="/css/jumbotron-narrow.css" rel="stylesheet">
<title>Hello, world!</title>
</head>
fragments/bodyHeader
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="header" th:fragment="bodyHeader">
<ul class="nav nav-pills pull-right">
<li><a href="/">Home</a></li>
</ul>
<a href="/"><h3 class="text-muted">HELLO SHOP</h3></a>
</div>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<div class="footer" th:fragment="footer">
<p>© Hello Shop V2</p>
</div>
뷰 리소스 등록
jumbotron-narrow.css 파일
/* Space out content a bit */
body {
padding-top: 20px;
padding-bottom: 20px;
}
/* Everything but the jumbotron gets side spacing for mobile first views */
.header,
.marketing,
.footer {
padding-left: 15px;
padding-right: 15px;
}
/* Custom page header */
.header {
border-bottom: 1px solid #e5e5e5;
}
/* Make the masthead heading the same height as the navigation */
.header h3 {
margin-top: 0;
margin-bottom: 0;
line-height: 40px;
padding-bottom: 19px;
}
/* Custom page footer */
.footer {
padding-top: 19px;
color: #777;
border-top: 1px solid #e5e5e5;
}
/* Customize container */
@media (min-width: 768px) {
.container {
max-width: 730px;
}
}
.container-narrow > hr {
margin: 30px 0;
}
/* Main marketing message and sign up button */
.jumbotron {
text-align: center;
border-bottom: 1px solid #e5e5e5;
}
.jumbotron .btn {
font-size: 21px;
padding: 14px 24px;
}
/* Supporting marketing content */
.marketing {
margin: 40px 0;
}
.marketing p + h4 {
margin-top: 28px;
}
/* Responsive: Portrait tablets and up */
@media screen and (min-width: 768px) {
/* Remove the padding we set earlier */
.header,
.marketing,
.footer {
padding-left: 0;
padding-right: 0;
}
/* Space out the masthead */
.header {
margin-bottom: 30px;
}
/* Remove the bottom border on the jumbotron for visual effect */
.jumbotron {
border-bottom: 0;
}
}
결과 화면
👀 회원 기능
👀 회원 등록
회원 등록 폼 객체
package jpabook.jpashop.controller;
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.NotEmpty;
@Getter
@Setter
public class MemberForm {
@NotEmpty(message = "회원 이름을 기입해 주세요")
//스프링부트 2.3부터는 implementation 'org.springframework.boot:spring-boot-starter-validation' 의존성을 추가해야 사용가능하다.
private String name;
@NotEmpty(message = "도시명을 기입해 주세요")
private String city;
@NotEmpty(message = "거리명을 기입해 주세요")
private String street;
@NotEmpty(message = "우편번호를 기입해 주세요")
private String zipcode;
}
✏ @NotEmpty
- Bean validation이 제공하는 어노테이션이다.
- javax.validation.constraints.NotEmpty 를 사용해서 데이터가 비어어있을 경우를 허용하지 않는다.
- 스프링부트 2.3부터는 implementation 'org.springframework.boot:spring-boot-starter-validation' 의존성을 추가해야 사용가능하다.
회원 등록 컨트롤러
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(value = "/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:/";
}
}
✏ 멤버 폼을 따로 만들어서 사용하는 이유.
- model.addAttribute("memberForm", new MemberForm());
- Member 객체대신 MemberForm을 파라미터로 받는 이유는
엔티티 소스가 @NotEmpty같은 어노테이션을 추가하면서 지저분해질수
있다. - 화면에서의 폼 데이터와 엔티티 데이터와 맞지않아서 차라리 화면에 딱
맞는 폼데이터를 만들어서 데이터를 받는게 낫기때문이다. - 실무에서는 간단한 폼 데이터를 갖는경우는 거의 없기 떄문에 폼데이터를
따로 만들어서 관리한다.✏Model 이란?
- import org.springframework.ui.Model;
- Model은 HashMap 형태를 갖고 있으며, Key,Value값을 가지고 있다.
- addAttribute() 와 같은 기능을 통해 모델에 원하는 속성과 그것에대한 값을 주어 뷰에 데이터를 전달할 수 있다.
- Spring에서 Controller의 메서드를 작성할 때 Model을 타입의 파라미터로 지정이 가능하다.
- Model 객체는 컨트롤러에서 생성된 데이터를 담아서 프론트에 전달해 준다.
👀 회원 조회
회원 목록 컨트롤러 추가
기존 MemberController 아래의 코드만 추가 했다.
... //회원 목록 컨트롤러 @GetMapping("members") public String create(Model model){ List<Member> members = memberService.findMember(); model.addAttribute("members", members); return "members/memberList"; }
회원 목록 뷰( templates/members/memberList.html)
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader" />
<div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>이름</th>
<th>도시</th>
<th>주소</th>
<th>우편번호</th>
</tr>
</thead>
<tbody>
<tr th:each="member : ${members}">
<td th:text="${member.id}"></td>
<td th:text="${member.name}"></td>
<td th:text="${member.address?.city}"></td>
<td th:text="${member.address?.street}"></td>
<td th:text="${member.address?.zipcode}"></td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
✏ th:each="member : ${members}" 의 의미
- 자바의 for(member : members)의 의미이며 리스트를 members의 길이만큼 member담아서 뿌려준다.
✏ 타임리프문법"?"
- 타임리프에서 ? 사용하면 null을 무시한다.
✏ 참고: 폼 객체 vs 엔티티 직접 사용
- 요구사항이 정말 단순할 때는 폼 객체( MemberForm ) 없이 엔티티( Member )를 직접 등록과 수정 화면
에서 사용해도 된다. - 하지만 화면 요구사항이 복잡해지기 시작하면, 엔티티에 화면을 처리하기 위한 기능이
점점 증가한다. - 결과적으로 엔티티는 점점 화면에 종속적으로 변하고, 이렇게 화면 기능 때문에 지저분해진
엔티티는 결국 유지보수하기 어려워진다. - 실무에서 엔티티는 핵심 비즈니스 로직만 가지고 있고, 화면을 위한 로직은 없어야 한다.
- 화면이나 API에 맞는 폼 객체나 DTO를 사용하자.
- 그래서 화면이나 API 요구사항을 이것들로 처리하고, 엔티티는 최대한 순수
하게 유지하자
📌상품 기능
👀 상품 등록
BookForm
- 멤버폼과 같은이유로 주문생성화면에서 필요한 데이터만 갖고있는 폼을 따로 만들어 서버와 데이터를 주고받을 수 있게 만들었다.
package jpabook.jpashop.controller;
import lombok.Getter;
import lombok.Setter;
@Getter@Setter
public class BookForm {
//상품 수정시 id 필요
private Long id;
//이름
private String name;
//가격
private int price;
//재고
private int stockQuantity;
//작가
private String author;
//isbn
private String isbn;
}
아이템 컨트롤러
package jpabook.jpashop.controller;
import jpabook.jpashop.domain.item.Book;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.service.ItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;
//1.상품 등록페이지 리턴
@GetMapping("/items/new")
public String createItems(Model model) {
model.addAttribute("form",new BookForm());
return "items/createItemForm";
}
//2.상품등록 데이터 저장
@PostMapping("items/new")
public String create(BookForm form){
Book book = new Book();
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
itemService.saveItem(book);
return "redirect:/";
}
//3.상품 목록 뿌리기
@GetMapping(value = "/items")
public String itemList(Model model) {
List<Item> items = itemService.finItem();
model.addAttribute("items",items);
return "items/itemlist";
}
}
✏ 1.상품 등록페이지 리턴
- model.addAttribute()을 이용해서 키와 값으로 북폼객체를 담아서 상품등록페이지 리턴
✏ 2.상품 등록페이지 리턴
- 상품 등록폼에서 데이터를 받아 Submit버튼 클릭시 포스트 방식으로 컨트롤러에 전달후 전달받은 데이터를 근거로 상품 저장 후 return "redirect:/" 상품 목록화면으로 리다이렉트
✏ 3.상품 목록 뿌리기
- itemService.finItem() 로직으로 찾은 상품을 리스트 타입으로 저장후 model.addAttribute() 를 이용해서 상품이 담긴 객체를 return "items/itemlist" 로 응답해준다.
상품 등록 뷰
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<form th:action="@{/items/new}" th:object="${form}" method="post">
<div class="form-group">
<label th:for="name">상품명</label>
<input type="text" th:field="*{name}" class="form-control"
placeholder="이름을 입력하세요">
</div>
<div class="form-group">
<label th:for="price">가격</label>
<input type="number" th:field="*{price}" class="form-control"
placeholder="가격을 입력하세요">
</div>
<div class="form-group">
<label th:for="stockQuantity">수량</label>
<input type="number" th:field="*{stockQuantity}" class="formcontrol" placeholder="수량을 입력하세요">
</div>
<div class="form-group">
<label th:for="author">저자</label>
<input type="text" th:field="*{author}" class="form-control"
placeholder="저자를 입력하세요">
</div>
<div class="form-group">
<label th:for="isbn">ISBN</label>
<input type="text" th:field="*{isbn}" class="form-control"
placeholder="ISBN을 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br/>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
상품 목록 뷰
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>상품명</th>
<th>가격</th>
<th>재고수량</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${items}">
<td th:text="${item.id}"></td>
<td th:text="${item.name}"></td>
<td th:text="${item.price}"></td>
<td th:text="${item.stockQuantity}"></td>
<td>
<a href="#" th:href="@{/items/{id}/edit (id=${item.id})}"
class="btn btn-primary" role="button">수정</a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/footer :: footer"/>
</div> <!-- /container -->
</body>
</html>
👀 상품 수정
상품수정 컨트롤러 코드
package jpabook.jpashop.controller;
import jpabook.jpashop.domain.item.Book;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.service.ItemService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class ItemController {
//상품 수정 폼이동
@GetMapping("items/{itemId}/edit")
public String updateItemForm(@PathVariable("itemId") Long itemId, Model model) {
Book item = (Book) itemService.findOne(itemId);
BookForm form = new BookForm();
form.setId(item.getId());
form.setName(item.getName());
form.setPrice(item.getPrice());
form.setStockQuantity(item.getStockQuantity());
form.setAuthor(item.getAuthor());
form.setIsbn(item.getIsbn());
model.addAttribute("form", form);
return "items/updateItemForm";
}
//상품 수정
@PostMapping("items/{itemId}/edit")
public String updateItem(@ModelAttribute("form") BookForm form) {
Book book = new Book();
book.setId(form.getId());
book.setName(form.getName());
book.setPrice(form.getPrice());
book.setStockQuantity(form.getStockQuantity());
book.setAuthor(form.getAuthor());
book.setIsbn(form.getIsbn());
itemService.saveItem(book);
return "redirect:/items";
}
}
상품 수정 폼 화면( items/updateItemForm )
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<form th:object="${form}" method="post">
<!-- id -->
<input type="hidden" th:field="*{id}" />
<div class="form-group">
<label th:for="name">상품명</label>
<input type="text" th:field="*{name}" class="form-control"
placeholder="이름을 입력하세요" />
</div>
<div class="form-group">
<label th:for="price">가격</label>
<input type="number" th:field="*{price}" class="form-control"
placeholder="가격을 입력하세요" />
</div>
<div class="form-group">
<label th:for="stockQuantity">수량</label>
<input type="number" th:field="*{stockQuantity}" class="form-
control" placeholder="수량을 입력하세요" />
</div>
<div class="form-group">
<label th:for="author">저자</label>
<input type="text" th:field="*{author}" class="form-control"
placeholder="저자를 입력하세요" />
</div>
<div class="form-group">
<label th:for="isbn">ISBN</label>
<input type="text" th:field="*{isbn}" class="form-control"
placeholder="ISBN을 입력하세요" />
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
✏ 상품 수정 폼 이동
- 상품 목록폼에서 수정 버튼 클릭시 ->/items/{itemId}/edit URL을 GET 방식으로 요청
- 아이템 컨트롤러의 updateItemForm() 실행 ->itemService.findOne(itemId);호출하고 수정할 아이템 조회
- model.addAttribute("form", form); = 조회 결과과를 모델 객체에 담아서 -> items/updateItemForm 전달
✏ 상품 수정 실행
- 상품 수정폼에서 정보수정 후 Submit 클릭
- /items/{itemId}/edit URL을 POST 방식으로 요청하고 updateItem() 메서드를 실행
- 그리고 컨트롤러 파라미터로 넘어온 itme 엔티티 인스턴스는 준영속성 상태다. 따라서 수정을 해도 변경 감지 기능은 동작하지 않는다.
📌변경 감지와 병합(marge) "중요"
준영속성 엔티티란?
- JPA(영속성컨텍스트)가 더 이상 관리하지 않는 엔티티
- DB에 한번 저장 되어서 ID(식별자가)있는 엔티티
준영속성 엔티티의 데이터를 수정하는 2가지 방법
- 변경 감지 기능 사용 (Dirty Checking)
- 영속성 컨텍스트에서 엔티티를 다시 조회한 후 데이터를 수정하는 방법이다.
- 병합 사용 (merge)
- 병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능이다.
- 병합은 준영속 상태의 엔티티를 영속 상태로 변경할 때 사용하는 기능이다.
변경 감지 기능 사용(dirty checking)
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item findItem = em.find(Item.class, itemParam.getId()); //같은 엔티티를 조회한
다.
findItem.setPrice(itemParam.getPrice()); //데이터를 수정한다.
}
- 영속성 컨텍스트에서 엔티티를 다시 조회한 후 데이터를 수정하는 방법이다.
- 변경할 엔티티를 파라미터로 받고 ->트렌잭션 안에서 다시 엔티티를 조회, 변경할 값 선택 -> 트랜잭션 커밋 시점에 더티체킹을하여 데이터베이스에 업데이트 쿼리를 날린다.
병합 사용(merge)
@Transactional
void update(Item itemParam) { //itemParam: 파리미터로 넘어온 준영속 상태의 엔티티
Item mergeItem = em.merge(item);
}
병합 동작방식 요약 정리
- 준영속 엔티티의 식별자 값으로 영속 엔티티를 조회한다.
- 영속 엔티티의 값을 준영속 엔티티의 값으로 모두 교체한다.(병합한다.)
- 트랜잭션 커밋 시점에 변경 감지 기능이 동작해서 데이터베이스에 UPDATE SQL이 실행
병합사용 주의 점
- merge를 사용시 모든데이터(속성)이 변경되어, 변경시 값이 없을 경우 Null값으로 업데이트가 되어 실무에서 위험하다.(병합은 모든 필드를 교체)
👍 merge vs dirty Checking 차이
- 변경감지(dirtyChecking)을 사용하면 원하는 데이터(속성만) 선택해서 데이터를 업데이트 할 수 있다.
- 병합은 모든 데이터가 비어있으면 null로 업데이트하면서 모든 필드를 교체한다.
👍 그냥 엔티티를 변경할 때는 항상 변경감지를 사용하자!
📌주문 기능
👀 상품 주문
상품주문 컨트롤러
package jpabook.jpashop.controller;
import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.service.ItemService;
import jpabook.jpashop.service.MemberService;
import jpabook.jpashop.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
private final MemberService memberService;
private final ItemService itemService;
//주문폼 이동
@GetMapping("/order")
public String createForm(Model model) {
List<Member> members = memberService.findMember();
List<Item> items = itemService.finItem();
model.addAttribute("members", members);
model.addAttribute("items", items);
return "order/orderForm";
}
//주문 데이터 받기
@PostMapping(value = "/order")
public String createOrder(@RequestParam("memberId") Long memberId,
@RequestParam("itemId") Long itemId,
@RequestParam("count") int count
) {
orderService.order(memberId, itemId, count);
return "redirect:/order";
}
}
주문 폼 이동
- 메인화면에서 상품주문 클릭시 @GetMapping("/order") 호출
- createForm()실행
- model.addAttribute("members", members);
model.addAttribute("items", items); 를 담아서 보내준다.
주문 실행
- 주문할 회원과 상품, 수량 선택 후 Submit버튼 클릭시
@PostMapping(value = "/order") 호출 받음- memberId , itemId,count 데이터를 파라미터로 받고
- orderService.order(memberId, itemId, count); 오더 서비스의 오더()를 실행한다.
- 주문 끝나면 상품 주문 내역이 있는"/order"로 리다이렉트 한다.
상품 주문 폼 (order/orderForm.html)
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header" />
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<form role="form" action="/order" method="post">
<div class="form-group">
<label for="member">주문회원</label>
<select name="memberId" id="member" class="form-control">
<option value="">회원선택</option>
<option th:each="member : ${members}"
th:value="${member.id}"
th:text="${member.name}" />
</select>
</div>
<div class="form-group">
<label for="item">상품명</label>
<select name="itemId" id="item" class="form-control">
<option value="">상품선택</option>
<option th:each="item : ${items}"
th:value="${item.id}"
th:text="${item.name}" />
</select>
</div>
<div class="form-group">
<label for="count">주문수량</label>
<input type="number" name="count" class="form-control" id="count"
placeholder="주문 수량을 입력하세요">
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
<br/>
<div th:replace="fragments/footer :: footer" />
</div> <!-- /container -->
</body>
</html>
👀 주문 내역 조회
//주문내역//
@GetMapping("/orde rs")
public String orderList(@ModelAttribute("orderSearch") OrderSearch orderSearch, Model model) {
List<Order> orders = orderService.findOrders(orderSearch);
model.addAttribute("orders" , orders);
return "order/orderList";
}
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:replace="fragments/header :: header"/>
<body>
<div class="container">
<div th:replace="fragments/bodyHeader :: bodyHeader"/>
<div>
<div>
<form th:object="${orderSearch}" class="form-inline">
<div class="form-group mb-2">
<input type="text" th:field="*{memberName}" class="formcontrol" placeholder="회원명"/>
</div>
<div class="form-group mx-sm-1 mb-2">
<select th:field="*{orderStatus}" class="form-control">
<option value="">주문상태</option>
<option th:each=
"status : ${T(jpabook.jpashop.domain.OrderStatus).values()}"
th:value="${status}"
th:text="${status}">option
</option>
</select>
</div>
<button type="submit" class="btn btn-primary mb-2">검색</button>
</form>
</div>
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>회원명</th>
<th>대표상품 이름</th>
<th>대표상품 주문가격</th>
<th>대표상품 주문수량</th>
<th>상태</th>
<th>일시</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:each="item : ${orders}">
<td th:text="${item.id}"></td>
<td th:text="${item.member.name}"></td>
<td th:text="${item.orderItems[0].item.name}"></td>
<td th:text="${item.orderItems[0].orderPrice}"></td>
<td th:text="${item.orderItems[0].count}"></td>
<td th:text="${item.status}"></td>
<td th:text="${item.orderDate}"></td>
<td>
<a th:if="${item.status.name() == 'ORDER'}" href="#"
th:href="'javascript:cancel('+${item.id}+')'"
class="btn btn-danger">CANCEL</a>
</td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/footer :: footer"/>
</div> <!-- /container -->
</body>
<script>
function cancel(id) {
var form = document.createElement("form");
form.setAttribute("method", "post");
form.setAttribute("action", "/orders/" + id + "/cancel");
document.body.appendChild(form);
form.submit();
}
</script>
</html>
👀 주문 취소
//주문 취소
@PostMapping("/order/{orderId}/cancel")
public String cancelOrder(@PathVariable("orderId") Long orderId){
orderService.cancelOrder(orderId);
return "redirect/orders";
}
REF
'강의 > 스프링부트 JPA활용 1' 카테고리의 다른 글
스프링부트 JPA활용 1 - 주문 도메인 개발(6) (0) | 2022.06.10 |
---|---|
스프링부트 JPA활용 1 - 상품 도메인 개발(5) (0) | 2022.06.10 |
스프링부트 JPA활용 1 - 회원 도메인 개발(4) (0) | 2022.06.10 |
스프링부트 JPA활용 1 - 애플리케이션 구현 준비(3) (0) | 2022.06.10 |
스프링부트 JPA활용 1 - 도메인 분석 설계(2) (0) | 2022.06.10 |