[스프링 부트 쇼핑몰 프로젝트 with JPA] 11. 상품 주문

2023. 10. 16. 19:17·Web & Android/스프링 부트 쇼핑몰 프로젝트 with JPA
이 내용은 스프링 부트 쇼핑몰 프로젝트 with JPA 책을 학습한 내용입니다.

1. 상품 재고 부족 Exception

  • 상품 주문 수량보다 현재 재고의 수가 적을 때 발생시킬 Exception 정의
  • 에러 메시지를 지정할 수 있는 RuntimeException 클래스 구현
// 상품 주문 수량보다 현재 재고의 수가 적을 때 발생시킬 Exception
public class OutOfStockException extends RuntimeException {

    public OutOfStockException(String message) {
        super(message);
    }
}

2. 상품 재고 변경

  • (기존 재고 - 주문 수량 재고) 로 stockNumber 수정
  • 만약 0 보다 작다면 재고가 부족한 것이므로 Exception 발생
// item 클래스
// 상품 재고 변경
    public void removeStock(int stockNumber) {
        // (기존 재고 - 주문 수량 재고)
        int restStock = this.stockNumber - stockNumber;

        // 만약 0보다 작다면 재고가 부족한 것이므로 Exception 발생
        if(restStock < 0) {
            throw new OutOfStockException("상품의 재고가 부족합니다. (현재 재고 수량 : " + this.stockNumber + ")");
        }
        this.stockNumber = restStock;
    }

3. OrderItem 객체

  • 주문 상품과 주문 수량 정보를 가지고 있는 OrderItem Entity 에 객체 생성 메소드 추가
// 주문 상품과 주문 수량 정보를 가지고 있는 메소드
    public static OrderItem createOrderItem(Item item, int count) {

        OrderItem orderItem = new OrderItem();
        orderItem.setItem(item);
        orderItem.setCount(count);
        orderItem.setOrderPrice(item.getPrice());

        item.removeStock(count);
        return orderItem;
    }

    public int getTotalPrice() {
        return orderPrice * count;
    }

4. Order 객체

  • OrderItem 객체를 연결하고 OrderItem 객체에 자신을 연결하는 메소드 추가
  • OrderItem 객체를 이용하여 주문 객체를 만드는 메소드 추가
  • 각 주문 상품의 TotalPrice를 구한뒤 모두 더하는 메소드 추가
// OrderItem 객체 연결 및 자신을 연결하는 메소드
    public void addOrderItem(OrderItem orderItem) {
        orderItems.add(orderItem); // 주문 객체에 주문 상품 객체 연결
        orderItem.setOrder(this);  // 주문 상품 객체에 주문 객체 연결(연관 관계 주인)
    }

    // 주문 객체를 만드는 메소드
    public static Order createOrder(Member member, List<OrderItem> orderItemList) {
        Order order = new Order();
        order.setMember(member);

        for(OrderItem orderItem : orderItemList) {
            order.addOrderItem(orderItem);
        }

        order.setOrderStatus(OrderStatus.ORDER);
        order.setOrderDate(LocalDateTime.now());
        return order;
    }

    // 각 주문 상품의 TotalPrice를 구한 뒤 모두 더하는 메소드
    public int getTotalPrice() {
        int totalPrice = 0;

        // 각 상품마다 TotalPrice를 구하고 모두 더함
        for(OrderItem orderItem : orderItems) {
            totalPrice += orderItem.getTotalPrice();
        }
        return totalPrice;
    }

5. 주문 정보 Dto

  • 제품 상세 페이지 화면에서 보내는 주문 정보 (상품, 수량)를 위한 DTO 객체 생성
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class OrderDto {

    @NotNull(message = "상품 아이디는 필수 입력 값입니다.")
    private Long itemId;

    @Min(value = 1, message = "최소 주문 수량은 1개입니다.")
    @Max(value = 999, message = "최대 주문 수량은 999개입니다.")
    private int count;

}

6. OrderService

  • 상품과 주문한 고객을 조회
  • 주문 상품 객체 생성 -> 주문 객체 생성
