설계
Entity | DAO | DTO | Controller | Service | mapper | View |
---|---|---|---|---|---|---|
CartItem | CartDao | CartDto CartOrderDto CartDeleteDto | CartController OrderController | CartService OrderService | cartMapper | product/read.html cart/list.html |
- 장바구니 읽기 (/cart/list)
- 장바구니에 담기 (/cart/add)
- 장바구니 물품 개수 증가 (/cart/increse)
- 장바구니 물품 개수 감소 (/cart/decrese)
- 장바구니에서 물품 삭제 (/cart/delete)
- 장바구니에서 주문 (/order/multiple)
CartItem
@Data
@AllArgsConstructor
public class CartItem {
private String username;
private Integer pno;
private Integer count;
}
src/main/java - com.example.demo.dao - CartDao
@Mapper
public interface CartDao {
// 장바구니에 저장
void save(CartItem cartItem);
// 해당 사용자의 장바구니 찾기
List<CartItem> findByUsername(String username);
// 장바구니 내의 상품 찾기
CartItem findByUsernameAndPno(String username, Integer pno);
// 장바구니 상품 개수 증가
void increase(CartItem cartItem);
// 장바구니 상품 개수 감소
void decrease(CartItem cartItem);
// 장바구니에서 삭제
void delete(List<Integer> pnos, String username);
}
src/main/java - com.example.demo.dto - CartDto
@Data
@AllArgsConstructor
public class CartDto {
private Integer pno;
private String vendor;
private String name;
private String imagename;
private Integer price;
private Integer count;
|
장바구니에서 출력할 정보만 담은 DTO.
src/main/java - com.example.demo.dto - CartOrderDto
@Data
public class CartOrderDto {
@Data
public static class Param {
private Integer pno;
private Integer count;
}
private List<Param> list;
}
주문할 때 필요한 정보만 담은 DTO.
src/main/java - com.example.demo.dto - CartDeleteDto
@Data
public class CartDeleteDto {
private List<Integer> pnos;
}
삭제할 때 필요한 정보만 담은 DTO.
src/main/java - com.example.demo.controller.mvc - CartController
@Secured("ROLE_USER")
@AllArgsConstructor
@Controller
public class CartController {
private CartService service;
// 장바구니 보기
@GetMapping("/cart/list")
public ModelAndView read(Principal principal) {
List<CartDto> list = service.read(principal.getName());
return new ModelAndView("cart/list").addObject("list", list);
}
// 장바구니 담기 - 이미 담긴 상품일 경우 개수를 증가시킨다.
@PostMapping("/cart/add")
public String add(Integer pno, Integer count, Principal principal) {
Boolean result = service.add(pno, count, principal.getName());
if (result == false)
return "redirect:/product/read?pno=" + pno;
return "redirect:/cart/list";
}
// 장바구니 물품 개수 증가
@PatchMapping("/cart/increase/{pno}")
public ResponseEntity<Integer> increase(@PathVariable Integer pno, Principal principal) {
Integer count = service.increase(pno, principal.getName());
if (count <= 0)
return ResponseEntity.status(HttpStatus.CONFLICT).body(count);
return ResponseEntity.ok(count);
}
// 장바구니 물품 개수 감소
@PatchMapping("/cart/decrease/{pno}")
public ResponseEntity<Integer> decrease(@PathVariable Integer pno, Principal principal) {
Integer count = service.decrease(pno, principal.getName());
if (count <= 0)
return ResponseEntity.status(HttpStatus.CONFLICT).body(count);
return ResponseEntity.ok(count);
}
// 장바구니에서 삭제
@PostMapping("/cart/delete")
public String delete(CartDeleteDto dto, Principal principal) {
service.delete(dto, principal.getName());
return "redirect:/cart/list";
}
}
src/main/java - com.example.controller.mvc - OrderController
@Secured("ROLE_USER")
@AllArgsConstructor
@Controller
public class OrderController {
private OrderService service;
// 여러 개 주문 시의 주문 정보 (장바구니)
@PostMapping("/order/multiple")
public ModelAndView order(CartOrderDto cartOrderDto, Principal principal, HttpSession session) {
OrderDto.View dto = service.orderList(cartOrderDto.getList(), principal.getName());
session.setAttribute("dto", dto);
return new ModelAndView("order/view").addObject("order", dto);
}
}
src/main/java - com.example.demo.service - CartService
@RequiredArgsConstructor
@Service
public class CartService {
private final CartDao cartDao;
private final ProductDao productDao;
@Value("${product.image.path}")
private String imagePath;
// 장바구니 읽기
public List<CartDto> read(String loginId) {
List<CartItem> cartItems = cartDao.findByUsername(loginId);
List<CartDto> cartDtos = new ArrayList<>();
for (CartItem cartItem : cartItems) {
Product p = productDao.findById(cartItem.getPno());
CartDto cartDto = new CartDto(p.getPno(), p.getVendor(), p.getName(), imagePath+p.getImagename(), p.getPrice(), cartItem.getCount());
cartDtos.add(cartDto);
}
return cartDtos;
}
// 장바구니 상품 추가
public Boolean add(Integer pno, Integer count, String loginId) {
CartItem cartItem = cartDao.findByUsernameAndPno(loginId, pno);
if (cartItem != null) {
Product product = productDao.findById(pno);
if ((cartItem.getCount() + count) > product.getStock())
return false;
cartDao.increase(new CartItem(loginId, pno, count));
} else {
cartDao.save(new CartItem(loginId, pno, count));
}
return true;
}
// 장바구니 상품 개수 증가
public Integer increase(Integer pno, String loginId) {
Product product = productDao.findById(pno);
CartItem cartItem = cartDao.findByUsernameAndPno(loginId, pno);
if ((cartItem.getCount()+1) >= product.getStock()) {
return -1;
}
cartDao.increase(new CartItem(loginId, pno, 1));
return cartItem.getCount() + 1;
}
// 장바구니 상품 개수 감소
public Integer decrease(Integer pno, String loginId) {
Product product = productDao.findById(pno);
CartItem cartItem = cartDao.findByUsernameAndPno(loginId, pno);
if (cartItem.getCount() <= 1)
return -1;
cartDao.decrease(new CartItem(loginId, pno, 1));
return cartItem.getCount() - 1;
}
// 장바구니에서 상품 삭제
public void delete(CartDeleteDto dto, String loginId) {
cartDao.delete(dto.getPnos(), loginId);
}
}
src/main/java - com.example.demo.service - OrderService
@RequiredArgsConstructor
@Service
public class OrderService {
private final OrderDao orderDao;
private final OrderItemDao orderItemDao;
private final AddressDao addressDao;
private final ProductDao productDao;
@Value("${product.image.path}")
private String imagePath;
public OrderDto.View orderList(List<Param> list, String loginId) {
List<OrderItem> orderItems = new ArrayList<>();
Integer totalPrice = 0;
for (Param param : list) {
Product product = productDao.findById(param.getPno());
OrderItem orderItem = new OrderItem(null, param.getPno(), product.getVendor(), product.getName(), imagePath+product.getImagename(), product.getPrice(), param.getCount(), product.getPrice() * param.getCount());
totalPrice = totalPrice + (product.getPrice() * param.getCount());
orderItems.add(orderItem);
}
return new OrderDto.View(totalPrice, orderItems);
}
}
src/main/resources - mapper - cartMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.CartDao">
<select id="findByUsername" resultType="cartitem">
select * from cart_item where username=#{username}
</select>
<select id="findByUsernameAndPno" resultType="cartitem">
select * from cart_item where username=#{username} and pno=#{pno} and rownum=1
</select>
<update id="update">
update cart_item set count=#{count} where username=#{username}
</update>
<insert id="save">
insert into cart_item values(#{username}, #{pno}, #{count})
</insert>
<update id="increase">
update cart_item set count=count+#{count} where username=#{username} and pno=#{pno}
</update>
<update id="decrease">
update cart_item set count=count-#{count} where username=#{username} and pno=#{pno}
</update>
<delete id="delete">
delete from cart_item where username=#{username} and pno in
<foreach collection='pnos' item='pno' open='(' close=')' separator=','>
#{pno}
</foreach>
</delete>
</mapper>
src/main/resources - templates - cart - list.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<title>Insert title here</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/main.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
// 화면이 로딩되면 전체 장바구니 가격을 계산해서 #total_price에 출력
function getTotalPrice() {
let totalPrice = 0;
// $.each(배열, function(idx, 원소))
// jQuery 객체를 대상으로는 $().each(function(idx, 원소))
$('.item_price').each(function(idx, item) {
totalPrice += parseInt($(item).text());
});
return totalPrice;
}
// + 플러스 버튼 처리
function plus() {
const $pno = $(this).attr('data-pno');
$.ajax({
url: "/cart/increase/" + $pno,
method: "patch"
}).done((count) => {
$(this).prev().text(count);
const $price = $(this).parent().prev().text();
$(this).parent().parent().parent().parent().next().find('.item_price').text(count * $price);
$('#total_price').text(getTotalPrice());
}).fail(() => alert("더 이상 구입할 수 없습니다. T-T"));
}
// - 마이너스 버튼 처리
function minus() {
const $pno = $(this).attr('data-pno');
let count = $(this).next().text();
if (count < 1)
return;
$.ajax({
url: "/cart/decrease/" + $pno,
method: "patch"
}).done((count) => {
$(this).prev().text(count);
const $price = $(this).parent().prev().text();
$(this).parent().parent().parent().parent().next().find('.item_price').text(count * $price);
$('#total_price').text(getTotalPrice());
})
}
$(document).ready(function() {
$('#total_price').text(getTotalPrice());
$('.plus').click(plus);
$('.minus').click(minus);
// 상품 삭제
// 여러 개의 값을 넘기는 방법 1. 서버에서 List로 받는다.
$('#delete').click(function() {
const $form = $('<form>').attr('method', 'post').attr('action', '/cart/delete').appendTo($('body'));
$('.pno').each(function(idx, checkbox) {
if ($(this).prop('checked') == true) {
$('<input>').attr('name', 'pnos').attr('type', 'text').val($(this).attr('data-pno')).appendTo($form);
}
})
if ($form.serialize() != "")
$form.submit();
})
// 상품 결제
// 여러 개의 값을 넘기는 방법 2. 서버가 여러 개의 값을 가지고 DTO를 만들고, DTO의 리스트를 갖는 DTO를 또 만든다.
$('#order').click(function() {
const result = confirm("상품을 주문하시겠습니까?");
if (result == true) {
let idx = 0;
const $form = $('<form>').attr('method', 'post').attr('action', '/order/multiple').appendTo($('body'));
$('.pno').each(function(idx, checkbox) {
const pno = parseInt($(checkbox).attr('data-pno'));
const count = parseInt($(checkbox).parent().next().next().find('.count').text());
$('<input>').attr('type', 'hidden').attr('name', 'list[' + idx + '].pno').val(pno).appendTo($form);
$('<input>').attr('type', 'hidden').attr('name', 'list[' + idx + '].count').val(count).appendTo($form);
idx++;
})
$form.submit();
}
})
})
</script>
</head>
<body>
<div id="page">
<header th:replace="/fragments/header.html">
</header>
<nav th:replace="/fragments/nav.html">
</nav>
<div id="main">
<aside th:replace="/fragments/aside.html">
</aside>
<section>
<table style="width:100%;" id="cart_table">
<tr th:each="item:${list}">
<td style="width:5%;">
<input type="checkbox" th:attr="data-pno=${item.pno}" class="pno">
</td>
<td style="width:10%;">
<img th:src="${item.imagename}" width="75px;" >
</td>
<td style="width:60%;">
<div class="upper">
<span th:text="${item.vendor}">LG</span>
<span th:text="${item.name}">OLED TV</span>
</div>
<div class="lower" style="overflow:hidden;">
<div class="left" style="float:left;">
<span th:text="${item.price}">9500</span>원
<div class="btn-group">
<button type="button" class="btn btn-primary minus" th:attr="data-pno=${item.pno}">-</button>
<button type="button" class="btn btn-primary count" th:text="${item.count}"></button>
<button type="button" class="btn btn-primary plus" th:attr="data-pno=${item.pno}">+</button>
</div>
</div>
<div class="right" style="float:right;">
<button type="button" class="btn btn-primary delete" th:attr="data-pno=${item.pno}">삭제</button>
</div>
</div>
</td>
<td style="width:25%; text-align: center;">
<span th:text="${item.count * item.price}" class="item_price"></span>원
</td>
</tr>
</table>
<div class="well well-sm">총액:<span id="total_price"></span>원</div>
<button id="order">결제</button>
<button id="delete">선택 상품 삭제</button>
</section>
</div>
<footer th:replace="/fragments/footer.html">
</footer>
</div>
</body>
</html>
src/main/java - templates - product - read.html
<script>
// 장바구니 버튼 처리
function cart() {
const $form = $('<form>').attr('method', 'post').attr('action', '/cart/add').appendTo($('body'));
$('<input>').attr('type', 'hidden').attr('name', 'pno').val($('#pno').text()).appendTo($form);
$('<input>').attr('type', 'hidden').attr('name', 'count').val($('#count_of_product').text()).appendTo($form);
$form.submit();
}
$(document).ready(function() {
// 장바구니 버튼
$('#cart').click(cart);
})
</script>