로그인
if(localStorage.getItem("user_id") === null){
alert('로그인 후 위시리스트에 등록 해주세요.')
navigate('/user/login')
}
처음에 상품을 위시리스트에 담기 위해서는 로그인을 하도록 페이지이동을 하였다. 로그인이 되어 있는지 알기 위해서 로컬 스토리지에 로그인한 정보를 담아두었기 때문에 이를 가져온다. 그래서 이 정보가 null이라면 로그인이 되지 않았다는 의미로, 로그인 페이지로 로그인을 하게 하였다. 로그인이 되었다면 바로 상품이 위시리스트에 담기게 하였다.
위시리스트에 담기
axios
.post(`http://localhost:3001/wish_list/wish`, {
user_no: localStorage.getItem("user_id"),
product_no: product.product_no,
})
.then((result) => {
console.log(result);
setHeart(!heart);
alert("상품이 위시리스트에 담겼습니다. ");
})
.catch((error) => {
alert("이미 위시리스트에 있는 상품입니다. ");
});
백엔드 코드로 갈 수 있게 axios를 넣어서 구현하였다. 이를 위해 로그인 정보인 id와 상품 id를 받아오게 하였다.
위의 사진은 위시리스트 db이다. 유저id와 상품id를 담아주도록 하였고, 만약 똑같은 유저와 상품 id가 존재할 경우 이미 사용자가 위시리스트에 담은 것이기에 맨 위의 동영상에서도 확인할 수 있는 것처럼 위시리스트에 담긴 상품이라는 alert창이 뜬다.
백엔드
router.post("/wish", async function (req, res) {
const user_no = req.body.user_no;
const product_no = req.body.product_no;
console.log("wish_list :: ", user_no);
console.log("wish_list :: ", product_no);
let isExists = false;
models.WISH_LIST.findAll({ where: { user_no: user_no } })
.then((product) => {
product.map((product) => {
if (product.dataValues.product_no === product_no) {
isExists = true;
}
});
if (isExists === false) {
models.WISH_LIST.create({
user_no: user_no,
product_no: product_no,
})
.then((product) => {
return res.status(200).json("success");
})
.catch((err) => {
console.log("에러난건가 ", err);
});
} else {
return res.status(400).json({
message: "이미 위시리스트에 있는 상품입니다. ",
});
}
console.log(isExists);
})
.catch((err) => {
console.log("error 발생 ", err);
});
});
백엔드에서 데이터를 받아와서 어떻게 구현할지 문제였다. 위시리스트에 담긴 상품을 위시리스트 메뉴에 가서 확인해야 했기 때문에 이 상품에 대한 정보를 가져왔어야 했다.
문제점
- 로컬 스토리지에 담을 경우 : 로그인을 한다는 가정하에 로컬스토리지에 담는다면, 영구적이지 않기 때문에 확인이 불가능하다. 그래서 사용자들이 위시리스트에 갔을 때 내가 담은 상품이 보이게 할려면 db에 담아놔야 한다.
- db에 담았을 때 : db에 위시리스트 정보(유저 id, 상품 id)만 담는다면, 위시리스트에 장바구니처럼 상품을 보여줘야 한다. 이를 어떻게 상품정보를 상품id로 비교해서 가져올 것인가.
→ 결론 : 상품 db에 있는 아이디와 위시리스트의 id와 비교하자.
코드 설명
우선 sql문을 두 번 써주었다. 코드에서 보면 위시리스트에 user_no를 통해 상품 id를 받아왔다. 그래서 findAll을 써서 다 가져왔다. 그리고 배열로 dataValues로 감싸져 있기 때문에 map을 써주어서 객체로 있는 정보를 상품 아이디만 가져와서 위시리스트에 담을 상품과 비교해주었다.
비교했을 때 상품의 아이디가 위시리스트에 이미 있는 경우 담을 필요가 없기 때문에 위시리스트에 있는 상품이라는 메시지를 프론트에 전달하였다. 그것이 아니라면 db에 저장하는 방식을 썼다.
하트 구현
위시리스트에 담겼을 경우 하트가 색깔이 찬 하트가 된다.
이렇게 구현이 되기 위해서 위에서 비교한 것처럼 비교해주면 된다. 그리고 이를 페이지가 있는 코드에서 넣어주면 된다. 말만 쉽다.
우선 상세페이지 이전의 상품이 열거된 페이지의 코드에서 wish의 db에 해당 상품의 id가 있는지 확인해준다.
const [wish, setWish] = useState(JSON.parse(localStorage.getItem("wish")));
→ 로컬스토리지에 위시리스트에 담긴 상품 id를 담아주고, 이를 useState를 이용하여 가져왔다.
<Link className="card-link" to={`/items/${cloth.product_no}`} state={{data : wish.includes(cloth.product_no)}}>
→ 위의 링크는 해당 이미지를 눌렀을 때 페이지가 이동하면서 상품에 대한 정보를 보여준다. 링크를 클릭시 상세페이지로 넘어가기 때문에 이때 로컬스토리지의 위시리스트에 담긴 상품id가 이곳에 있는지 확인해준다.
이 값은 불린값으로 나오기에 useLocation을 상세페이지에서 확인해준다. 이를 확인하여 하트값에 넣어준다. 그러면 하트가 true면 꽉찬 하트가 나오고, false값이면 빈 하트가 출력되게 된다.
덧붙여서
{wish.length >= 0 && products.map((cloth, index) => (
<Col span={6} key={index}>
<Link className="card-link" to={`/items/${cloth.product_no}`} state={{data : wish.includes(cloth.product_no)}}>
<div className="card-list">
이렇게 해서 넣어줬다.
위시리스트의 핵심!
상품의 정보를 useEffect를 사용하여 비동기식으로 가져오기 때문에 map이 끝나야 해당 구문이 실행이 된다. 만약 wish.length≥0이 없을 경우, 앞에서 말한 것처럼 비동기식으로 돌아가기 때문에 heart값은 계속 false값이다. 그러므로 위시값을 다 받아와주고 useEffect의 비동기 구문이 다 끝날 때 페이지 이동이 되기 때문에 제대로 된 값이 페이지에 들어온다.