@Service
@Transactional
@RequiredArgsConstructor
public class OrderService {

    private final ItemRepository itemRepository;   // 상품을 불러와서 재고 변경해야 함
    private final MemberRepository memberRepository; // 멤버를 불러와서 연결해야 함
    private final OrderRepository orderRepository;  // 주문 객체를 저장해야 함
    private final ItemImgRepository itemImgRepository;  // 상품 대표 이미지를 출력해야함

    // 상품과 주문한 고객 조회
    public Long order(OrderDto orderDto, String email) {
        Item item = itemRepository.findById(orderDto.getItemId()).orElseThrow(EntityNotFoundException::new);
        Member member = memberRepository.findByEmail(email);

        List<OrderItem> orderItemList = new ArrayList<>();
        
        // OrderItem.createOrderItem -> static 메소드
        OrderItem orderItem = OrderItem.createOrderItem(item, orderDto.getCount());
        orderItemList.add(orderItem);
        
        // Order.createOrder -> static 메소드
        Order order = Order.createOrder(member, orderItemList);
        orderRepository.save(order);
        
        return order.getId();
    }

7. OrderController

  • 비동기 방식으로 Json 데이터를 주고 받음
    • @RequestBody : http 요청의 body 부분 데이터를 자바 객체로 변환해서 받음
    • @ResponseBody : 자바 객체를 http 응답 body 부분으로 보냄 (ResponseEntity 자료형)
  • 입력값에 문제가 있을 시 필드 에러 정보들을 ResponseEntity 객체에 담아서 반환
  • 현재 로그인한 유저의 정보를 담고 있는 Principal 객체에서 유저의 email 추출
  • 정상적으로 동작 시 생성된 주문 객체의 Id 와 상태코드 200을 보냄
@Controller
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    @PostMapping("/order")
    @ResponseBody
    public ResponseEntity order(@RequestBody @Valid OrderDto orderDto, BindingResult bindingResult, Principal principal) {

        if(bindingResult.hasErrors()) {
            StringBuilder sb = new StringBuilder();
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            for(FieldError fieldError : fieldErrors) {
                sb.append(fieldError.getDefaultMessage());
            }

            return new ResponseEntity<String>(sb.toString(), HttpStatus.BAD_REQUEST);
        }

        // 현재 로그인한 유저의 정보를 담고 있는 Principal 객체에서 유저의 email 추출
        String email = principal.getName();
        Long orderId;

        try {
            orderId = orderService.order(orderDto, email);
        } catch (Exception e) {
            return new ResponseEntity<String>(e.getMessage(), HttpStatus.BAD_REQUEST);
        }

        // 정상 작동 시, 생성된 주문 객체의 id와 상태코드 200 보냄
        return new ResponseEntity<Long>(orderId, HttpStatus.OK);
    }

8. 주문 상세 페이지 수정

