이 내용은 스프링 부트 쇼핑몰 프로젝트 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 |