- 마이 페이지 구성
- 찜 목록 (룰렛, 찜 정보) (Mypage.js)
- 닉네임 변경 (ChangeNickname.js)
- 비밀번호 변경 (ChangePassword.js)
- 회원 탈퇴 (Resign.js)
- 비어 있을 때
마이페이지 버튼을 클릭하여 들어왔을 때 가장 처음이자 기본으로 띄워주는 페이지인 찜목록입니다.
햄릿 증후군을 위해 재밌고 신선하도록 룰렛을 추가하여 고를 수 있고 그 아래에 내가 여태 찜했던 식당 정보와 주류, 찜목록 삭제 버튼이 있습니다.
조건문을 설정해놓아 만약 아직 찜목록에 어떠한 상품도 존재하지 않는다면 룰렛과 찜목록 모두 보이지 않고 비어있다는 시각적 피드백을 줍니다.
여기서 룰렛은 react-custom-roulette을 활용하여 구현하였습니다.
이렇게 가지고온 모듈을 활용하여 룰렛의 세부 설정을 마친 후
앞서 언급한 찜목록에 존재하는 찜갯수에 따라 룰렛의 렌더링 여부를 결정하게 됩니다.
[클라이언트]
useEffect(() => {
axios.post("/mypage", { data: ID }).then((응답) => {
setView([...응답.data]);
});
}, []);
[서버]
app.post("/mypage", function (요청, 응답) {
db.collection("selection")
.find({ id: 요청.body.data })
.toArray(function (에러, 결과) {
응답.json(결과);
});
});
data의 길이가 0일 때는 룰렛을 표기하지 않고 텅-비어있다는 시각적 피드백을 줍니다.
- 여기서 view란?
views - useEffect를 활용하여 서버로부터 가져온 내 ID에 해당하는 찜목록의 내역const data = []; let num = 1; // views안에 있는 값들을 item에 넣어주세요 for (const item of views) { data.push({ id: num, option: item.식당 }); num++; }
{/* 룰렛 추가(찜이 0개 시, 보이지 않게) */}
{data.length != 0 && (
<div align="center" className="container pt-3 rounded">
<div class="container mt-5 p-1 rounded shadow-lg col-lg-8">
<h2 class="m-3 text-center text-light">
<strong>골라요! 룰렛</strong>
</h2>
</div>
<div className="pt-3 pb-3 ">
<Wheel
mustStartSpinning={mustSpin}
prizeNumber={prizeNumber}
data={data}
outerBorderColor={["#f2f2f2"]}
outerBorderWidth={[7]}
innerBorderColor={["#f2f2f2"]}
radiusLineColor={["#f2f2f2"]}
radiusLineWidth={[6]}
textColors={["#ffffff"]}
fontSize={[17]}
perpendicularText={[false]}
backgroundColors={["#474073", "#4F4D8C", "#8F8EBF", "#2E4159", "#262626"]}
onStopSpinning={() => {
setMustSpin(false);
}}
/>
</div>
<div className="bg-light rounded col-lg-6 shadow-lg">
<h4 className="p-4">{!mustSpin && state ? data[prizeNumber].option : "찜한 가게 중 하나를 골라드려요"}</h4>
</div>
<div className="pt-2">
<button className="btn press_btn btn-lg " onClick={handleSpinClick}>
룰렛 돌리기
</button>
</div>
</div>
)}
여기에서 찜목록이 비어있지 않다면
아래 useEffect를 활용하여 서버로 부터 받아온 데이터들을 state 변수인 view에 설정을 하여 우리가 넣어놓은 식당들을 나열하여 줍니다.
views.map((v, i) => {
return (
<div className="col-6 col-md-4 col-lg-3 col-xl-2 pt-3" key={i}>
<div className="d-flex justify-content-center">
<div className="card h-100" style={{ width: "18rem;" }}>
<h5 className="text-center card-title p-1">{views[i].drink}</h5>
<img src={views[i].사진} className="card-img-top p-1" alt="..." style={{ height: "10rem;" }} />
<div className="card-body">
<p className="card-text">
<strong>식당</strong> : {views[i].식당}
</p>
<p className="card-text">
<strong>종류</strong> : {views[i].종류}
</p>
<p className="card-text">
<strong>위치</strong> : {views[i].위치}
</p>
<p className="card-text">
<strong>평균가격</strong> : {views[i].평균가격}
</p>
<p className="card-text">
<strong>특징</strong> : {views[i].특징}
</p>
<button
className="btn btn-dark mt-3 d-grid gap-2 mx-auto"
onClick={() => {
axios
.post(
"/delete",
{
data: views[i].삭제용,
},
{ withCredentials: true }
)
.then((결과) => {
console.log(결과);
결과.data === "삭제완료" && alert("삭제가 완료 되었습니다. ");
})
.then(
axios.post("/mypage", { data: ID }).then((응답) => {
갖고온거 = 응답.data;
console.log("갖고온거", 갖고온거);
setView([...갖고온거]);
})
);
}}
>
삭제하기
</button>
</div>
</div>
</div>
</div>
);
})
삭제하기 버튼을 누를 시 나와 있는 onClick 함수를 보면
서버로부터 delete에 post 요청을 하는 모습을 볼 수 있습니다.
[서버]
app.post("/delete", function (요청, 응답) {
db.collection("selection").deleteOne({ 삭제용: 요청.body.data }, function (err, result) {
응답.json("삭제완료");
});
});
서버에 /delete를 보면 우리가 저장했던 찜의 데이터베이스에서 삭제용을 찾아 deleteOne을 하는 모습을 볼 수 있습니다.
여기서 body에 전달한 삭제용은 무엇인가 하면
앞서 안주 선택페이지에서 하트 이미지를 눌러 찜목록에 추가할 때 "삭제용" 키 값으로 본인이 로그인시에 넣었던 id와 식당이름이 들어갑니다.
찜목록에 이미 존재하는 식당은 중복 추가가 되지 않기 때문에 자신 (본인의 id)이 추가한 식당 한 개만이 데이터베이스에 존재하기 때문입니다.
마지막으로 소개할 기능은 골라요 룰렛 기능입니다.
GIF를 참고하면 삭제하기 기능도 실행을 해놓은 것을 볼 수 있고
react-custom-roulette을 활용하여 구현했습니다.
[결과 표시]
<div className="bg-light rounded col-lg-6 shadow-lg">
<h4 className="p-4">{!mustSpin && state ? data[prizeNumber].option : "찜한 가게 중 하나를 골라드려요"}</h4>
</div>
선택을 완료하기 전 결과창에는 "찜한 가게 중 하나를 골라드려요"라는 문구가 나오며 마지막으로 결과가 나왔을 때 (mustSpin이 false이고 state가 true일 때) 우리가 앞서 for of를 통해 넣은 views의 option에 해당 하는 식당이 나옵니다.
for (const item of views) {
data.push({ id: num, option: item.식당 });
num++;
}
여기서 넣은 option의 item식당을 표기하는 것인데
룰렛이 돌아갈 땐 mustSpin의 값이 true로 바뀌고 멈출 때 다시 false로 바뀝니다.
*초기 값도 false (state)
이를 사용하여
우리가 룰렛 버튼을 눌렀을 때 mustSpin의 값과 state의 값을 true 바꾸어주고 룰렛이 다 멈추었을 시 우리의 data[prizeNumber].option을 통해 prizeNumber에 해당하는 data의 인덱스의 option값을 띄워줍니다.