4월 11일

SJY0000·2022년 4월 11일
0

Springboot

목록 보기
12/24

오늘 배운 것

  • Cart 페이지
  • 결제시스템

cart 페이지

모든 페이지에 띄우기

  • 모든 페이지에 cart정보를 띄우기위하여 ControllerAdvice가 적용된 클래스에 추가
  • cart에 담긴 상품의 갯수와 총 가격을 표시
@ControllerAdvice
public class Common {

	@Autowired
	private PageRepository pageRepo;
	
	@Autowired
	private CategoryRepository categoryRepo;
	
	@ModelAttribute
	public void sharedData(Model model, HttpSession session) {
		// cpages에 모든 페이지들을 순서대로 담아서 전달
		List<Page> cpages = pageRepo.findAllByOrderBySortingAsc();
		List<Category> categories = categoryRepo.findAll();
		// 현재 Cart 상태
		boolean cartActive = false; // Cart가 존재하지 않을 때 false
		
		if (session.getAttribute("cart") != null) {
			@SuppressWarnings("unchecked")
			HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>) session.getAttribute("cart");
			
			int size = 0; // Cart에 담긴 상품의 갯수
			int total = 0; // 총 가격
			
			for (Cart item : cart.values()) {// 장바구니 cart 객체들을 반복, cart.values()는 key값을 빼고 데이터만 반복
				size += item.getQuantity();
				total += item.getQuantity() * Integer.parseInt(item.getPrice()); // 상품 수량 * 가격 = 총 가격
			}
			model.addAttribute("csize", size);
			model.addAttribute("ctotal", total);
			cartActive = true;					// Cart가 존재함
		}
		
		model.addAttribute("cpages", cpages);
		model.addAttribute("ccategories", categories);
		model.addAttribute("cartActive", cartActive); // Cart가 존재하면 true, 없으면 false
	}
}
  • category들을 출력 하는 곳에 함꼐 출력할 수 있도록 함
<div th:replace="/fragments/cart_partial"></div>

각각의 상품들한테 버튼추가

  • 장바구니에 추가 버튼 클릭 시 HashMap<Integer, cart> cart객체에 경우에 따라 추가가 됨
  • session.getAttribute는 참조변수를 참조하기 때문에 불러온 값을 따로 set으로 저장하지 않아도 원본데이터를 자동으로 수정함
  • 추가를 한 상품의 가격과 수량을 추가하여 데이터 전송
	/**
	 * 제품의 id를 입력받아 Session에 CartList를 저장한다.
	 * @param id
	 * @param session
	 * @return
	 */
	@GetMapping("/add/{id}")
	public String add(@PathVariable("id") int id, HttpSession session, Model model, @RequestParam(required = false) String cartPage) {
		//0. 상품을 DB에서 검색
		Product product = productRepo.getById(id);
		
		//1. Session에 상품 저장 (Map<id,cart>로 그룹을 만든다. 
		if (session.getAttribute("cart") == null) { // Session에 Cart가 없으면
			HashMap<Integer, Cart> cart = new HashMap<>();
			cart.put(id, new Cart(id, product.getName(), product.getPrice(), 1, product.getImage()));
			session.setAttribute("cart", cart);		
		
		} else {//2. 이미 Cart가 있을경우 ((1) 그 상품이 이미 담겨져 있을 경우, (2) 없을 경우
			HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>) session.getAttribute("cart");
			
			if(cart.containsKey(id)) { // Cart에 그 상품이 있을 경우
				int qty = cart.get(id).getQuantity(); // 현재 Cart에 담긴 상품의 수량
				cart.put(id, new Cart(id, product.getName(), product.getPrice(), ++qty, product.getImage())); // session에서 참조변수를 불러오는 것이라 call by reference로 인행 원본데이터에도 영향을 줌
			} else {
			cart.put(id, new Cart(id, product.getName(), product.getPrice(), 1, product.getImage()));
			session.setAttribute("cart", cart); // 새로 생성하는 것이기 때문에 session에 저장해야할 필요가 있다.
			}
		}
		
		HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>) session.getAttribute("cart");
		
		int size = 0; // Cart에 담긴 상품의 갯수
		int total = 0; // 총 가격
		
		for (Cart item : cart.values()) {// 장바구니 cart 객체들을 반복, cart.values()는 key값을 빼고 데이터만 반복
			size += item.getQuantity();
			total += item.getQuantity() * Integer.parseInt(item.getPrice()); // 상품 수량 * 가격 = 총 가격
		}
		model.addAttribute("csize", size);
		model.addAttribute("ctotal", total);

		if(cartPage != null) { // cart.html 페이지에서 (+)버튼을 눌렀을 때는 다시 Cart페이지로 새로고침
			return "redirect:/cart/view";
		}
		
		return "cart_view";
		
	}
  • 버튼 클릭 시 AJAX 데이터를 전송하여 결과를 받음
  • 성공적으로 진행 시 옆의 cart현황에 결과를 덮어씌우고 1초동안 추가됐다는 표시를 띄움
