장바구니 담기 기능 구현

기여·2024년 7월 31일
0

소소한 개발팁

목록 보기
63/103

방법1 - 본인이 생각했던 로직

  1. 동일 사용자가 동일 상품 몇 번 담았는지 확인, 즉 해당 (uid, product_code) 존재하는 cart건수 카운트
    1.1. 동일 상품 담은 기록 없으면 새 cart 삽입
    1.2.1. 동일 상품 담은 기록 있으면 해당건의 cartid 찾기
    1.2.2. 조회된 cartid의 레코드에 대해 장바구니에 담은 수량 변경

소스코드:
1, Vo

@Data
public class CartVo {
	
	private String cartid;	
	private String uid;	
	private String username;	
	private String product_code;	
	private String pname;
	private String pimgStr;	
	...
	private int pstock;	
	private int pquantity;	
	private int camount;			
	private int pprice;
	private int pdelifee;
	
	// 통화 변환. 천단위 콤마 표시
		public String getFmtPprice() {
			NumberFormat numberFormat = 
					NumberFormat.getNumberInstance(Locale.getDefault());
			
			return numberFormat.format(pprice);
		}
		
		public String getFmtPdelifee() {
			NumberFormat numberFormat = 
					NumberFormat.getNumberInstance(Locale.getDefault());
			
			return numberFormat.format(pdelifee);
		}	
}

2, Mapper

@Mapper
public interface CartMapper {
	
	//장바구니 담기
	
		//1. 해당 사용자가 해당 상품 몇 번 담았는지, 즉 (uid, product_code) 존재 cart건수 카운트	
		@Select ("select count(*) from cart "
				+ "WHERE uid=#{username} "
				+ "and product_code = #{product_code}")
		
				int countCart (@Param("username") String username, 
		    		@Param("product_code") String product_code);
		
		//1.1. 동일 상품 담은 기록 없으면 cart에 새 데이터 삽입
		@Insert ("insert into cart (uid, product_code, pquantity) "
				+ "values (#{uid}, #{product_code}, #{pquantity})")	
		
				void addCart (CartVo vo);
		
		//1.2.1. 동일 상품 담은 기록 있으면 해당건의 cartid 찾기	
		@Select ("select cartid from cart "
				+ "WHERE uid=#{username} "
				+ "and product_code = #{product_code}")
			
				String resultCartid(@Param("username") String username, 
					@Param("product_code") String product_code);
		
		//1.2.2. 조회된 cartid의 레코드에 대해 장바구니에 담은 수량 업뎃	
		@Update ("update cart set pquantity=pquantity + #{pquantity} "
				+ "where cartid=#{cartid}")
		
				void updatePquantity (CartVo vo);
	
	//장바구니 담기 끝	
}

3, Service

@Service
public class CartSvc {
	
	@Autowired
	CartMapper cartMapper;
	
	public void addCart(CartVo vo) {
		cartMapper.addCart(vo); //장바구니에 담기
	}

	public int countCart (String username, String product_code) {
		return cartMapper.countCart(username, product_code);
		
		/*
		해당 사용자가 해당 상품 몇 번 담았는지, 즉 (uid, product_code) 중복된 cart건수 카운트
		countCart: 카운트 메서드
		 */		 
	}
	
	public String resultCartid(String username, String product_code) {
        return cartMapper.resultCartid(username, product_code);  //장바구니 담은 기록 있는 cartid 추출
	}
	
	public void updatePquantity(CartVo vo) {
		cartMapper.updatePquantity(vo); //장바구니에 담은 수량 업뎃
	}	
}

4, Ctrl

@RequestMapping("/cart/")
@Controller
public class CartCtrl {
	
	@Autowired
	CartSvc cartSvc;
	
	@Autowired
	HttpSession session;
	
	@PostMapping("addCart")
	String addCart (Model model, CartVo vo) {
		System.out.println("addCart");
		
		String username=(String) session.getAttribute("username");
		vo.setUid(username); //redirect 경로 잡기 위해 db의 uid에 username값 대입
		
		String product_code = vo.getProduct_code();
		int countC = cartSvc.countCart(username, product_code);
		
		if (countC == 0) { //해당 사용자가 해당 상품 최초 담으면 (담은 기록 없으면) cart에 새 레코드 생성
			cartSvc.addCart(vo);
			
		} else { //countC > 0
			String cartid = cartSvc.resultCartid(username, product_code);
			//(select cartid from cart WHERE uid=? and product_code=?) 통해 cartid 찾기
            
			vo.setCartid(cartid); //해당 레코드의 cartid 지정			

			cartSvc.updatePquantity(vo); //해당 cartid의 레코드에서 해당 상품의 수량만 변경
		}		
		
		System.out.println("addCart uid/username: "+vo.getUid());
		
		return "redirect:cart?uid="+vo.getUid();
	}	
}

5, 제품 상세보기 > 주문 양식

