[Spring Boot] 23. 주문 완료, 주문 정보 조회

shr·2022년 3월 9일
0

Spring

목록 보기
22/23
post-thumbnail

주문 완료


  1. 주문 완료 페이지
  • src/main/resources - templates - order - result.html
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
        <title>Insert title here</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
        <link rel="stylesheet" href="/css/main.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
    </head>
    <body>
    <div id="page">
        <header th:replace="/fragments/header.html">
        </header>
        <nav th:replace="/fragments/nav.html">
        </nav>
        <div id="main">
            <aside th:replace="/fragments/aside.html">
            </aside>
            <section>
                <div th:text="${order}"></div>
                <h1>주문이 완료되었습니다! (๑ᵔ⩊ᵔ๑)</h1>
                <a class="btn btn-info" href="/order/list">주문 정보 보기</a>
            </section>
        </div>
        <footer th:replace="/fragments/footer.html">
        </footer>
    </div>
    </body>
    </html>

  1. 컨트롤러와 서비스 설정
  • src/main/java - com.example.demo.controller.mvc - OrderController

    @Secured("ROLE_USER")
    @AllArgsConstructor
    @Controller
    public class OrderController {
        private OrderService service;
    
        // 주문 처리 성공 화면으로 forward
        @PostMapping("/order/new")
        public String order(Integer addressNo, Principal principal, HttpSession session, RedirectAttributes ra) {
            OrderDto.View dto = (OrderDto.View)session.getAttribute("dto");
            service.add(addressNo, dto, principal.getName());
            ra.addFlashAttribute("isNew", true);
            return "redirect:/order/result";
        }
    
        // 주문 처리 성공 화면
        @GetMapping("/order/result")
        public String result(HttpServletRequest request) {
            Map<String, ?> flashMap = RequestContextUtils.getInputFlashMap(request);
            if (flashMap == null)
                return "redirect:/";
            return "order/result";
        }
    }

    📝 /order/new를 거치지 않고 /order/result로 넘어갈 수 없게 하려고 한다. 이를 위해서 RedirectAttributes를 사용했다. /order/new에서 ra 값을 담아 이동시키고, /order/result에서 ra 값이 존재하는지를 확인한다.

    💡 Session과 RedirectAttributes의 차이

    Session은 값을 일정 시간 동안 저장해 놓지만 RedirectAttributes는 일회용에 가깝다. 예를 들어 내 정보 보기 화면에 들어가기 전에 비밀번호를 확인한다고 했을 때, Session을 이용하면 한번 비밀번호를 입력해 놓으면 일정 시간 동안은 다시 비밀번호를 확인하지 않지만 RedirectAttributes를 이용하면 내 정보 보기에 들어갈 때마다 비밀번호를 확인한다.

  • src/main/java - com.example.demo.service - OrderService

    package com.example.demo.service;
    
    @RequiredArgsConstructor
    @Service
    public class OrderService {
    
        private final OrderDao orderDao;
        private final OrderItemDao orderItemDao;
    
        @Transactional
        public void add(Integer addressNo, OrderDto.View dto, String loginId) {
            Order order = new Order(null, loginId, DeliveryStatus.PAY, LocalDate.now(), dto.getTotalprice(), addressNo);
            orderDao.save(order);
            for (OrderItem orderItem : dto.getOrderItems()) {
                orderItem.setOrderNo(order.getOrderNo());
                orderItemDao.save(orderItem);
            }
        }
    
    }

    💡 Transactional

    add()의 작업을 하나의 트랜잭션으로 묶는다. (모두 성공 아니면 모두 실패) 주로 서비스에서 DAO 작업들을 하나로 묶어 줄 때 사용한다. 예를 들어, Order와 OrderItem은 각각의 SQL이 존재하지만 주문이라는 작업을 위한 하나의 트랜잭션이라고 볼 수 있다. 따라서 Order 저장에는 성공하더라도 OrderItem 저장에 실패했다면 Order 저장까지 모두 날려 준다.


  1. DAO 설정
  • src/main/java - com.example.demo.dao - OrderDao

    package com.example.demo.dao;
    
    public interface OrderDao {
        @SelectKey(statement="select order_seq.nextval from dual", keyProperty="orderNo", before=true, resultType=Integer.class)
        @Insert("insert into orders values(#{orderNo}, #{username}, #{deliveryStatus}, #{orderday}, #{totalPrice}, #{addressNo})")
        public Integer save(Order order);
    }
  • src/main/java - com.example.demo.dao - OrderItemDao

    package com.example.demo.dao;
    
    public interface OrderItemDao {
        @Insert("insert into order_item values(#{orderNo}, #{pno}, #{vendor}, #{name}, #{price}, #{count}, #{orderItemPrice})")
        public void save(OrderItem orderItem);
    }