<div style="position: relative">
  <p>
    <a class="btn btn-primary addToCart" th:attr="data-id=${product.id}" th:href="@{'/cart/add/' + ${product.id}}">장바구니추가</a>
  </p>
  <div class="btn btn-sm btn-success hide productAdded">추가됨!</div>
</div>

<script>
      // 장바구니추가버튼을 눌렀을 때 이벤트 추가
      $('a.addToCart').click(function (e) {
        e.preventDefault(); // 원래 a 태그의 기능인 주소이동을 멈춤
        let $this = $(this); // JQuery 변수, $변수명, 클릭한 장바구니추가버튼

        let id = $this.attr('data-id'); // 상품 id 값
        let url = '/cart/add/' + id;
        // Ajax get 메소드, 주소는 url, 보내는 데이터 없음 {}, 결과는 data
        $.get(url, {}, function (data) {
          $('div.cart').html(data); // Cart화면 덮어쓰기
        }).done(function () {
          $this.parent().parent().find('div.productAdded').fadeIn(); // parent()는 상위태그로 이동, find()는 자식태그들 중에서 찾음, 희마하게 보여주기
          setTimeout(function () {
            $this.parent().parent().find('div.productAdded').fadeOut(); // 1초뒤에 사라짐
          }, 1000);
        });
      });
    </script>
<p>장바구니에 <span th:text="${csize}"></span> 개 상품이 있습니다.</p>
<p>총 가격은 <span th:text="${ctotal}"></span> 원 입니다.</p>
<p>
  <a href="/cart/view" class="btn btn-success">카트 보기</a>
  <a href="/cart/clear" class="btn btn-danger float-right">비우기</a>
</p>
  • cart에 상품 추가 전

  • cart에 상품 추가

cart 페이지

  • cart페이지로 이동 시 session에 저장되어있는 cart객체를 불러와서 출력
  • 상품의 +,-,삭제 버튼 클릭 시 상품 수량 증가,감소와 cart에서 상품삭제 기능 추가
  • 비우기 버튼 클릭 시 cart객체 삭제
  • noCartView의 값을 true를 줘서 th:unless="{noCartView}" 참이되어 옆의 cart 데이터를 출력하지 않음
@GetMapping("/view")
	public String view(HttpSession session, Model model) {
		if (session.getAttribute("cart") == null) {
			return "redirect:/"; // Cart가 없을 경우 홈페이지로 이동
		}
		HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>) session.getAttribute("cart");
		
		model.addAttribute("cart", cart);
		model.addAttribute("noCartView", true); // 왼쪽 Cart_view는 필요없음
		
		return "cart";
	}
	
	@GetMapping("/subtract/{id}")
	public String subtract(@PathVariable("id") int id, HttpSession session, Model model, HttpServletRequest httpServletRequest) {
		HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>) session.getAttribute("cart");
		// 현재 cart에 담긴 상품의 갯수를 가져오기
		int qty = cart.get(id).getQuantity();
		if (qty == 1) { // 1에서 빼면 0 => cart에서 없어져야함
			cart.remove(id); // key값으로 삭제
			if(cart.size() == 0) { // cart에 상품이 담겨있지 않으면
				session.removeAttribute("cart"); // session에서 삭제
			}
		} else {
			cart.get(id).setQuantity(--qty);			
		}
		
		String referenLink = httpServletRequest.getHeader("Referer"); // 요청된 이전주소의 정보가 들어있음
		
		return "redirect:" + referenLink; // 다시 이전페이지로 이동
	}
	
	@GetMapping("/remove/{id}")
	public String remove(@PathVariable("id") int id, HttpSession session, Model model, HttpServletRequest httpServletRequest) {
		HashMap<Integer, Cart> cart = (HashMap<Integer, Cart>) session.getAttribute("cart");
		
		cart.remove(id); // id로 삭제
			
		if(cart.size() == 0) { // cart에 상품이 담겨있지 않으면
			session.removeAttribute("cart"); // session에서 삭제
		}

		String referenLink = httpServletRequest.getHeader("Referer"); // 요청된 이전주소의 정보가 들어있음
		
		return "redirect:" + referenLink; // 다시 이전페이지로 이동
	}
	
	@GetMapping("/clear")
	public String clear(HttpSession session, Model model, HttpServletRequest httpServletRequest) {
		session.removeAttribute("cart"); // session에서 삭제

		String referenLink = httpServletRequest.getHeader("Referer"); // 요청된 이전주소의 정보가 들어있음
		
		return "redirect:" + referenLink; // 다시 이전페이지로 이동
	}
  • 체크아웃 버튼 클릭 시 결제진행
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
  <head th:replace="/fragments/head :: head-front"></head>
  <body>
    <nav th:replace="/fragments/nav :: nav-front"></nav>
    <main role="main" class="container-fluid mt-5">
      <div class="row">
        <div th:replace="/fragments/categories :: categories"></div>
        <div class="col"></div>
        <div class="col-7">
          <h2 class="display-4">장바구니</h2>
          <table class="table">
            <tr>
              <th>상품</th>
              <th>이미지</th>
              <th>수량</th>
              <th>가격</th>
              <th>합계</th>
            </tr>
            <!-- item은 HashMap cart의 한개의 item-->
            <!-- 이 때 key값은 제품 id, value는 cart 객체이므로 item.value.name 제품이름-->
            <tr th:each="item : ${cart}">
              <td th:text="${item.value.name}"></td>
              <td>
                <img th:src="@{'/media/'+${item.value.image}}" style="height: 2em" />
              </td>
              <td>
                <span th:text="${item.value.quantity}"></span>
                <a th:href="@{'/cart/add/'+${item.value.id}} + '?cartPage=true'" class="btn btn-success btn-sm">+</a>
                <a th:href="@{'/cart/subtract/'+${item.value.id}}" class="btn btn-primary btn-sm">-</a>
                <a th:href="@{'/cart/remove/'+${item.value.id}}" class="btn btn-danger btn-sm">삭제</a>
              </td>
              <td th:text="${item.value.price} + ' 원'"></td>
              <td th:with="totalPrice = ${item.value.price}*${item.value.quantity}" th:text="${totalPrice} + ' 원'"></td>
            </tr>
            <tr>
              <th colspan="5" class="text-right pr-5" th:text="'총 합계 : ' + ${ctotal} + ' 원'"></th>
            </tr>
            <tr>
              <td>
                <a href="/cart/clear" class="btn btn-danger">비우기</a>
              </td>
              <td colspan="4" class="text-right">
                <a href="#" class="btn btn-success checkout">체크아웃</a>
              </td>
            </tr>
          </table>
        </div>
        <div class="col"></div>
      </div>
    </main>
    <footer th:replace="/fragments/footer :: footer"></footer>


