본문 바로가기
BackEnd/JPA

[JPA] 웹 계층 개발(4)

by 개발 Blog 2024. 10. 5.

공부 내용을 정리하고 앞으로의 학습에 이해를 돕기 위해 작성합니다.

 

이번 글에서는 상품 주문 기능을 구현하는 과정을 살펴본다. OrderController를 통해 상품을 주문하고, 주문 목록을 조회하며, 주문을 취소하는 기능까지 구현한다.

 

상품 주문

메인 화면에서 "상품 주문" 버튼을 선택하면 /order 경로로 GET 요청이 발생한다. 이때 OrderControllercreateForm() 메서드가 실행되어, 주문할 회원과 상품 목록을 조회하고 이를 model에 담아 뷰에 넘겨준다. 해당 뷰는 orderForm.html로, 주문할 회원과 상품을 선택하고 수량을 입력할 수 있는 폼을 제공한다.

 

주문할 회원과 상품, 수량을 선택한 후 "Submit" 버튼을 누르면, /order 경로로 POST 요청이 발생한다. 이때 OrderController의 order() 메서드가 실행된다. 이 메서드는 다음과 같은 흐름으로 작동한다.

  • memberId, itemId, count를 파라미터로 받아 서비스 계층에 주문을 요청한다.
  • orderService.order(memberId, itemId, count)를 통해 실제 주문 로직이 실행된다.
  • 주문이 완료되면 주문 내역을 확인할 수 있는 /orders 경로로 리다이렉트 된다.

OrderController

package jpabook.jpashop.controller;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Order;
import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.repository.OrderSearch;
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.*;

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.findMembers();
        List<Item> items = itemService.findItems();

        model.addAttribute("members", members);
        model.addAttribute("items", items);

        return "order/orderForm";
    }

    @PostMapping("/order")
    public String order(@RequestParam("memberId") Long memberId,
                        @RequestParam("itemId") List<Long> itemIds,
                        @RequestParam("count") int count) {
        for (Long itemId : itemIds) {
            orderService.order(memberId, itemId, count);
        }
        return "redirect:/orders";
    }

    @GetMapping("/orders")
    public String orderList(@ModelAttribute("orderSearch") OrderSearch orderSearch, Model model) {
        List<Order> orders = orderService.findOrders(orderSearch);
        model.addAttribute("orders", orders);

        return "order/orderList";
    }

    @PostMapping("/orders/{orderId}/cancel")
    public String cancelOrder(@PathVariable("orderId") Long orderId) {
        orderService.cancelOrder(orderId);
        return "redirect:/orders";
    }
}

 

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>

 

주문 목록 검색, 취소

 

주문 목록 검색

주문 내역을 조회하기 위해 /orders 경로로 GET 요청이 발생하며, OrderController의 orderList() 메서드가 실행된다. 이 메서드는 orderSearch 객체를 사용하여 주문 목록을 검색하고, 이를 model에 담아 뷰로 전달한다. 뷰는 orderList.html로, 검색 결과를 테이블 형태로 표시한다.

 

주문 취소

주문 목록에서 "CANCEL" 버튼을 누르면 해당 주문을 취소할 수 있다. 이때 /orders/{orderId}/cancel 경로로 POST 요청이 발생하고, OrderController의 cancelOrder() 메서드가 실행되어 주문이 취소된다.

 

orderController 수정

package jpabook.jpashop.web;
@Controller
@RequiredArgsConstructor
public class OrderController {
    @PostMapping(value = "/orders/{orderId}/cancel")
    public String cancelOrder(@PathVariable("orderId") Long orderId) {
        orderService.cancelOrder(orderId);
    }
```

 

ordeList.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>
        <div>
            <form th:object="${orderSearch}" class="form-inline">
                <div class="form-group mb-2">
                    <input type="text" th:field="*{memberName}" class="form-control" 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>

 

'BackEnd > JPA' 카테고리의 다른 글

[JPA] 웹 계층 개발(3)  (0) 2024.10.05
[JPA] 웹 계층 개발(2)  (0) 2024.10.05
[JPA] 웹 계층 개발(1)  (2) 2024.10.04
[JPA] 주문 도메인 개발  (3) 2024.10.03
[JPA] 상품 도메인 개발  (0) 2024.10.03