이번 프로젝트에서는 꽤나 많은 부분을 구현했다,
쇼핑몰의 공지사항이나 F&Q 등 문의사항을 올릴 수 있는 게시판과 리뷰게시판,
회원기능을 위한 로그인, 회원가입에 관련 된 기능들,
관리자 페이지 등..
취업상담사분들이 감사하게도,
실제 서비스해도 될 만큼 완성도가 높다고 평가해주셨는데, ( 정말..? )
사실 많은 부분들이 이전에 했던 Icewater 커뮤니티 프로젝트와 겹친다.
그래서 그런 부분들, 게시판과 회원에 관한 부분들은 따로 포스팅 하지 않을 계획이다.
주문 페이지는 3개의 구성으로 되어있다.
장바구니에 담은 상품 및 수량이 표시되는 주문상품목록 ,
주문자와 수취인을 적는 주문정보 ,
총액과 할인쿠폰, 적립금과 배송비를 계산해주는 결제 부분이다.
주문정보쪽엔 주소API를 사용해 우편번호와 주소를 불러올 수 있도록 했고,
결제부분에는 import 결제 연동 API로 결제가 가능하도록 구현했다.
카카오페이, 네이버페이, 신용카드 결제가 가능하며,
결제를 하면 실제로 계좌에서 돈이 빠져나가지만 테스트모드이기 때문에
일정시간 후에 자동으로 결제취소 및 환불이 된다.
Controller ( 장바구니 페이지 -> 주문페이지 )
@RequestMapping(value = "/orderView",method=RequestMethod.POST)
public String orderView(HttpSession session, HttpServletRequest request, HttpServletResponse response,
OrderItemVO orderItemVO, Model model,int totalPrice,int totalVol) throws Exception{
List<OrderItemVO> orderList = new ArrayList<>();
for(int i=0; i < (orderItemVO.getItem_no()).length; i++) {
OrderItemVO VO = new OrderItemVO();
VO.setOrder_item_img(orderItemVO.getItem_img()[i]);
VO.setOrder_item_name(orderItemVO.getItem_name()[i]);
VO.setOrder_item_option(orderItemVO.getItem_option()[i]);
VO.setOrder_item_price(orderItemVO.getItem_price()[i]);
VO.setOrder_option_no(orderItemVO.getOption_no()[i]);
VO.setOrder_item_no(orderItemVO.getItem_no()[i]);
VO.setOrder_select_vol(orderItemVO.getSelect_vol()[i]);
orderList.add(VO);
}
model.addAttribute("order", orderList);
model.addAttribute("totalPrice", totalPrice);
model.addAttribute("totalVol", totalVol);
if(session.getAttribute("member") != null) {
MemberVO memberVO = (MemberVO) session.getAttribute("member");
model.addAttribute("coupon", mainService.orderCoupon(memberVO.getMEM_NO()));
}
return "/main/orderView";
}
먼저, 이번 쇼핑몰 프로젝트의 사용자 로직의 흐름은
회원가입 - 로그인 - 상품 장바구니에 추가 - 장바구니 - 주문 - 주문확인 이다.
때문에, 상품을 직접 주문하는 것이 아닌,
일단 장바구니에 담은 후 장바구니 페이지로 이동해서 수량을 설정하고 주문하는 것이다.
때문에 , 주문페이지의 주문상품들은 따로 select문을 돌리지 않고,
장바구니페이지에 담긴 상품들의 정보, 옵션, 수량을 그대로 가져오도록 했다.
장바구니 목록을 select문을 이용해 List 형태로 출력하는데,
거기에 input 태그를 hidden으로 추가했다.
주문버튼을 누르면 input태그를 감싼 form이 submit되어,
해당 값을 이용해서 주문페이지의 주문상품목록을 구성했다.
장바구니 목록은 다수이기 때문에, 컨트롤러에서 배열로 받아서 for문으로 처리해줬다.
사실 장바구니 페이지의 상품PR과 옵션PR로 join을 해서 select문을 이용해 List를 출력할 수도 있겠다.
하지만 그냥 이런게 간단히 장바구니의 파라미터를 주문페이지로 넘겨도 로직에 이상이 없겠다는 생각이 들어서
따로 쿼리를 쓰지 않는 방향으로 구현했다.
조금 더 복잡하고 많은 데이터를 가져와야 할때는 이런식으로 장바구니를 통째로 주문페이지로 넘겨서 처리하는
방법은 맞지 않겠지만, 소규모의 실서비스용이 아닌, 테스트, 구현하는 것에 목적을 둔 이번 프로젝트에서는
이 방법을 사용해보고 싶었고, 구현속도가 이쪽이 더 빠를 것 같아서 선택했다.
select join은 너무 많이 사용해서 다른 방법이 사용하고 싶기도 했다..
장바구니 페이지의 총액과 총수량도 가져와주고,
회원의 쿠폰을 사용 할 수 있도록 회원의 쿠폰리스트도 가져와줬다.
<!-- 쿠폰목록 -->
<select id="orderCoupon" resultType="kr.co.vo.CouponVO">
<![CDATA[
SELECT * FROM COUPON WHERE TO_CHAR(CPN_EDATE ,'YYMMDD') > TO_CHAR(SYSDATE,'YYMMDD') AND CPN_MEM_NO = #{mem_no}
]]>
</select>
아직 포스팅하지 않은 관리자 페이지에서 쿠폰을 생성, 발급할 수 있는데,
유효기간이 지나지 않은 쿠폰을 회원PR을 기준으로 가져온다.
View
$(document).ready(function() {
$("#fromBtn").on("click", function(){
var name = document.getElementById('order_to_name').value;
var tel = document.getElementById('order_to_tel').value;
var email = document.getElementById('order_to_email').value;
var post = document.getElementById('order_to_post').value;
var adr = document.getElementById('order_to_adr').value;
$("#order_from_name").val(name);
$("#order_from_tel").val(tel);
$("#order_from_email").val(email);
$("#order_from_post").val(post);
$("#order_from_adr").val(adr);
});
total();
});
주문목록은 위에서 설명한 장바구니에서 넘어온 값을 이용해서 forEach를 이용해 목록을 만들었고,
그 다음은 주문정보 부분이다.
id값이 order_to에 해당하는 부분은 구매자 이고, order_from은 수취인이다.
구매자 부분의 input창에는 회원가입 시 입력하는 세션의 값을 이용해서 자동입력이 되도록 했고,
위의 scrpit문은 수취인 부분의 위와 동일 부분의 함수이다.
구매자 부분의 값을 document.getElementById() 를 이용해서 가져와서
수취인 부분으로 입력해주는 onclick 이벤트이다.
이제 결제 부분이다.
function total() {
console.log("total실행됨");
var coupon = document.getElementById("order_coupon");
var point = document.getElementById("order_point");
var totalPrice = ${totalPrice};
var order_coupon = coupon.options[coupon.selectedIndex].value;
var order_coupon_name = coupon.options[coupon.selectedIndex].text;
var order_point = point.value;
var mem_point = ${member.MEM_POINT};
var payment = 0;
if(parseInt(mem_point) < parseInt(order_point)){
$("#order_point").val(0);
return false;
}
var discount = (parseInt(totalPrice) * (parseInt(order_coupon) * 0.01))
payment = parseInt(totalPrice) - discount;
payment = payment - parseInt(order_point) +2500;
var sum = new Intl.NumberFormat('ko-KR', {
style : 'currency',
currency : 'KRW'
}).format(payment);
var discountSum = new Intl.NumberFormat('ko-KR', {
style : 'currency',
currency : 'KRW'
}).format(discount);
$("#payment").html(sum);
$("#discount").html(discountSum);
$("#order_cpn_name").val(order_coupon_name);
$("#ordered_cpn_disc").val(parseInt(discount));
$("#ordered_orderprice").val(parseInt(payment));
}
function orderBtn(){
var email_rule = /^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i;
var tel_rule = /^\d{2,3}\d{3,4}\d{4}$/;
if ($("#order_to_name").val() == null || $("#order_to_name").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("구매자 명을 입력해주세요");
$("#order_to_name").focus();
return false;
}
if ($("#order_to_tel").val() == null || $("#order_to_tel").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("구매자번호를 입력해주세요.");
$("#order_to_tel").focus();
return false;
}
if ($("#order_to_email").val() == null || $("#order_to_email").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("구매자이메일을 입력해주세요.");
$("#order_to_email").focus();
return false;
}
if ($("#order_to_adr").val() == null || $("#order_to_adr").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("구매자주소를 입력해주세요.");
$("#order_to_adr").focus();
return false;
}
if ($("#order_from_name").val() == null || $("#order_from_name").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("수취인명을 입력해주세요.");
$("#order_from_name").focus();
return false;
}
if ($("#order_from_tel").val() == null || $("#order_from_tel").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("수취인번호를 입력해주세요.");
$("#order_from_tel").focus();
return false;
}
if ($("#order_from_adr").val() == null || $("#order_from_adr").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("수취인 주소를 입력해주세요.");
$("#order_from_adr").focus();
return false;
}
if ($("#order_from_post").val() == null || $("#order_from_post").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("수취인 우편번호를 입력해주세요.");
$("#order_from_post").focus();
return false;
}
if ($("#order_to_post").val() == null || $("#order_to_post").val() == "") {
toastr.options.preventDuplicates = true;
toastr.warning("구매자 우편번호를 입력해주세요.");
$("#order_to_post").focus();
return false;
}
iamport();
}
function iamport(){
var name = document.getElementById('order_to_name').value;
var tel = document.getElementById('order_to_tel').value;
var email = document.getElementById('order_to_email').value;
var post = document.getElementById('order_to_post').value;
var adr = document.getElementById('order_to_adr').value;
console.log("totalPay11=" + payment);
IMP.init('----');
IMP.request_pay({
pg : 'html5_inicis',
pay_method : 'card',
merchant_uid : new Date().getTime() ,
name : 'Homme 결제' ,
amount : parseInt(payment),
buyer_email : email,
buyer_name : name,
buyer_tel : tel,
buyer_addr : adr,
buyer_postcode : post
}, function(rsp) {
console.log(rsp);
if ( rsp.success ) {
var msg = '결제가 완료되었습니다.';
msg += '고유ID : ' + rsp.imp_uid;
msg += '상점 거래ID : ' + rsp.merchant_uid;
msg += '결제 금액 : ' + rsp.paid_amount;
msg += '카드 승인번호 : ' + rsp.apply_num;
$("#merchant_uid").val(parseInt(rsp.merchant_uid));
orderInsert();
} else {
var msg = '결제에 실패하였습니다.';
msg += '//' + rsp.error_msg;
}
alert(msg);
});
}
function orderInsert(){
var formObj = $("form[name='orderForm']");
formObj.attr("action", "/main/orderInsert");
formObj.attr("method", "post");
formObj.submit();
}
</script>
결제 부분은 총 4개의 script 함수가 있다.
- 결제금액을 계산해주는 total()
- 입력칸 유효성검사 orderBtn()
- 결제api iamport()
- submit을 보낼 orderInsert() 이다.
순서대로 설명하면 total()은
먼저 쿠폰 부분의 값과 회원의 적립금을 입력하는 부분의 값을 getElementById를 이용해 가져온다.
쿠폰은 selectOption으로 구성되있어서, 한가지를 선택할 수 있는데
options 함수를 이용해서 쿠폰의 이름(text)와 할인율(value)를 가져온다.
세션에 저장되어있는 회원의 적립금도 가져와준다.
discount 변수는 총액 * (쿠폰의 할인율 * 1/100) 해준다.
예를 들어 쿠폰의 할인율이 15프로라면 총액*0.15이 되는 것이다.
이제 총액의 15프로의 금액이 discount에 담기고 그 값을 총액에서 빼준다.
변수 payment에 총액 - discount 를 해주고,
입력한 적립금의 값을 다시 - 해주고,
배송비 2500원을 더해준다.
그 값을 원하는 위치의 id값을 가진 태그로 보내준다.
이제 총액과 할인율에 맞는 결제액이 나왔다.
orderBtn()은 결제버튼을 눌렀을 때,
빠진 곳이 있는지 유효성 검사를 하는 함수이다.
유효성검사를 통과하면, iamport() 함수를 호출한다.
iamport() 결제를 사용하는 부분은 생략하겠다.
결제가 성공하여 success가 나왓을때,
orderInsert() 함수를 호출한다.
이제 주문서 form에 입력된 값을 insert하여, 주문을 insert 시키면 된다.
주문을 insert 시키는 부분은 다음 포스팅에 작성하겠다.
주문이 가장 어려운 부분은 아니였지만,
가장 손이 많이 가고 많은 파라미터를 교환해야하는 부분이였기 때문에 꽤나 길어졌다.
'SPRING > Homme Shop' 카테고리의 다른 글
[스프링] 쇼핑몰 - 주문목록 (0) | 2021.12.09 |
---|---|
[스프링] 쇼핑몰 - 상품 주문 [ 2 ] (0) | 2021.12.09 |
[스프링] 쇼핑몰 - 비동기 장바구니 [3] (0) | 2021.12.08 |
[스프링] 쇼핑몰 - 비동기 장바구니 [2] (1) | 2021.12.08 |
[스프링] 쇼핑몰 - 비동기 장바구니 [1] (6) | 2021.12.08 |