결제시스템

i'port 결제시스템 이용

https://www.iamport.kr/

  • 로그인 후 관리자 콘솔 클릭 한 번 더 로그인 후
  • 내 정보에서 가맹점 식별코드 복사 후 js에 붙여넣기
  • 테스트 할 결제 시스템 종류 정하고 테스트 모드 설정, 사이트코드를 가맹점코드에 넣기

Cart 페이지에 JS추가

  • cart페이지에 cdn과 js를 추가
    [[${ctotal}]] vs에서 에러표시가 뜨지만 제대로 작동을 했다가 안했다가 하는데 이유가 뭐지....)
    +++ [[${ctotal}]]가 있는 부분에 script 태그를 따로 주고 th:inline="javascript" 설정을 하니 작동은 잘되지만 오류 표시는 계속 있다 $ 랑 { 사이에 ,를 주라고 뜨지만 그러면 작동을 안함 이유가 뭐지........
    ++++++ 12일 어제는 script 안에 th:inline 값을 줘야 작동했는데 오늘은 빼고 그냥 해봤더니 제대로 작동한다. 뭔데 도대체.... 업데이트 하거나 이런것도 없고 컴퓨터 끄거나 한적도 없고 프로그램도 켜놓았던 상탠데 하루 지났다고 안되던게 된다고????? 업데이트도 없었는데
    인터넷을 아무리 뒤져봐도 오류 왜 뜨는지도 모르겠고 영상을 봐도 영상에도 똑같이 오류표시줄이 있다 그냥 냅둬야하나 찝찝한데
<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.2.0.js"></script>
    <script>
      $(function () {
        $('a.checkout').click(function (e) {
          e.preventDefault();
          $.get('/cart/clear', {}, function () {}); // ajax로 cart clear 요청
          //$("form#paypalform").submit();
          kakaoPay();
        });
      });
    </script>
    <script th:inline="javascript">
      function kakaoPay() {
        var IMP = window.IMP; // 생략 가능
        IMP.init('imp97492501'); // 예: imp00000000
        IMP.request_pay(
          {
            pg: 'kakaopay',
            //pay_method : 'card', //생략 가능
            merchant_uid: 'order_no_0015', // 상점에서 관리하는 주문 번호(중복안되어야 됨)
            name: '주문명:결제테스트',
            amount: [[${ctotal}]],
            buyer_email: 'iamport@siot.do',
            buyer_name: '구매자이름',
            buyer_tel: '010-1234-5678',
            buyer_addr: '서울특별시 강남구 삼성동',
            buyer_postcode: '123-456',
          },
          function (rsp) {
            if (rsp.success) {
              let msg = '결제가 완료되었습니다.';
              msg += '고유ID : ' + rsp.imp_uid;
              msg += '상점 거래ID : ' + rsp.merchant_uid;
              msg += '결제 금액 : ' + rsp.paid_amount;
              msg += '카드 승인번호 : ' + rsp.apply_num;
              if (!alert(msg)) location.reload();
            } else {
              let msg = '결제에 실패하였습니다.';
              msg += '에러내용 : ' + rsp.error_msg;
              alert(msg);
            }
          }
        );
      }
    </script>

  • 결제결과에 따라 결과가 i'port 관리자 페이지에 출력됨

0개의 댓글