88일차
메인페이지 구상하기
내브바
배너
가장 많이 팔린 상품 6개 ~ 3개
쇼핑하러가기
footer
위시리스트(찜하기)
좋아요방식사용
로그인하면 눌러지게 즐겨찾기 누르면 누르기
실시간으로 변하는것 보이기
눌러져잇으면 색변하게 보이기
멤버 id별 좋아요 목록 보기
찜목록 삭제하기
상품 상세페이지로 가기
테이블
멤버 id 상품 id 좋아요 여부?
즐겨찾기 기능(목록 추가하기(ajax) 기능 등등)
1. 메인페이지
배너 Carousel을 이용해서 사진이 돌아가게 해주기
top 3 상품 가장많이 팔린 상품 3가지를 가져온다.
1.1 캐러셀
안에 추가되는 이미지는 ajax를 통해서 가져오게 된다.
<div id="carouselExampleIndicators" class="carousel slide" data-bs-interval="false">
<div class="carousel-indicators">
<button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>
<button type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide-to="1" aria-label="Slide 2"></button>
</div>
<div class="carousel-inner"></div>
<button class="carousel-control-prev" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#carouselExampleIndicators" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="visually-hidden">Next</span>
</button>1.2 베스트 상품
카드 형식의 그리드로 3가지를 가져오게 된다.
<div>
<div class="container px-4 px-lg-5 mt-5">
<h2 class="mb-4">
<b>베스트 상품</b>
</h2>
<div id="productView" class="row gx-4 gx-lg-5 row-cols-2 row-cols-md-3 row-cols-xl-4 justify-content-center"></div>
</div>
</div>1.3 컨트롤러
캐러셀을 위한 사진들 정보와 베스트 상품의 정보를 한번에 가져오기 위해 ResponseEntity를 만들고 Map에 담아서 가져온다.
@GetMapping("listView")
public ResponseEntity<Map<String, Object>> listView() {
Map<String, Object> result = productService.getTopView();
return ResponseEntity.ok(result);
}이를 변형해서 @ResponseBody가 있으면 맵 그자체만 반환할 수도 있다.
@ResponseStatus(HttpStatus.OK)으로 상태를 표시하고 ResponseBody를 붙이고 Map을 반환하면 위와 같은 코드가 된다.
@GetMapping("listView")
@ResponseBody
@ResponseStatus(HttpStatus.OK)
public Map<String, Object> listView() {
Map<String, Object> result = productService.getTopView();
return result;
}이 두 코드는 거의 동일한 기능을 수행한다. 둘 다 "listView"라는 엔드포인트에 대한 GET 요청을 처리하며, productService.getTopView()를 호출하여 결과를 반환한다.
첫번째 코드는 ResponseEntity를 생성해서 반환하는데 ResponseEntity는 HTTP응답 상태코드 헤더 및 본문 데이터를 포함할 수 있는 객체이다.
상태코드를 설정하고 result를 응답본문에 포함시켰다.
두번째 코드는 @ResponseBody와 @ResponseStatus(HttpStatus.OK) 어노테이션을 사용하여 응답을 생성한다.
@ResponseBody는 메서드가 반환하는 객체를 HTTP 응답 본문에 직접 쓰도록 지정하는 데 사용한다.
@ResponseStatus(HttpStatus.OK)는 HTTP 상태 코드 200(OK)를 설정하는 데 사용한다.
응답상태에 따라서 구분해서 응답을 하고자하면 위코드를 사용하는게 좋고 아니라면 간단하게 후자를 사용해도 나쁘지 않다.
1.4 서비스
productList를 가져오는 것은 평소와 같게 3가지 상품만 가져오면된다.
// 메인페이지 top 3 가져오기
@Override
public Map<String, Object> getTopView() {
String prefix = "meatshop/main/carousel/"; // 원하는 경로
// ListObjectsV2Request를 사용하여 S3 버킷의 객체 목록을 가져옴
ListObjectsV2Request listRequest = ListObjectsV2Request.builder()
.bucket(bucketName)
.prefix(prefix)
.build();
// 객체 목록 요청 실행
ListObjectsV2Response listResponse = s3.listObjectsV2(listRequest);
// 파일 개수 출력
int fileCount = (int) listResponse.contents().stream()
.filter(s3Object -> !s3Object.key().endsWith("/"))
.count();
// S3 클라이언트 종료
List<ProductView> productList = productMapper.getTopView();
return Map.of("fileCount", fileCount, "productList", productList);
}기존과 다르게 파일 개수를 만들고 있다.
파일 개수를 가져오는 이유는 메인 배너 페이지에 사진이 현재는 2개만 넣지만 언제까지나 2개일수는 없고
유동적으로 개수를 구해서 사진의 개수에 따라 캐러셀에 반복적으로 img태그가 형성되도록 하고 싶었다.
1.String prefix = "meatshop/main/carousel/"; - "meatshop/main/carousel/"이라는 경로를 지정한다.
2.ListObjectsV2Request를 사용하여 S3 버킷의 객체 목록을 가져오기 위한 요청을 생성한다.
ListObjectsV2Request.builder()를 사용하여 빌더 객체를 생성하고 bucketName에는 버킷의 이름을 지정하고 prefix에는 원하는 경로를 지정한다.
3.ListObjectsV2Response를 사용하여 객체 목록 요청을 실행한다.
s3.listObjectsV2(listRequest)는 이전 단계에서 생성한 요청을 실행하고, listResponse에 결과를 저장한다.
4.(int) listResponse.contents().stream().filter(s3Object -> !s3Object.key().endsWith("/")).count(); - 파일 개수를 계산한다.
5.listResponse.contents()는 가져온 객체 목록을 반환한다.
.stream()은 객체 목록을 스트림으로 변환한다.
.filter(s3Object -> !s3Object.key().endsWith("/"))는 디렉토리가 아닌 파일만 필터링한다.
s3Object.key().endsWith("/")를 사용하여 디렉토리 여부를 확인하고, !를 사용하여 디렉토리가 아닌 것만 선택한다.
.count()는 선택된 파일 개수를 반환한다.
(int)는 결과를 정수형으로 캐스팅한다.
1.5 매퍼
메인 아이템 view를 만들어서 출력한다.
<select id="getTopView" resultMap="mainItemView">
SELECT * FROM mainitemview
</select>쿼리문이 나름 복잡하다.
SELECT p.*, COUNT(ol.product_id) AS sold, c.category_name
FROM products p
JOIN categories c ON p.category_id = c.category_id
LEFT JOIN (SELECT oi.order_id, oi.product_id, o.status FROM orderitems oi
JOIN orders o ON o.id = oi.order_id WHERE status != 'CANCEL') ol ON
p.product_id = ol.product_id
WHERE pub = 1
GROUP BY p.product_id
ORDER BY sold DESC, price DESC LIMIT 3;판매량을 기준으로 불러올 것이기 때문에 orderitems테이블에서 상품id별 개수를 가져오게 된다.
그런데 여기서 문제점은 주문을 하고 '취소'를 한 상품이 있다는 것이다. 주문에 대한 처리는 order테이블에 있다.
그래서 서브쿼리를 이용해서 order테이블과 orderitem테이블을 join해서 CANCEL이 아닌 주문 테이블을 만들 었다.
이후 product테이블과 카테고리이름도 필요하기 때문에 세가지 테이블을 join해서 상위 3가지를 가져오게 된다.
비공개 처리된 상품은 가져오지 않는다.
1.6 ajax
ajax를 통해서 데이터들을 가져오게 된다.
function listView() {
$.ajax("/listView", {
method: "get",
success: function(result) {
const fileCount = result.fileCount;
const productList = result.productList;
$(".carousel-inner").empty();
for (let i = 0; i < fileCount; i++) {
const carouselHTML = `
<div class="carousel-item ${i === 0 ? 'active' : ''}">
<img src="${bucketUrl}/main/carousel/main${i + 1}.jpg" class="d-block w-100" alt="banner1">
</div>`
$(".carousel-inner").append(carouselHTML);
}
$("#productView").empty();
productList.forEach(function(product) {
const productHTML =
`
<div class="col mb-5">
<div class="card h-100">
<div>
<img class="card-img-top" src="${bucketUrl}/product/${product.productId}/main.png" alt="사진준비중!" />
</div>
<div class="card-body p-4">
<div class="text-center">
<h5 class="fw-bolder">${product.productName}</h5>
${product.countryOfOrigin}
<br />
${product.price}원
<br />
남은수량 : ${product.stockQuantity}
<span class="badge text-bg-warning ${product.stockQuantity <= 10 && product.stockQuantity > 0 ? '' : 'd-none'}">마감임박</span>
<span class="badge text-bg-danger ${product.stockQuantity === 0 ? '' : 'd-none'}">품절</span>
</div>
</div>
<div class="card-footer p-4 pt-0 border-top-0 bg-transparent">
<div class="text-center">
<button onclick="location.href='/product/info/${product.productId}'" class="btn btn-secondary mt-auto" ${product.stockQuantity === 0 ? 'disabled' : ''}>${product.stockQuantity === 0 ? '상품준비중' : '상품 상세 보기'}</button>
</div>
</div>
</div>
</div>`
$("#productView").append(productHTML);
});
}
}); };
가져온 fileCount에 따라서 carouselHTML을 만들어 carousel-inner클래스 밑에 들어가게 한다.
가져온 상품 리스트는 자바스크립트의 foreach를 사용해서 전체 html을 만들고 뿌려주게 된다.
2. 쇼핑하러가기
여긴 별거 없다 이미지를 누르면 상품페이지로 가게 된다.
<div class="container-lg mt-5">
<img id="product" src="/img/main/product.jpg" class="d-block w-100" alt="product">
</div>3. footer
footer는 부트스트랩을 사용한 template중 하나에서 가져오게 되었다.'
페이지 설명 상품보기 고객관련메뉴로 나누었다.
<footer class="site-footer mt-5">
<div class="container">
<div class="row">
<div class="col-sm-12 col-md-6">
<h6>About</h6>
<p class="text-justify">
팀: 사람 이름
<br />
주제 : 고기 쇼핑몰
<br />
</p>
</div>
<div class="col-xs-6 col-md-3">
<h6>Categories</h6>
<ul class="footer-links">
<li>
<a href="/product/list">전체상품보기</a>
</li>
<li>
<a href="/product/list?category=1">소고기</a>
</li>
<li>
<a href="/product/list?category=2">돼지고기</a>
</li>
<li>
<a href="/product/list?category=3">닭고기</a>
</li>
</ul>
</div>
<div class="col-xs-6 col-md-3">
<h6>Customer</h6>
<ul class="footer-links">
<li>
<a href="/noticeBoard/list">공지사항</a>
</li>
<li>
<a href="/question/list">1대1문의</a>
</li>
<li>
<a href="/faq/list">FAQ</a>
</li>
<li>
<a class="nav-link" href="/member/mypage?id=<sec:authentication property="name" />">마이페이지</a>
</li>
<li>
<a href="/location">찾아오시는길</a>
</li>
</ul>
</div>
</div>
</div>
</footer>2023.06.01
간단한 일이지만 시간이 많이 걸리는 일들이었다.
파일개수를 아마존객체를 만들어서 가져오는 것에 대해서 새로운 고민을 하게 되었다.
'국비 > Project-2 쇼핑몰' 카테고리의 다른 글
| 2023.06.07 90일차 Team Project (0) | 2023.06.22 |
|---|---|
| 2023.06.02 89일차 Team Project (0) | 2023.06.06 |
| 2023.05.31 87일차 Team Project (0) | 2023.06.06 |
| 2023.05.30 86일차 Team Project (0) | 2023.05.31 |
| 2023.05.26 85일차 Team Project (0) | 2023.05.31 |