부트캠프를 수료한지 1달, 1차 프로젝트를 진행한지는 벌써 3달 정도의 시간이 흘렀는데 어느정도 리액트에 익숙해지고, 타입스크립트를 알아가는 요즘 미뤄뒀던 1차 프로젝트, 이상한 나라의 초콜릿 쇼핑몰 리팩토링을 시작한다!
사실 빨리 포폴 완성해야해...
당시 내가 담당했던 부분은 상품 상세 페이지와 장바구니 부분이었다.
장바구니는 로컬스토리지를 사용하여 구현하였고, 자바스크립트와 친하지 않았던 당시의 나는 엄청 삽질을 하며... 결국 제대로 완성시키지 못해 아쉬움이 있었다. 이제는... 조금 수월하게 할 수 있겠지?!
장바구니부터 수정해보려고 npm start
로 서버 실행시켜서 동작시켜보는데 내 기억보다는 잘 돌아갔다!! 몇몇 동작을 했을때 결제 정보창에 반영이 되지 않는 현상이 있어 이 부분부터 해결해보기로!
사실 장바구니 부분은 처음엔 내가 맡기는 했지만 여러사람이 붙어 해결을 했기에 결국 내코드라고 보기엔 애매했던 코드였다. 그래서 이번에 다시 보는데 막바지엔 다른 팀원분이 작업해서인지 완전 초면인 코드...!
코드를 파악해보니 결제창에 반영되는 부분은 동작이 하나하나 일어날때 마다 반영이 되어야 하는데,
(예를들면, 체크박스가 풀릴때마다 / 수량 변경이 될 때마다)
해당 부분들이 함수가 아니라 이벤트로 해당 상품의 가격과 수량을 DOM요소에서 찾아 더하고 빼며 결제정보창을 수정하고 있었다.
function minusQuantity(item) {
let innerNumb = item.path[1].querySelector(".productQuantity").innerText;
// 1보다 작을시 버튼 클릭 금지
if (innerNumb > 1) {
if (item.path[2].firstChild.checked) {
let targetNumber = item.path[1].querySelector(".productQuantity");
let targetQuantity = item.path[2].querySelector(".productQuantityNumb");
let selectedPrice = item.path[2].querySelector(".productPriceSpan");
const thisId = item.path[2].querySelector(".id").innerText;
// 로컬스토리지에서 박스랑 id같은걸 찾기
let getLocal = JSON.parse(localStorage.getItem("cartList"));
let findTarget;
getLocal.forEach((x) => {
if (x.id == thisId) {
x.quantity = Number(x.quantity) - 1;
findTarget = x.quantity;
}
});
localStorage.setItem("cartList", JSON.stringify(getLocal));
targetNumber.textContent = findTarget;
targetQuantity.textContent = findTarget;
const totalPrice = item.path[2].querySelector(".totalPrice");
//주문 수량과 가격을 곱하여 해당 상품의 총 금액을 보여줌
totalPrice.innerText =
Number(selectedPrice.innerText) * Number(findTarget);
//+ 버튼을 누르면 다시 활성화
const minus = item.path[1].querySelector(".minusProductQuantity");
minus.disabled = false;
let getPayProductQuantity = document.getElementById("payProductQuantity");
getPayProductQuantity.textContent =
Number(getPayProductQuantity.textContent) - 1;
//결제 정보 창에 총가격 표시
const selectedProductPrice = document.querySelectorAll(".totalPrice");
const totalPriceArr = [];
for (let i = 0; i < selectedProductPrice.length; i++) {
totalPriceArr.push(selectedProductPrice[i].innerText);
}
const sumOfTotalPrice = totalPriceArr.reduce(
(prev, next) => Number(prev) + Number(next),
0
);
payProductPrice.innerText = sumOfTotalPrice;
payTotalPrice.innerText = sumOfTotalPrice + 3000;
} else {
let targetNumber = item.path[1].querySelector(".productQuantity");
targetNumber.textContent = targetNumber.innerText - 1;
let targetQuantity = item.path[2].querySelector(".productQuantityNumb");
targetQuantity.textContent = targetQuantity.innerText - 1;
let totalPrice = item.path[2].querySelector(".totalPrice");
let selectedPrice = item.path[2].querySelector(".productPriceSpan");
totalPrice.innerText =
Number(totalPrice.innerText) - Number(selectedPrice.innerText);
}
}
}
초반에 작성했던 코드 중 수량 조절을 하는 마이너스 부분의 함수이다....
지금봐도 너무 어질어질 한 것....
해당 부분을 함수화 하고, 필요 없는 부분을 쳐내면서 수정해
function minusQuantity(event, id) {
const targetProduct = localStorageItem.filter(
(product) => product.id === id
)[0];
if (targetProduct.quantity <= 1) {
event.target.disabled = true;
return;
}
localStorageItem.forEach((product) => {
if (product.id == id) {
product.quantity = Number(product.quantity) - 1;
}
});
localStorage.setItem("cartList", JSON.stringify(localStorageItem));
while (displaying.hasChildNodes()) {
displaying.removeChild(displaying.firstChild);
}
displayData();
setPayBox();
}
우선 장바구니 페이지를 구현하면서 결제정보창을 그리는 부분을 함수로 빼내어 재사용하게 만들었고, 함수의 parameter를 활용해 노드를 뒤져서 값을 찾아내던 부분을 수정하여 코드를 보다 깔끔하게 만들었다.
다음으로 복잡했던 부분은 선택삭제를 구현하는 부분이었는데, 이 부분의 원래 코드는 아래와 같았다.
function checkSelectAll(item) {
let getLocal = JSON.parse(localStorage.getItem("cartList"));
let id = item.path[1].querySelector(".id").innerText;
if (item.path[0].checked == false) {
allSelectedCheckbox.checked = false;
payProductQuantity.innerText -=
item.path[1].childNodes[4].childNodes[1].innerText;
payProductPrice.innerText -=
item.path[1].childNodes[5].childNodes[4].childNodes[0].innerText;
payTotalPrice.innerText -=
item.path[1].childNodes[5].childNodes[4].childNodes[0].innerText;
getLocal.forEach((x) => {
if (x.id == id) {
x.quantity =
Number(x.quantity) -
Number(item.path[1].childNodes[4].childNodes[1].innerText);
}
});
localStorage.setItem("cartList", JSON.stringify(getLocal));
} else {
let trueCnt = 0;
for (let i = 0; i < selectedCheckBox.length; i++) {
if (selectedCheckBox[i].checked === true) {
trueCnt += 1;
}
}
if (trueCnt == selectedCheckBox.length) {
allSelectedCheckbox.checked = true;
}
payProductQuantity.innerText =
Number(payProductQuantity.innerText) +
Number(item.path[1].childNodes[4].childNodes[1].innerText);
payProductPrice.innerText =
Number(payProductPrice.innerText) +
Number(item.path[1].childNodes[5].childNodes[4].childNodes[0].innerText);
payTotalPrice.innerText =
Number(payTotalPrice.innerText) +
Number(item.path[1].childNodes[5].childNodes[4].childNodes[0].innerText);
getLocal.forEach((x) => {
if (x.id == id) {
x.quantity =
Number(x.quantity) +
Number(item.path[1].childNodes[4].childNodes[1].innerText);
}
});
localStorage.setItem("cartList", JSON.stringify(getLocal));
}
}
살펴보면 반복문을 통해 각각의 노드 요소들이 checked인지 아닌지를 보면서 수량과 가격을 계산하고 있는데 리팩토링을 거치면서
function individualCheck(e) {
const checkedProductBox = document.querySelectorAll(
".selectedCheckBox:checked"
);
const checkedProduct = [];
checkedProductBox.forEach((box) => {
if (box.checked) {
checkedProduct.push(box.parentElement.querySelector(".id").innerText);
}
});
if (!e.target.checked) {
allSelectedCheckbox.checked = false;
makingFilteredCartForCheckBox(checkedProduct);
setPayBox();
}
if (e.target.checked) {
makingFilteredCartForCheckBox(checkedProduct);
setPayBox();
if (checkedProduct.length === localStorageItem.length) {
allSelectedCheckbox.checked = true;
}
}
}
이렇게 수정하였다.
반복되는 수량과 가격 계산 부분을 함수화하여 재사용한 것이 코드 줄 수를 크게 줄일 수 있었고, checked된 노드들만 가져 올 수 있는
document.querySelectorAll('.className:checked')
를 알게된 것이 크게 작용했다.
이래서 기초가 탄탄해야 하는것....!
장바구니 부분을 수정하며 크게 달라진 부분은 이 정도이고, 이것도 성장의 과정이라 생각해 원본을 최대한 바꾸지 않으면서 코드 수정을 하려 했다.
이번 리팩토링에서는 우선적으로 반복되는 부분을 줄이고, 정리하면서 기존의 474줄의 코드를 311줄의 코드로 줄였다
더 자세한 장바구니 리팩토링 코드를 보고 싶으시다면
여기로 놀러오세요~!