
📕 두번째 프로젝트 - [밀리는 서재]
🎞️ 시연영상
https://www.youtube.com/watch?v=vOymkA7-0rU
﹣ 2022.11.14~11.25
[프론트] - React, Javascript, Router, Sass
[백엔드] - Javascript, Express(Node.js), Mysql, dbmate(DB scheme 버전관리), jsonwebtoken(토큰 발행), bcryptjs(비밀번호 암호화),Axios

트렐로를 이용해서 진행상황을 공유하고, 디스코드를 이용하여 매일 10시 회의를 하였습니다!

{item.map(
({
id,
title,
cover_img,
toc,...
}) => (
<div className="wrapper" key={id}>
<div className={css.BookContainer}>
<img src={cover_img} alt="책 이미지" />
<div className={css.BookInfo}>
<div className={css.BookInfoContent}>
...
{books_authors.map((prop, idx) => {
return <AuthorName key={idx} {...prop} />;
})}
</div>
...
<Index key={toc.id} toc={toc} />
{books_authors.map(author => {
return <Author key={author.author_id} {...author} />;
})}
<Publisher key={publisher.id} publisher={publisher} />
<Comments setBookInfoComments={setCommentArray} />
</div>
)
)}
백엔드에서 해당 책의 정보가 담긴 데이터들을 가져왔다. map 함수를 돌려서 데이터를 넣어주었고, 해당하는 컴포넌트에 props를 이용해 데이터를 넘겼다. 배열데이터는 한번 더 map함수를 돌려 넘겨주었다.

const [bookData, setBookData] = useState([]);
const [likeCheck, setLikeCheck] = useState(false);
//찜 데이터 보내기
const onFavorite = () => {
fetch(`서버주소`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
body: JSON.stringify({
books_id: bookData[0].id,
}),
})
.then(res => res.json())
.then(res => {
fetch(`서버주소`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
})
.then(res => res.json())
.then(data => {
setLikeCheck(data);
});
});
};
//찜 데이터 삭제
const delFavorite = () => {
fetch(`서버주소`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
body: JSON.stringify({
books_id: bookData[0].id,
}),
})
.then(res => res.json())
.then(res => {
fetch(`서버주소`, {
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
})
.then(res => res.json())
.then(data => {
setLikeCheck(data);
});
});
};
찜부분은 fetch 를 많이해야만 했다..! 먼저 찜했을때 books_id 데이터를 보내고, 한번 더 클릭 시 삭제할 수 있도록 했다. 그리고 페이지에서 찜한 상품의 별은 노란색이 채워져 있어야해서 찜 값이 true일 시 적용 될 수 있도록 하였다. 물론 이 부분도 체크 데이터를 확인하기 위해 fetch를 사용했다.

//내 서재에 데이터 보내기
const onMyShelf = () => {
fetch(`서버주소`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
body: JSON.stringify({
books_id: bookData[0].id,
}),
});
};
찜과 같이 서재 담기를 클릭 시 백으로 데이터를 전송하였다! 그 후 내서재 페이지에서 해당 책이 담긴 것을 확인 할 수 있다.

----------댓글 추가
const addComment = event => {
fetch(`서버주소`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
body: JSON.stringify({
books_id: params.id,
content: value.current.value,
}),
})
.then(res => res.json())
.then(res => {
fetch(`서버주소`)
.then(res => res.json())
.then(data => {
setComments(data.reviewInfo.reviewArray);
setBookInfoComments(data.reviewInfo.reviewArray);
});
});
};
// 댓글 데이터 가져오기
useEffect(() => {
fetch(`서버주소`)
.then(res => res.json())
.then(data => {
setComments(data.reviewInfo.reviewArray);
setBookInfoComments(data.reviewInfo.reviewArray);
});
});
댓글을 추가하면 백엔드로 데이터를 보내주고, 백에서 리뷰데이터를 가져와 보여주었다.
----------댓글 삭제
//comments.js
const onRemove = review_id => {
if (window.confirm('삭제 하시겠습니까?')) {
fetch(`서버주소`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
body: JSON.stringify({
review_id: review_id,
}),
})
.then(res => res.json())
.then(result => {
setComments(comments.filter(props => props.review_id !== review_id));
});
}
};
//comment.js
{visible && (
<button className={css.moreArea}
onClick={() => onRemove(review_id)}>
삭제하기
</button>
)}
삭제하기 버튼을 클릭하면 onRemove 함수가 실행 되고, filter메소드를 이용해서 해당 review_id와 다른 review_id들로 새로운 배열을 만들었다.

const copyUrl = () => {
let url = '';
let textarea = document.createElement('textarea');
document.body.appendChild(textarea);
url = window.document.location.href;
textarea.value = url;
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
alert('복사가 완료되었습니다.');
};
textarea의 value값에 지금 브라우저의 url을 넣어주고, document.execCommand('copy');로 클립보드에 복사하고, document.body.removeChild(textarea);로 필요없는 textarea를 제거했다.

useEffect(() => {
fetch(`서버주소`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
authorization: localStorage.getItem('token'),
},
})
.then(res => res.json())
.then(data => {
setBookDataList(data);
});
}, []);
<div className={css.bookContainer}>
<div className={css.bookBox}>
<div className={css.imgBox}>
{bookDataList.map((bookData, idx) => (
<img key={idx} src={bookData.cover_img} alt="책 이미지" />
))}
</div>
<div className={css.bookSupport}></div>
</div>
</div>
백엔드에서 찜 데이터, 서재담기 한 책의 데이터 가져온 후, 찜에 해당하는 책, 내 책장에 해당하는 책들의 이미지를 넣어주었다.
﹣ 두번째 프로젝트여서 그런지 한결 수월했던 것 같았다. 백엔드와의 연결, 내 파트 코드 작성, 에러 해결 등등 프로젝트 하는 기간 동안 많이 전과 비교해 성장했음을 느낄 수 있었다! 마지막으로 2주동안 함께 열심히 해준 밀리는서재 팀원들 모두 수고하셨고 감사했습니다~!🙇🏻♀️
겸둥아 우리의 품으로 돌아와주겠어? 넘 잘만드렀쟈너 😖 ❤️