@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
	}
}
<div th:replace="/fragments/cart_partial"></div>
	/**
	 * 제품의 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";
		
	}
<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에 상품 추가
@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>
<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>