[Spring][쇼핑몰 프로젝트][42] 주문 구현 - 1
프로젝트 Github : https://github.com/sjinjin7/Blog_Project
프로젝트 포스팅 색인(index) : https://kimvampa.tistory.com/188
목표
주문 구현 - 서버 구현
이번 포스팅부터 회원이 '주문 페이지'에서 "결제하기" 버튼을 클릭했을 때 서버에서 처리할 작업을 구현하겠습니다. 이번 포스팅에선 회원이 요청한 주문 데이터를 서버로 넘기는 것을 목표로 합니다.
순서
1. 개요
2. DTO 생성
3. order.jsp
4. OrderController
5. 테스트
1. 개요
서버로 넘겨줄 데이터는 3가지입니다.
하나, 배송지 정보
둘, 사용 포인트 정보
셋, 사용자 주문 상품 정보
뷰에서는 사용자가 '결제하기' 버튼을 클릭했을 때 위의 정보가 전송되도록 구현해야 합니다.
따라서 뷰에서 해줄 것은 서버에 "주문 전송"을 요청하는 <form> 태그 작성하고, "결제하기"버튼을 클릭했을 때 <form> 태그의 <input> 태그 값을 세팅 후 서버에 form 데이터를 전송하도록 자바스크립트 코드를 추가해 줄 것입니다.
서버에선 뷰의 요청을 받아들이고 뷰에서 전달받은 데이터를 활용하여 주문 작업을 수행해야 합니다.
이번 포스팅에선 뷰의 경우 서버에 "주문" 요청과 데이터 전송을 하도록 작업하고, 서버에서는 뷰의 요청을 수행하는 URL 매핑 메서드를 작성하고, 뷰에서 전송하는 데이터를 담을 수 있는 DTO 클래스를 생성할 것입니다.
2. DTO 생성
뷰에서 전송하는 '주문 정보' 데이터를 담을 DTO 클래스를 만들겠습니다.
OrderDTO, OrderItemDTO 이름으로 두 개의 DTO클래스를 생성할 것입니다. DTO를 구성할 방식은 "OrderPageDTO", "OrderPageItemDTO"와 비슷한 구성으로 만들 것입니다.
OrderItemDTO는 "하나"의 '주문 상품 정보'를 담습니다.
OrderDTO는 '배송지 정보', '사용 포인트 정보', "여러 개"의 '주문 상품 정보'를 담습니다. (여러 개의 주문 상품 정보를 담는 방식은 OrderIteDTO를 요소를 가지는 List 객체를 통해 '여러 주문 상품 정보'를 가지게 됩니다.)
서버로 데이터를 전달 역할만 수행을 한다면 기존의 "OrderPageDTO ", "OrderPagteItemDTO" 사용해도 무방합니다. 하지만 새로 만들 DTO클래스는("OrderDTO", "OrderItemDTO")는 뷰가 전송하는 정보를 담을 뿐만 아니라 두 가지의 정보를 더 담도록 만들 것이기 때문에 굳이 새로운 클래스를 작성해 줄 것입니다.
하나는 DB로부터 저장된 정보(상품 할인율, 상품 가격)이고, 다른 하나는 주문 작업에 필요로 한 정보(배송비, 최종 비용(상품 비용 + 배송비 - 사용 포인트) 등)입니다.
'주문 작업에 필요로 한 정보'인 경우 'DB로부터 저장된 정보'를 활용하여 DTO 객체 안에서 만들어 낼 수 있도록 메서드도 만들어 줄 것입니다. 물론 '주문 작업에 필요로 한 정보'를 DTO 객체에 만들어 주지 않고 Service 메서드에서 만들어내서 작업을 해도 되지만, Service 단계에서는 자잘한 처리보다는"주문"을 처리하는 핵심 로직에 집중할 수 있도록 하기 위해서 DTO 객체에서 만들어 내도록 할 것입니다.
정리하면 새로 생성할 DTO 클래스는 뷰에서 서버로 전송할 정보, DB에 꺼내올 정보, DB에 등록할 정보, 주문 비즈니스 로직 처리에 필요로 한 정보 담는 역할을 합니다.
OrderItemDTO
com.vam.model 패키지에 OrderItemDTO 클래스를 생성합니다.
생성한 클래스에 아래의 변수를 추가해줍니다.
/* 주문 번호 */
private String orderId;
/* 상품 번호 */
private int bookId;
/* 주문 수량 */
private int bookCount;
/* vam_orderItem 기본키 */
private int orderItemId;
/* 상품 한 개 가격 */
private int bookPrice;
/* 상품 할인 율 */
private double bookDiscount;
/* 상품 한개 구매 시 획득 포인트 */
private int savePoint;
/* DB테이블 존재 하지 않는 데이터 */
/* 할인 적용된 가격 */
private int salePrice;
/* 총 가격(할인 적용된 가격 * 주문 수량) */
private int totalPrice;
/* 총 획득 포인트(상품 한개 구매 시 획득 포인트 * 수량) */
private int totalSavePoint;
getter/setter, toString 메서드를 추가합니다.
주문 작업에 필요로 한 데이터를 세팅해주는 메서드를 추가해줍니다.
public void initSaleTotal() {
this.salePrice = (int) (this.bookPrice * (1-this.bookDiscount));
this.totalPrice = this.salePrice*this.bookCount;
this.savePoint = (int)(Math.floor(this.salePrice*0.05));
this.totalSavePoint =this.savePoint * this.bookCount;
}
OrderDTO
com.vam.model 패키지에 OrderDTO 클래스를 생성합니다.
생성한 클래스에 아래의 변수를 추가해줍니다.
/* 주문 번호 */
private String orderId;
/* 배송 받는이 */
private String addressee;
/* 주문 회원 아이디 */
private String memberId;
/* 우편번호 */
private String memberAddr1;
/* 회원 주소 */
private String memberAddr2;
/* 회원 상세주소 */
private String memberAddr3;
/* 주문 상태 */
private String orderState;
/* 주문 상품 */
private List<OrderItemDTO> orders;
/* 배송비 */
private int deliveryCost;
/* 사용 포인트 */
private int usePoint;
/* 주문 날짜 */
private Date orderDate;
/* DB테이블 존재 하지 않는 데이터 */
/* 판매가(모든 상품 비용) */
private int orderSalePrice;
/* 적립 포인트 */
private int orderSavePoint;
/* 최종 판매 비용 */
private int orderFinalSalePrice;
getter/setter, toString 메서드를 추가합니다.
주문 작업에 필요로 한 데이터를 세팅해주는 메서드를 추가해줍니다.
public void getOrderPriceInfo() {
/* 상품 비용 & 적립포인트 */
for(OrderItemDTO order : orders) {
orderSalePrice += order.getTotalPrice();
orderSavePoint += order.getTotalSavePoint();
}
/* 배송비용 */
if(orderSalePrice >= 30000) {
deliveryCost = 0;
} else {
deliveryCost = 3000;
}
/* 최종 비용(상품 비용 + 배송비 - 사용 포인트) */
orderFinalSalePrice = orderSalePrice + deliveryCost - usePoint;
}
3. order.jsp
'기존 배송지> <input> 태그 추가
추후 Js코드를 통해 사용자가 '사용자 정보 주소록' 선택했을 때 가져올 데이터가 저장된 hidden 타입의 <input> 태그를 추가해줍니다. "addressInfo_input_div_1"<div> 태그 내부에 있는 세 번째 <td> 태그에 아래의 코드를 추가합니다.
<input class="addressee_input" value="${memberInfo.memberName}" type="hidden">
<input class="address1_input" type="hidden" value="${memberInfo.memberAddr1}">
<input class="address2_input" type="hidden" value="${memberInfo.memberAddr2}">
<input class="address3_input" type="hidden" value="${memberInfo.memberAddr3}">
배송지 정보 판단 근거
'주문 요청' form 태그를 추가하기 앞서서 '배송지 섹션' 부분에 추가 적인 작업을 해주겠습니다. 지금 회원은 자신이 원하는 대로 '기존 배송지 정보'를 사용하거나 '직접 입력 배송지'를 사용할 수 있도록 화면은 구성되어 있습니다. 하지만 우리가 서버에 데이터를 전송하기 위해서 배송지 정보를 가져올 때 회원이 어떠한 '배송지 정보'를 선택했는지 판단할 수 있는 근거 가 없습니다. 따라서 회원은 어떠한 '배송지 정보'를 선택했는지를 판단할 수 있는 근거를 만들어 주겠습니다.
배송지 정보 입력란("addressInfo_input_di" <div> 태그)에 있는 배송지 <input> 태그가 있는 <td> 태그에 아래의 코드를 추가해줍니다. 단 '사용자 정보 주소록'에는 값이 T, '직접 입력' 에는 F를 값으로 같도록 합니다.
<input class="selectAddress" value="T" type="hidden">
<input class="selectAddress" value="F" type="hidden">
회원이 선택한 방식은 값이 "T"를 가지도록 할 것입니다. 따라서 '배송지 정보' 선택 버튼을 클릭했을 때 해당 값들이 변경되도록 해주어야 합니다. '배송지 정보' 버튼 동작 메서드에 아래의 코드를 추가해줍니다.
/* selectAddress T/F */
/* 모든 selectAddress F만들기 */
$(".addressInfo_input_div").each(function(i, obj){
$(obj).find(".selectAddress").val("F");
});
/* 선택한 selectAdress T만들기 */
$(".addressInfo_input_div_" + className).find(".selectAddress").val("T");
form 태그 추가
<body> 태그 내부 아무 곳에 아래의 <form> 태그를 추가합니다. '상품 정보' <input> 태그의 경우 동적으로 추가해 줄 것입니다.
<!-- 주문 요청 form -->
<form class="order_form" action="/order" method="post">
<!-- 주문자 회원번호 -->
<input name="memberId" value="${memberInfo.memberId}" type="hidden">
<!-- 주소록 & 받는이-->
<input name="addressee" type="hidden">
<input name="memberAddr1" type="hidden">
<input name="memberAddr2" type="hidden">
<input name="memberAddr3" type="hidden">
<!-- 사용 포인트 -->
<input name="usePoint" type="hidden">
<!-- 상품 정보 -->
</form>
'결제하기' 버튼 JS 코드
'결제하기' 버튼을 누르게 되면 주문 처리 정보들을 <from> 태그 내에 <input> 태그에 저장을 하고 서버에 "주문"처리를 요청하면서 form에 있는 데이터를 서버에 전송시켜 주어야 합니다.
'결제하기' 버튼을 클릭했을 때 동작하는 메서드를 추가합니다.
/* 주문 요청 */
$(".order_btn").on("click", function(){
});
배송지 정보가 form 태그 <input>에 저장되도록 하는 코드를 추가합니다.
/* 주문 요청 */
$(".order_btn").on("click", function(){
/* 주소 정보 & 받는이*/
$(".addressInfo_input_div").each(function(i, obj){
if($(obj).find(".selectAddress").val() === 'T'){
$("input[name='addressee']").val($(obj).find(".addressee_input").val());
$("input[name='memberAddr1']").val($(obj).find(".address1_input").val());
$("input[name='memberAddr2']").val($(obj).find(".address2_input").val());
$("input[name='memberAddr3']").val($(obj).find(".address3_input").val());
}
});
});
회원이 입력한 '사용 포인트'를 <input> 태그에 저장하는 코드를 추가 합니다.
/* 주문 요청 */
$(".order_btn").on("click", function(){
/* 주소 정보 & 받는이*/
$(".addressInfo_input_div").each(function(i, obj){
if($(obj).find(".selectAddress").val() === 'T'){
$("input[name='addressee']").val($(obj).find(".addressee_input").val());
$("input[name='memberAddr1']").val($(obj).find(".address1_input").val());
$("input[name='memberAddr2']").val($(obj).find(".address2_input").val());
$("input[name='memberAddr3']").val($(obj).find(".address3_input").val());
}
});
/* 사용 포인트 */
$("input[name='usePoint']").val($(".order_point_input").val());
});
상품 정보 <input> 태그를 동적으로 생성하도록 하는 코드를 추가해줍니다.
/* 주문 요청 */
$(".order_btn").on("click", function(){
/* 주소 정보 & 받는이*/
$(".addressInfo_input_div").each(function(i, obj){
if($(obj).find(".selectAddress").val() === 'T'){
$("input[name='addressee']").val($(obj).find(".addressee_input").val());
$("input[name='memberAddr1']").val($(obj).find(".address1_input").val());
$("input[name='memberAddr2']").val($(obj).find(".address2_input").val());
$("input[name='memberAddr3']").val($(obj).find(".address3_input").val());
}
});
/* 사용 포인트 */
$("input[name='usePoint']").val($(".order_point_input").val());
/* 상품정보 */
let form_contents = '';
$(".goods_table_price_td").each(function(index, element){
let bookId = $(element).find(".individual_bookId_input").val();
let bookCount = $(element).find(".individual_bookCount_input").val();
let bookId_input = "<input name='orders[" + index + "].bookId' type='hidden' value='" + bookId + "'>";
form_contents += bookId_input;
let bookCount_input = "<input name='orders[" + index + "].bookCount' type='hidden' value='" + bookCount + "'>";
form_contents += bookCount_input;
});
$(".order_form").append(form_contents);
});
마지막으로 form을 서버로 전송하는 코드를 추가합니다.
/* 주문 요청 */
$(".order_btn").on("click", function(){
/* 주소 정보 & 받는이*/
$(".addressInfo_input_div").each(function(i, obj){
if($(obj).find(".selectAddress").val() === 'T'){
$("input[name='addressee']").val($(obj).find(".addressee_input").val());
$("input[name='memberAddr1']").val($(obj).find(".address1_input").val());
$("input[name='memberAddr2']").val($(obj).find(".address2_input").val());
$("input[name='memberAddr3']").val($(obj).find(".address3_input").val());
}
});
/* 사용 포인트 */
$("input[name='usePoint']").val($(".order_point_input").val());
/* 상품정보 */
let form_contents = '';
$(".goods_table_price_td").each(function(index, element){
let bookId = $(element).find(".individual_bookId_input").val();
let bookCount = $(element).find(".individual_bookCount_input").val();
let bookId_input = "<input name='orders[" + index + "].bookId' type='hidden' value='" + bookId + "'>";
form_contents += bookId_input;
let bookCount_input = "<input name='orders[" + index + "].bookCount' type='hidden' value='" + bookCount + "'>";
form_contents += bookCount_input;
});
$(".order_form").append(form_contents);
/* 서버 전송 */
$(".order_form").submit();
});
4. OrderController
뷰에서 요청하는 "주문"을 처리하는 URL 매핑 메서드를 OrderContorller 클래스에 작성해줍니다.
@PostMapping("/order")
public String orderPagePost(OrderDTO od, HttpServletRequest request) {
System.out.println(od);
return "redirect:/main";
}
리턴 타입은 "주문" 작업 후 '메인 페이지'로 이동하도록 String 타입으로 지정하였습니다. 그리고 뷰에서 전송한 정보들을 전달받을 수 있도록 "OrderDTO" 타입의 파라미터 지정해 주었습니다.
정보를 정상적으로 전달받았는지 확인하기 위해서 println() 메서드를 작성해주었습니다.
5. 테스트
서버를 구동시켜서 데이터가 정상적으로 넘어오는지 확인합니다.
REFERENCE
DATE
- 2020.12.17