우리 페이지는 네비게이션에 Cart라는 탭으로 장바구니 기능이 추가되어 있다.
Cart를 누르거나 상품리스트페이지에서 AddCart를 누르거나 상세페이지 장바구니담기를 누르면 값이 업데이트 되면서 모달창이 오른쪽으로부터 나온다.
<div className={['cartModal', isClickedCart && 'cartModalOn'].join(' ')}>
.cartModal {
position: fixed;
transition: right 0.1s linear;
right: -22rem;
}
.cartModalOn {
right: 0;
}
장바구니 모달창에는 각 상품의 수량을 증가, 감소시킬 수 있다.
각각의 상품을 컴포넌트화하고 그 안에서 state값으로 수량을 관리하면 각각의 수량을 관리할 수 있다.
그러나 총 수량으로 관리하기 위해서는 자식 컴포넌트의 액션이 부모 컴포넌트의 값에 영향을 주도록 로직을 구현해야했다.
부모 컴포넌트 총 수량 조절
const [totalOrderNum, setTotalOrderNum] = useState(0);
const decreaseTotalOrderNum = () => {
if (totalOrderNum < 2) {
setTotalOrderNum(1);
} else if (totalOrderNum < cartData.length + 1) {
setTotalOrderNum(cartData.length);
} else {
setTotalOrderNum(totalOrderNum => totalOrderNum - 1);
}
};
const increaseTotalOrderNum = () => {
setTotalOrderNum(totalOrderNum => totalOrderNum + 1);
};
const ProdInCart = ({
cartData,
decreaseTotalOrderNum,
increaseTotalOrderNum,
}) => {
const [orderNum, setOrderNum] = useState(cartData.quantity);
const increaseOrderNum = () => {
setOrderNum(prev => prev + 1);
increaseTotalOrderNum();
patchCartData('addition');
};
const decreasseOrderNum = e => {
if (orderNum < 2) {
setOrderNum(1);
} else {
setOrderNum(prev => prev - 1);
decreaseTotalOrderNum();
patchCartData('subtraction');
}
};
useEffect(() => {
setOrderNum(cartData.quantity);
}, [cartData]);
}
state[index]
를 통해 state의 배열 중 자신에게 해당하는 요소만 수정할 수 있다.state[index]
값을 수정하면 빈 배열이었던 state는 각각의 금액으로 구성된 배열이 완성된다.useEffect
를 통해 총합을 계산하면 카트에 담긴 상품의 총액을 구할 수 있다.⇒ 자세한 설명은 아래 글을 참고하면 된다. https://www.notion.so/d89aa2af4ad04c5c82a9b45b748f7481
문제 인식
원인 파악
map 메서드를 적용할 배열의 값이 왜 undefined인지 로직을 살펴보았다. 메인페이지에 들어서자마자 네브바 컴포넌트가 호출되고 네브바 안의 카트 컴포넌트는 카트에 담긴 데이터리스트를 요청하게 되는데, 사실 백엔드에서는 카트에 담긴 데이터를 보낼 때 토큰이 있는 경우에만 데이터를 보내던 것이었다.
⇒ 즉, API에 호출해서 받은 값은 없는데 이 값을 가지고 map함수를 돌리다가 에러가 난것이었다.
문제 해결
로그인이 되어 있지 않을 때, 즉 로그인 토큰이 발급되어 있지 않을 때는 카트 데이터를 요청하지 않도록 로직을 수정하였다. ⇒ 데이터를 가져오는 함수에 조건문 추가.
const getCartData = () => {
if (token) {
fetch('http://10.58.3.49:8000/orders/cart', {
method: 'GET',
headers: {
AUTHORIZATION: token,
},
})
.then(res => res.json())
.then(res => {
if (res.message === 'EMPTY CART') {
return;
} else {
setCartData(res.result[0].product);
let sum = 0;
res.result[0].product.forEach(product => {
sum = sum + product.quantity;
});
setTotalOrderNum(sum);
}
});
}
};
처음엔 매거진B 사이트와 같이 add to cart 버튼을 누르면 모달창이 나오고 장바구니에 상품이 추가되어 나오도록 로직을 구현하려 했다.
그러나, 장바구니 모달창은 공통 컴포넌트인 Nav.js에 속해있고 add to cart버튼은 외부 컴포넌트 리스트페이지와 상세페이지 컴포넌트에 속해있다.
const Router = () => {
return (
<BrowserRouter>
**<Nav />**
<Routes>
**<Route path="/Products/:product_id" element={<ProductDetail />} />
<Route path="/ProductList" element={<ProductList />} />**
</Routes>
<Footer />
</BrowserRouter>
);
};
따라서 add to cart 버튼 클릭 시, 모달창을 띄우기 위해서는 외부 컴포넌트간의 데이터 이동이 가능해야했다.
결국, add to cart 버튼 클릭 시 모달창은 나오지 않고 장바구니에 추가됐다는 알람만 뜨게 하는 형식으로 방향을 수정했다.
유저는 카트에 상품이 추가됐다는 알람을 확인하고 네브바의 Cart탭을 클릭하면 isClickedCart
라는 state값이 바뀌어 모달창이 나온다.
Cart.js의 useEffect는 API로부터 카트 데이터리스트들을 불러오는데, 2번째 인자값으로 isClickedCart
를 추가해 모달창이 작동하면 자동으로 데이터리스트를 업데이트해 원하는 구현이 가능했다.
const Router = () => {
const [modalState, setModalState] = useState(false);
return (
<BrowserRouter>
**<Nav modalState={modalState} setModalState={setModalState} />**
<Routes>
**<Route
path="/Products/:product_id"
element={<ProductDetail setModalState={setModalState} />}
/>
<Route
path="/ProductList"
element={
<ProductList
modalState={modalState}
setModalState={setModalState}
/>
}
/>**
</Routes>
<Footer />
</BrowserRouter>
);
};