자바스크립트로 구현한 쇼핑몰 기능을 담은 프로젝트
- mock data를 json으로 직접 만들어 진행
- localStorage로 데이터 저장
📚 구조 & 기능
📌 Home
header
- (mobile) sidebar menu
- scroll시, nav-menu 상단 고정
new-section
- new goods section - 가로 scroll
- goods 클릭 시, Detail-Page 이동
goods-section
- mock data 받아와서 동적으로 list 출력
- 가격 1000 단위 절사
- goods pagination
- 색상별 정렬
- goods 클릭 시, Detail-Page로 이동
- 좋아요 클릭 시, WishList에 저장
- icon 색상 변경 (재 클릭 시 원 상태 + WishList에서 삭제)
- 장바구니 클릭시, Cart에 저장
- 장바구니 총 수량 + 1 (최상단에 nav icon에 표시)
- 재 클릭 시, 이미 cart에 들어가 있는 상품 고지 알림
footer
- info
📌 WishList-Page
- WishList-Page에서 상품 삭제 기능
- WishList 비어있을 시, 고지 문구
📌 Cart-Page
- cart에서 상품 삭제 기능
- goods별 수량 count
- goods별 합계 금액
- goods 총 합계 금액
- cart 비어있을 시, 고지 문구
📌 Detail-Page
- 좋아요 클릭 시, WishList에 저장
- 장바구니 클릭시, Cart에 저장
📚 Problem & Solution
1. 좋아요 클릭 시, WishList-Page에 저장
Uncaught (in promise) TypeError: Cannot set property 'innerHTML' of null at displayItemsss
오류가 생겼다. innerHTML을 설정할 수 없다는 뜻인데, 각각 다른 HTML 파일에서 js파일 하나를 로드하려다 보니 'itemContainer' 변수를 읽어오지 못해서 생긴 문제였다. 처음에는 그냥 파일을 분리하려 했는데 'createHTML'함수를 재사용해야 하다 보니 무조건 이 파일에서 해결해야 하는 문제였다..
if (itemContainer !== null) {
itemContainer.innerHTML = shoesBox
.map((shoes) => createHTML(shoes))
.join("");
}
결론은! itemContainer이 null이 아닌 값에서만 실행되게 하는 if문을 만들었더니 드디어 각 HTML마다 있던 위 오류가 사라졌다..!!
2. 빈 배열로 다시 돌아가는 현상
//wish-storage
let wishObj = localStorage.getItem("wishList")
? JSON.parse(localStorage.getItem("wishList"))
: [];
3. header, footer 공통 마크업 분리
//header, footer markup data include
async function asyncMarkupData() {
const allElements = document.getElementsByTagName("*");
Array.prototype.forEach.call(allElements, function (el) {
const includePath = el.dataset.includePath;
if (includePath) {
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
el.outerHTML = this.responseText;
}
};
xhttp.open("GET", includePath, true);
xhttp.send();
}
});
}
<!-- header -->
<div data-include-path="header.html"></div>
<!-- footer -->
<div data-include-path="footer.html"></div>
- 공통 마크업을 분리해서 HTML 코드를 확실히 많이 줄일 수 있었다.
4. pagination
//pagination
function pagination(shoesBox) {
const pageContainer = document.querySelector(".goods-pagination");
let pageArray = []; //페이지
let totalCount = shoesBox.length; //총 데이터 수
let totalPage = Math.ceil(totalCount / 6); //총 페이지 수
let currentPage = 1; //현재 페이지
//1 ~ 총 페이지 수 출력
for (let i = 1; i <= totalPage; i++) {
pageArray.push(i);
}
//페이지 그리기
if (pageContainer !== null) {
pageContainer.innerHTML = pageArray
.map((num) => paginationHTML(num))
.join("");
}
//클릭한 버튼의 page number
function pageData(e) {
if (e.target.tagName === "BUTTON") {
let currentPage = parseInt(e.target.innerHTML);
displayItems(pageShowBox,currentPage);
loadCart(pageShowBox);
loadWish(pageShowBox);
}
}
pageContainer && pageContainer.addEventListener("click", pageData);
displayItems(pageShowBox,currentPage);
}
//data slice
let dataPerPage = 6;
let startIndexItem = currentPageNum - 1;
let pageShowBox = shoesBox.slice(
dataPerPage * startIndexItem,
dataPerPage * currentPageNum
);
- react에서 pagination을 구현해보았지만 js에서 pagination을 구현해본 건 처음이었다.(거꾸로 된 거 같은 기분..) react로 게시판을 만들 때 사용했던 pagination 로직을 떠올리며 비슷하게 구현해보았는데, react에서는 contextAPI나 redux로 상태를 전달할 수 있었지만 js에서는 불가능해서인지 코드가 뭔가 복잡해진 것 같다..
5. 클릭시, 클릭된 데이터 다른 페이지로 이동하기
- a 태그의 링크로 클릭한 상품의 id를 detail 페이지의 url 파라미터로 넘기고, 해당 페이지에서 url의 파라미터를 받아왔다.
//load detail goods
export function loadDetail(shoesBox) {
let url = location.search;
let params = url.substring(url.indexOf("?") + 1, url.length);
const detailGoods = shoesBox.find((shoes) => {
return shoes.id === parseInt(params);
});
paintDetail(detailGoods);
detailSelectGoods(detailGoods);
}
'Solution & What I learn' 카테고리의 다른 글
Heroku로 react 프로젝트 배포하기 (json-server 포함) (2) | 2021.03.15 |
---|---|
[React - Project] movie | Issue & Solution (0) | 2021.03.03 |
[React, TypeScript - Project] 독서 기록 서비스 구현하기 | Issue & Solution (0) | 2021.02.13 |
[React, Redux - Project] 댓글 서비스 구현하기 | Issue & Solution (0) | 2021.01.29 |
[React - Project] 지출 기록 서비스 구현하기 | Issue & Solution (0) | 2021.01.06 |