  • 비동기 통신 방법인 Ajax 를 이용하여 주문 요청 및 응답
  • "주문하기" 버튼을 누르면 order() 함수 스크립트를 수행하도록 지정
<button type="button" class="btn btn-primary btn-lg" onclick="order()">주문하기</button>
  • order() 함수 Ajax 코드
<!-- itemDetail.html -->
function order(){
      var token = $("meta[name='_csrf']").attr("content");
      var header = $("meta[name='_csrf_header']").attr("content");

      var url = "/order";
      var paramData = {
        itemId : $("#itemId").val(),
        count : $("#count").val()
      };

      var param = JSON.stringify(paramData);

      $.ajax({
        url      : url,
        type     : "POST",
        contentType : "application/json",
        data     : param,
        beforeSend : function(xhr){
          /* 데이터를 전송하기 전에 헤더에 csrf값을 설정 */
          xhr.setRequestHeader(header, token);
        },
        dataType : "json",
        cache   : false,
        success  : function(result, status){
          alert("주문이 완료 되었습니다.");
          location.href='/';
        },
        error : function(jqXHR, status, error){

          if(jqXHR.status == '401'){
            alert('로그인 후 이용해주세요');
            location.href='/members/login';
          } else{
            alert(jqXHR.responseText);
          }

        }
      });
    }
  • 스프링 시큐리티를 사용할 경우 CSRF 토큰 값을 자동으로 주고 받지만, Ajax 통신할 때는 직접 코드를 구현해서 CSRF 토큰을 보내야함
<meta name="_csrf" th:content="${_csrf.token}"/>
  <meta name="_csrf_header" th:content="${_csrf.headerName}"/>
  • 주문 성공

저작자표시 비영리 변경금지 (새창열림)

'Web & Android > 스프링 부트 쇼핑몰 프로젝트 with JPA' 카테고리의 다른 글

[스프링 부트 쇼핑몰 프로젝트 with JPA] 13. 주문 취소  (0) 2023.10.16
[스프링 부트 쇼핑몰 프로젝트 with JPA] 12. 구매 내역 조회  (0) 2023.10.16
[스프링 부트 쇼핑몰 프로젝트 with JPA] 10. 상세페이지  (0) 2023.10.16
[스프링 부트 쇼핑몰 프로젝트 with JPA] 9. 메인화면  (0) 2023.10.16
[스프링 부트 쇼핑몰 프로젝트 with JPA] 8-2. [상품 관리] 상품 목록 페이지  (0) 2023.10.16
'Web & Android/스프링 부트 쇼핑몰 프로젝트 with JPA' 카테고리의 다른 글
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 13. 주문 취소
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 12. 구매 내역 조회
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 10. 상세페이지
  • [스프링 부트 쇼핑몰 프로젝트 with JPA] 9. 메인화면
woojin._.
woojin._.
여러가지 개발을 해보며 발생하는 이야기들에 대한 블로그입니다:)
  • woojin._.
    Jin's Dev Story
    woojin._.
  • 전체
    오늘
    어제
    • 분류 전체보기 (829)
      • Tools (25)
        • eGovFrame (3)
        • GeoServer (3)
        • QGIS (2)
        • LabelImg (2)
        • Git (6)
        • GitHub (1)
        • Eclipse (7)
        • Visual Studio (1)
      • Web & Android (121)
        • SpringBoot (37)
        • Three.js (2)
        • Spring Data JPA (9)
        • 스프링 부트 쇼핑몰 프로젝트 with JPA (25)
        • Thymeleaf (4)
        • Spring Security (15)
        • Flutter (29)
      • Programming Language (61)
        • JAVA (27)
        • JavaScript (14)
        • Dart (2)
        • Python (15)
        • PHP (3)
      • Database (43)
        • PostgreSQL (32)
        • MYSQL (7)
        • Oracle (3)
        • MSSQL (1)
      • SERVER (17)
        • TCP_IP (3)
        • 리눅스 (7)
        • AWS (7)
      • Coding Test (445)
        • 백준[JAVA] (108)
        • 프로그래머스[JAVA] (260)
        • 알고리즘 고득점 Kit[JAVA] (3)
        • SQL 고득점 Kit[ORACLE] (74)
      • CS 지식 (49)
        • [자료구조] (14)
        • [네트워크] (12)
        • [데이터베이스] (10)
        • [알고리즘] (9)
        • [운영체제] (4)
      • 기타 (6)
      • 자격증 & 공부 (62)
        • 정보처리기사 (2)
        • SQLD (6)
        • 네트워크관리사 2급 (5)
        • 리눅스마스터 1급 (44)
        • 리눅스마스터 2급 (1)
        • ISTQB (3)
        • 시스템보안 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 인기 글

  • 태그

    리눅스마스터
    Oracle
    리눅스마스터 1급
    데이터베이스
    backjoon
    CS
    Linux
    programmers
    springboot
    리눅스
    spring
    백준
    python
    CS지식
    JPA
    시큐리티
    Flutter
    Spring Security
    DB
    pcce 기출문제
    baekjoon
    스프링
    자바
    postgresql
    플러터
    프로그래머스
    스프링 부트 쇼핑몰 프로젝트 with JPA
    데이터
    스프링부트
    Java
  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
woojin._.
[스프링 부트 쇼핑몰 프로젝트 with JPA] 11. 상품 주문
상단으로

티스토리툴바