오늘 할 것 : 리뷰 장바구니 구매
GCP에 배포
장바구니
로컬저장소(로컬스토리즈) 브라우저 저장소에 저장이 된다.
Js로 구현한다. 잘 사용하지 않느다.
로그인하고 장바구니에 담았다고 치자.
DB화 해야한다.
장바구니 구매 리뷰는 프로덕트와 멤버에 종송적이지 않다.
이 세개를 묶자.(장바구니 구매 리뷰)
carts 로 (스키마 하나 더 만든다.)
-> 장바구니 테이블 생성 carts
-> shopping
패키지 -> CartEntity
-> ShoppingController postCartAdd
-> AddResult
-> AddVo
-> ShoppingController postCartAdd
-> IShoppingMapper insertCart
-> ShoppingMapper.xml insertCart
-> ShoppingService 생성후 ShoppingController
-> ShoppingService
-> ShoppingController
-> detal.html 추가
-> detail.js 추가
-> 장바구니에 상품을 담아보자.
-> ShoppingController getCart
Tuple과 HashMap의 차이점
- 해쉬맵은 키와 값쌍으로 이루어진 것들의 나열이다.
[K-V]
-> 키가 겹치면 안된다.- pair는 그냥 두 타입의 쌍이다. 이거 단 하나만 가진다. ->
[L-R]
누가 키고 값이고가 아니라 둘 다 값이다. (getLight, getLeft로 사용.)
Triple 사용예시
Dto를 만들지 않고 Tuple(튜플)로 하자.
-> ShoppingController getCart
apache.commons.lang3
로 추가- List로 수정. List로 사용하자.
-> ShoppingService getCartOf
-> IShoppingMapper selectCartByEmail
-> ShoppingMapper.xml selectCartByEmail
- 장바구니에 담았던 상품을 또 담으면 리스트가 중복으로 생기지 않고 사려고 하는 갯수만 올라간다. 어떻게 하면 좋을까?
count
를 모두 합해주고ORDER BY
를 걸어주자.
-> ShoppingController getCart
- 향상된 for문 사용.
-> ShoppingController productService
상수 추가
-> ProductService getProductByIndex
추가
IProductMapper
에 이미getProductByIndex
있으니깐 바로 적어주자 .
-> ShoppingController getCart
ImmutablePair
가 붙은 객체들은 보통 final 이다.
-> cart.html
- 장바구니를 누르면 장바구니에 담은 상품의 사진이 나온다.
<tr th:each="pair : ${pairList}" th:with="cartEntity=${pair.getLeft()}, productEntity=${pair.getRight()}"> <td> <img th:src="@{/product/detail/thumbnail (id=${productEntity.getThumbnailId()})}"> </td> </tr>
- 설명
-> ShoppingController getCart
-> ShoppingController getCart
-> cart.html
<body> <div class="logo-container"> <img alt="Coupang" class="logo" th:src="@{/resources/images/logo.png}"> </div> <table> <thead> <tr> <th></th> <th>상품정보</th> <th>상품금액</th> <th>배송비</th> </tr> </thead> <tbody> <tr th:each="pair : ${pairList}" th:with="cartEntity=${pair.getLeft()}, productEntity=${pair.getRight()}"> <td> <img class="thumbnail" th:src="@{/product/detail/thumbnail (id=${productEntity.getThumbnailId()})}"> </td> <td> <div class="title" th:text="${productEntity.getTitle()}"></div> <div class="detail" th:with="dt = ${#dates.createNow()}"> <span class="due" style="color: black;" th:if="${productEntity.getDelivery().equals('normal')}" th:with="tomorrow = ${T(org.apache.commons.lang3.time.DateUtils).addDays(dt, 2)}" th:text="${#dates.format(tomorrow, 'M/d') + '(' + #dates.dayOfWeekName(tomorrow).substring(0, 1) + ') 도착 예정'}"></span> <span class="due" th:if="${productEntity.getDelivery().equals('rocket')}" th:with="tomorrow = ${T(org.apache.commons.lang3.time.DateUtils).addDays(dt, 1)}" th:text="${#dates.format(tomorrow, 'M/d') + '(' + #dates.dayOfWeekName(tomorrow).substring(0, 1) + ') 도착 보장'}"></span> <span class="due" th:if="${productEntity.getDelivery().equals('rocketFresh')}" th:with="tomorrow = ${T(org.apache.commons.lang3.time.DateUtils).addDays(dt, 1)}" th:text="${#dates.format(tomorrow, 'M/d') + '(' + #dates.dayOfWeekName(tomorrow).substring(0, 1) + ') 새벽 도착 보장'}"></span> <span class="spring"></span> <span th:text="${#numbers.formatInteger(productEntity.getPrice(), 0, 'COMMA')+'원'}"></span> <span th:text="${cartEntity.getCount() + '개'}"></span> <span th:text="${#numbers.formatInteger(productEntity.getPrice() + cartEntity.getCount(), 0, 'COMMA') + '원'}"></span> <a th:href="@{/shopping/cart/delete/ (index=${productEntity.getIndex()})}" onclick="return confirm('정말로 해당 삼품을 장바구니에서 삭제할까요?')">삭제</a> </div> </td> <td class="spaced"> <div th:text="${#numbers.formatInteger(productEntity.getPrice() + cartEntity.getCount(), 0, 'COMMA') + '원'}"></div> <img alt="로켓배송" class="rocket" th:src="@{/resources/images/rocket.png}" th:if="${productEntity.getDelivery().equals('rocket')}"> <img alt="로켓프레시" class="rocket" th:src="@{/resources/images/rocket-fresh.png}" th:if="${productEntity.getDelivery().equals('rocketFresh')}"> </td> <td class="spaced"> <!--/*@thymesVar id="getDelivery" type="dev.jwkim.coupang.entities.product.ProductEntity"*/--> <span class="fee" th:if="${productEntity.getDelivery().equals('normal')}">배송비 3,000원</span> <span class="fee" th:if="${productEntity.getDelivery().equals('rocket') || productEntity.getDelivery().equals('rocketFresh')}">무료</span> </td> </tr> </tbody> <tfoot> <tr> <td colspan="4"> <div class="wrapper"> <span>상품가격</span> <span class="value" th:text="${#numbers.formatInteger(totalProduct, 0, 'COMMA') + '원'}"></span> <span class="sign">+</span> <span>배송비</span> <span class="value" th:text="${#numbers.formatInteger(totalShopping, 0, 'COMMA') + '원'}"></span> <span class="sign">=</span> <span>주문금액</span> <span class="value" th:text="${#numbers.formatInteger(total, 0, 'COMMA') + '원'}"></span> </div> </td> </tr> </tfoot> </table> <div class="buttons"> <a class="button back" th:href="@{/}">계속 쇼핑하기</a> <a class="button order" th:href="@{/shopping/order}">구매하기</a> </div> </body>
-> cart.css
<style>
@charset "UTF-8";
body > .logo-container {
width: var(--content-max-width);
margin: 2rem 0 1rem 0;
}
body > .logo-container > .logo {
height: 2.5rem;
}
body > table {
width: var(--content-max-width);
border-collapse: collapse;
}
body > table > thead th,
body > table > tfoot td {
background-color: rgb(250, 250, 250);
border-top: 0.0625rem solid rgb(221, 221, 221);
border-bottom: 0.0625rem solid rgb(221, 221, 221);
padding: 0.75rem;
white-space: nowrap;
}
body > table > tbody .thumbnail {
height: 6rem;
margin: 1rem 2rem 1rem 0;
}
body > table > tbody .title {
border-bottom: 0.0625rem solid rgb(221, 221, 221);
font-size: 1.125rem;
font-weight: 500;
margin-bottom: 0.625rem;
padding-bottom: 0.625rem;
}
body > table > tbody .detail {
align-items: center;
display: flex;
flex-direction: row;
justify-content: flex-start;
margin-right: 0.5rem;
}
body > table > tbody .detail > * + * {
margin-left: 0.5rem;
}
body > table > tbody .detail > .spring {
flex: 1;
}
body > table > tbody .rocket {
height: 1.125rem;
margin-top: 0.25rem;
}
body > table > tbody td {
border-bottom: 0.0625rem solid rgb(221, 221, 221);
}
body > table > tbody td.spaced {
border: 0.0625rem solid rgb(221, 221, 221);
border-right: none;
padding: 0.5rem 1rem;
text-align: center;
}
body > table > tfoot td {
padding: 1.5rem;
}
body > table > tfoot .wrapper {
align-items: center;
display: flex;
flex-direction: row;
justify-content: flex-end;
}
body > table > tfoot .value {
font-weight: 600;
margin-left: 0.5rem;
}
body > table > tfoot .sign {
top: 0.0625rem;
width: 1.5rem;
height: 1.5rem;
align-items: center;
background-color: rgb(200, 200, 200);
border-radius: 50%;
box-sizing: border-box;
color: rgb(255, 255, 255);
display: flex;
flex-direction: row;
font-size: 1.25rem;
font-weight: 800;
justify-content: center;
margin: 0 0.5rem;
padding-bottom: 0.3rem;
position: relative;
}
body > table > tfoot .value:last-child {
font-size: 1.125rem;
}
body > .buttons {
align-items: center;
display: flex;
flex-direction: row;
justify-content: center;
margin: 3rem 0;
}
body > .buttons > .button {
width: 10rem;
border-radius: 0.5rem;
font-size: 1.5rem;
padding: 1rem 2rem;
text-align: center;
}
body > .buttons > .button + .button {
margin-left: 0.75rem;
}
body > .buttons > .button.back {
border: 0.125rem solid rgb(66, 132, 243);
color: rgb(66, 132, 243);
}
body > .buttons > .button.order {
background-color: rgb(66, 132, 243);
border: 0.125rem solid rgb(66, 132, 243);
color: rgb(255, 255, 255);
}
</style>
-> 최종결과