<form action="/cart/addCart" method="post">
				<input type="hidden" name="product_code" th:value="${m.product_code}">
				
				<div>
				<strong  th:text="${m.pname}"></strong>
				</div>
				
				<div th:text="'판매가: ' + ${m.fmtPprice} + '원'"></div>
		        <div th:text="${m.pdelifee == 0 ? '무료배송❗' : '배송비: ' + m.fmtPdelifee + '원'}"></div>
		        
		        <div th:text="'주문가능 수량: ' + ${m.pstock} + '개'"></div>  
		        
		        주문수량: <input type="number" name="pquantity" value="1"  min="1" th:max="${m.pstock}">
		        <br><br>
		        <div>		        
			        <input type="submit"  value="바로 구매" class=button>
					<!-- 	실제 장바구니 이동 -->

					&emsp;
					
					<input type="button"  value="장바구니에 담기" class=subbtn>
					<!-- 	장바구니 이동 안 하고 추가만 함 -->

				</div>
	        </form>

6, 장바구니 (초본, 미연산)

<h1 th:text="'cart (' + ${lisize} + '건)'"></h1>

<form action="" method="post">

<input type="hidden" name="uid" value="${session.username}">

<table>
	<tr class="tr_color">
		<th>no</th> 		
		
		<th>cartid</th> 		
		<th>pname</th> 		
		<th>pimg</th>
		<th>pprice</th>		
		<th>pquantity</th>		
		<th>pdelifee</th>		 
		
	</tr>
		
	<tr th:each="m, stat : ${li}" class="tr_color">	
		
		<td th:text="${stat.count}"></td>		
				
		<td th:text="${m.cartid}"/>
		<td>
			<a th:href="@{/prd/viewPrd(product_code=${m.product_code})}" 
	        th:text="${m.pname}">
	        </a>
        </td>		
		
		<td>		
			<a th:href="@{/prd/viewPrd(product_code=${m.product_code})}">
	            <img th:src="@{/img/}+${m.pimgStr}" width="100" />
	        </a>	        	        	
		</td>
				
		<td>
		<input type="text" name="pprice" th:value="${m.fmtPprice} + '원'" readonly>
		</td>
		
		<td>
		<input type="number" name="pquantity" th:value="${m.pquantity}"  min="1" th:max="${m.pstock}">
		</td>
				
		<td>
		<input type="text" name="pdelifee" th:value="${m.fmtPdelifee} + '원'" readonly>
		</td>		
				
	</tr>	
</table>
</form>

방법2 - chat gpt에게 추천 받은 바
위보다 더 효율적인 로직 있는지 물어보니 아래와 같은 답변 받았다.

1, 단일 쿼리로 처리:
기존 로직에서는 상품을 담은 기록을 찾기 위해 두 번의 쿼리 (countCart, resultCartid)를 사용합니다. 이를 하나의 쿼리로 통합하여 처리할 수 있습니다. COUNT 쿼리 대신, cartid를 직접 조회하고 그 결과로 새 레코드를 삽입할지, 수량을 업데이트할지 결정할 수 있습니다.

2, 트랜잭션 사용:
장바구니 작업은 다단계 작업이기 때문에, 특히 여러 쿼리를 수행할 때 데이터 무결성을 보장하기 위해 트랜잭션을 사용하는 것이 좋습니다. 트랜잭션을 사용하면 모든 작업이 성공적으로 완료되었을 때만 데이터베이스에 반영되고, 그렇지 않으면 롤백됩니다.

1, Mapper

@Mapper
public interface CartMapper {		

	//1. 사용자가 동일 제품 담은 레코드 조회
	@Select("SELECT cartid FROM cart "
			+ "WHERE uid = #{uid} "
			+ "AND product_code = #{product_code}")
			
			String findCartidByUidAndProductCode(@Param("uid") String uid, 
			@Param("product_code") String product_code);
	
	//2. 동일 건 없으면 새 레코드 삽입
	@Insert("INSERT INTO cart (uid, product_code, pquantity) "
			+ "VALUES (#{uid}, #{product_code}, #{pquantity})")
			
			void addCart(CartVo vo);
	
	//3. 동일 건 있으면 해당 건의 주문수량 변경
	@Update("UPDATE cart SET pquantity = pquantity + #{pquantity} "
			+ "WHERE cartid = #{cartid}")
			
			void updatePquantity(CartVo vo);
            }

2, Svc

@Service
public class CartSvc {
	
	@Autowired
	CartMapper cartMapper;
	
	@Transactional
    public void addOrUpdateCart(CartVo vo) {
    
        	//1. 사용자가 동일 제품 담은 레코드 조회 결과의 cartid 가져오기
	        String cartid = cartMapper.findCartidByUidAndProductCode(vo.getUid(), vo.getProduct_code());
	        
            //2. 동일 건 없으면 새 레코드 삽입
	        if (cartid == null) { 
	            cartMapper.addCart(vo);
	            
	        //3. 동일 건 있으면 해당 건의 주문수량 변경
	        } else { 
	            vo.setCartid(cartid);
	            cartMapper.updatePquantity(vo);
	        }
    }
    }

3, Ctrl

@RequestMapping("/cart/")
@Controller
public class CartCtrl {
	
	@Autowired
	CartSvc cartSvc;
	
	@Autowired
	HttpSession session;
	
	@PostMapping("addCart")
	public String addCart(Model model, CartVo vo) {
        String username = (String) session.getAttribute("username");
        vo.setUid(username);

        cartSvc.addOrUpdateCart(vo);

        return "redirect:cart?uid=" + vo.getUid();
    }
    }

코드가 너무 깔끔해져서 감동..
chat gpt에게 추가로 물어보기 잘했다.
어쩌면 나 기기보다 더 기기 같은걸..
괜찮다. 초중보자니까 🥹

profile
기기 좋아하는 여자

0개의 댓글