주문 정보 조회


  1. 주문 정보 페이지
  • src/main/resources - templates - order - list.html
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
        <title>Insert title here</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
        <link rel="stylesheet" href="/css/main.css">
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
        <script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
    </head>
    <body>
    <div id="page">
        <header th:replace="/fragments/header.html">
        </header>
        <nav th:replace="/fragments/nav.html">
        </nav>
        <div id="main">
            <aside th:replace="/fragments/aside.html">
            </aside>
            <section>
                <div th:text="${list}"></div>
            </section>
        </div>
        <footer th:replace="/fragments/footer.html">
        </footer>
    </div>
    </body>
    </html>

  1. 주문 정보 조회 페이지에서 필요한 정보들만 담은 DTO, 출력용 엔티티
  • src/main/java - com.example.demo.dto - OrderDto

    package com.example.demo.dto;
    
    @NoArgsConstructor(access = AccessLevel.PRIVATE)
    public class OrderDto {	
        @Data
        @AllArgsConstructor
        @Builder
        public static class Read {
            private Integer orderNo;
            private String username;
            private String deliveryStatus;
            private String orderday;
            private Integer totalPrice;
            private String address;
        }
    }
  • src/main/java - com.example.demo.entity - Order

    package com.example.demo.entity;
    
    @Data
    @AllArgsConstructor
    public class Order {
        public OrderDto.Read toRead() {
            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
            return OrderDto.Read.builder().orderNo(orderNo).username(username).deliveryStatus(deliveryStatus.getKorean())
                    .orderday(dtf.format(orderday)).totalPrice(totalPrice).build();
        }
    }

  1. 컨트롤러와 서비스
  • src/main/java - com.example.demo.controller.mvc - OrderController

    package com.example.demo.controller.mvc;
    
    @Secured("ROLE_USER")
    @AllArgsConstructor
    @Controller
    public class OrderController {
        private OrderService service;
    
        @GetMapping("/order/list")
        public void list(Model model, Principal principal) {
            model.addAttribute("list", service.readAll(principal.getName()));
        }
    
    }

    💡 ModelAndView

    • Model과 View 모두 사용하는 경우 → 원래대로 사용
    • View를 사용하지 않는 경우 → void 리턴
    • View만 사용하는 경우 → String 리턴
    • Model만 사용하는 경우 → Model 파라미터 받기
  • src/main/java - com.example.demo.service - OrderService

    package com.example.demo.service;
    
    @RequiredArgsConstructor
    @Service
    public class OrderService {
    
        private final OrderDao orderDao;
        private final OrderItemDao orderItemDao;
        private final AddressDao addressDao;
        private final ProductDao productDao;
    
        @Value("${product.image.path}")
        private String imagePath;
    
        // 나의 주문 정보
        public List<OrderDto.Read> readAll(String loginId) {
            List<Order> list = orderDao.findByUsername(loginId);
            List<OrderDto.Read> orderDtos = new ArrayList<>();
            for (Order order : list) {
                OrderDto.Read dto = order.toRead();
                Address address = addressDao.findByUsernameAndAddressNo(loginId, order.getAddressNo());
                dto.setAddress(address.getAddress1() + " " + address.getAddress2());
                orderDtos.add(dto);
            }
            return orderDtos;
        }
    
        // 내가 주문한 물품 정보
        public OrderDto.View orderOne(Integer pno, Integer count, String loginId) {
            Product product = productDao.findById(pno);
            OrderItem orderItem = new OrderItem(null, pno, product.getVendor(), product.getName(), imagePath+product.getImagename(), product.getPrice(), count, product.getPrice()*count);
            return new OrderDto.View(product.getPrice() * count, Arrays.asList(orderItem));
        }
    
    }

  1. DAO 설정
  • src/main/java - com.example.demo.dao - OrderDao

    package com.example.demo.dao;
    
    public interface OrderDao {
        @Select("select * from orders where username=#{username}")
        public List<Order> findByUsername(String username);
    }

profile
못하다 보면 잘하게 되는 거야 ・ᴗ・̥̥̥

0개의 댓글