diff --git a/package-lock.json b/package-lock.json index 5d7e156..a12f7fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@mui/material": "^5.14.18", "@types/jsonwebtoken": "^9.0.5", "axios": "^1.6.2", - "base-64": "^1.0.0", + "bcryptjs": "^2.4.3", "cookie": "^0.6.0", "js-cookie": "^3.0.5", "jsonwebtoken": "^9.0.2", @@ -26,6 +26,7 @@ "sass": "^1.69.5" }, "devDependencies": { + "@types/bcryptjs": "^2.4.6", "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^18", @@ -1037,6 +1038,12 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true + }, "node_modules/@types/js-cookie": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz", @@ -1562,10 +1569,10 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, "node_modules/binary-extensions": { "version": "2.2.0", diff --git a/package.json b/package.json index 34933a3..56c120c 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@mui/material": "^5.14.18", "@types/jsonwebtoken": "^9.0.5", "axios": "^1.6.2", - "base-64": "^1.0.0", + "bcryptjs": "^2.4.3", "cookie": "^0.6.0", "js-cookie": "^3.0.5", "jsonwebtoken": "^9.0.2", @@ -27,6 +27,7 @@ "sass": "^1.69.5" }, "devDependencies": { + "@types/bcryptjs": "^2.4.6", "@types/js-cookie": "^3.0.6", "@types/node": "^20", "@types/react": "^18", diff --git a/src/app/detail/page.tsx b/src/app/detail/page.tsx index 6238ce2..168a9eb 100644 --- a/src/app/detail/page.tsx +++ b/src/app/detail/page.tsx @@ -38,8 +38,7 @@ export default function Detail(): JSX.Element { const fetchData = async () => { try { const response = await axios.get( - // `https://www.jerneithe.site/board/detail/${boardDetail.boardId}`, - "https://www.jerneithe.site/board/detail/15", + `https://www.jerneithe.site/board/detail/${boardDetail.boardId}`, ); setBoardDetail(response.data); } catch (error) { @@ -109,7 +108,8 @@ export default function Detail(): JSX.Element { {decoded_nickName === boardDetail.nickName && (
+ className="ml-auto flex flex-col items-center" + > etc
diff --git a/src/app/mypage/page.tsx b/src/app/mypage/page.tsx index d0fb9f1..f67fca1 100644 --- a/src/app/mypage/page.tsx +++ b/src/app/mypage/page.tsx @@ -96,12 +96,23 @@ export default function Mypage() { const response = await axios.post( `https://www.jerneithe.site/user/api/profile`, - { email: "user94@test.com" } + { email: "user95@test.com" } ); setUserProfile(response.data); // 비밀번호 디코딩 - let pw_jwt: string = response.data.password; + // console.log("회원정보 pw: ", response.data.password); + // let pw_jwt = response.data.password; + // let pw_payload = pw_jwt.substring( + // pw_jwt.indexOf(".") + 1, + // pw_jwt.lastIndexOf(".") + // ); + + // console.log("pw_payload: ", pw_payload); + + // let pw_decode = base64.decode(pw_payload); + + // console.log("pw 디코딩: ", pw_decode); // 기존 것 // let password_jwt: string = response.data.password; @@ -112,7 +123,7 @@ export default function Mypage() { // const decoded_password = decodedPass?.sub; // setPassword(decoded_password); - console.log("postData: ", response.data); + console.log("회원정보 Data: ", response.data); } catch (error) { console.error("회원정보 에러: ", error); } @@ -120,22 +131,20 @@ export default function Mypage() { profileData(); }, []); - // --------------------------------------------------------- + // ------------------------------------------------------------------------ // board 이미지 데이터 불러오기 useEffect(() => { const postData = async () => { const req = await axios.get("https://www.jerneithe.site/board/list"); const data: FEEDATA[] = req.data; - const filteredData = data.filter((item) => item.nickName === "dongdong"); + const filteredData = data.filter((item) => item.nickName === "테스터"); console.log("filterData: ", filteredData); setMyPostData(filteredData); }; postData(); }, []); - console.log("디코딩 비번: ", password); - // 회원 정보 수정 모달 이벤트 const handleSettingsClick = () => { setShowProfileModify(!showProfileModify); @@ -155,12 +164,16 @@ export default function Mypage() { {/* ------------- 프로필 부분 ------------- */} {userPofile && ( <> - + )} {/* --------------------------------------- */} {/* ------------- tap 부분 ------------- */} - + {/* */} @@ -177,7 +190,7 @@ export default function Mypage() { handleSettingsClick={handleSettingsClick} email={userPofile.email} name={userPofile.name} - password={password} + password={userPofile.password} /> )} diff --git a/src/component/CommentModal.tsx b/src/component/CommentModal.tsx index 478f10e..184be66 100644 --- a/src/component/CommentModal.tsx +++ b/src/component/CommentModal.tsx @@ -2,6 +2,8 @@ import { useState } from "react"; import "../style/modal_comment.scss"; import Comment from "./Comment"; import Reply from "./Reply"; +import axios from "axios"; +import { Result } from "postcss"; // interface handleCommentClickProps { // handleCommentClick: () => void; @@ -12,11 +14,13 @@ interface CommentModalProps { } interface ReplyType { + id: number; text: string; createdAt: Date; } interface CommentType { + id: number; nickname: string; comment: string; reply: ReplyType[]; @@ -50,44 +54,121 @@ export default function CommentModal(props: CommentModalProps) { index: number ) => { const newReply = [...reply]; - newReply[index] = { text: e.target.value, createdAt: new Date() }; + newReply[index] = { id: 0, text: e.target.value, createdAt: new Date() }; setReply(newReply); }; - const handleFormSubmit = (e: React.FormEvent) => { + const handleFormSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (comment.trim() === "") { alert("댓글을 입력해주세요"); return; } - setComments([ - ...comments, - { - nickname: user.nickname, - comment: comment, - reply: [], - showReplyInput: false, - isDeleted: false, - createdAt: new Date(), - }, - ]); - setComment(""); + + try { + const comRes = await axios({ + method: "POST", + url: "https://www.jerneithe.site/comment/write", + headers: { + Authorization: + "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDEzOTI0NTUsImV4cCI6MTcwMTQwMzI1NSwic3ViIjoi7YWM7Iqk7YSwIn0.IW2xr6LD3aq0Wsxl1kBqce-YtxQBn4whGBAsrTDKNc0", + }, + data: { + boardId: 4, + content: comment, + }, + }); + + console.log("댓글 작성 응답: ", comRes.data); + + const comResData = comRes.data; + + setComments([ + ...comments, + { + id: comResData.id, // 댓글 아이디 + nickname: comResData.nickname, + comment: comment, + reply: [], + showReplyInput: false, + isDeleted: false, + createdAt: new Date(), + }, + ]); + setComment(""); + } catch (err) { + console.log("댓글 작성 에러: ", err); + } + + // 기존 코드 + // if (comment.trim() === "") { + // alert("댓글을 입력해주세요"); + // return; + // } + // setComments([ + // ...comments, + // { + // nickname: user.nickname, + // comment: comment, + // reply: [], + // showReplyInput: false, + // isDeleted: false, + // createdAt: new Date(), + // }, + // ]); + // setComment(""); }; - const handleReplySubmit = (e: React.FormEvent, index: number) => { + const handleReplySubmit = async (e: React.FormEvent, index: number) => { e.preventDefault(); if (!reply[index]?.text || reply[index]?.text.trim() === "") { alert("답글을 입력해주세요"); return; } - comments[index].reply.push(reply[index]); - setComments([...comments]); - setReply([ - ...reply.slice(0, index), - { text: "", createdAt: new Date() }, - ...reply.slice(index + 1), - ]); + try { + const repRes = await axios({ + method: "POST", + url: "https://www.jerneithe.site/comment/reply", + headers: { + Authorization: + "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDEzOTI0NTUsImV4cCI6MTcwMTQwMzI1NSwic3ViIjoi7YWM7Iqk7YSwIn0.IW2xr6LD3aq0Wsxl1kBqce-YtxQBn4whGBAsrTDKNc0", + }, + data: { + commentId: comments[index].id, // 답글을 작성할 댓글 ID + content: reply[index].text, // 답글 내용 + }, + }); + + console.log("답글 작성 응답: ", repRes.data); + + // 서버로부터 받은 답글 id를 저장함 + const newReply: ReplyType = { + id: repRes.data.id, // 답글 id 추가 + text: reply[index].text, + createdAt: new Date(), + }; + + comments[index].reply.push(newReply); + setComments([...comments]); + setReply([ + ...reply.slice(0, index), + { id: repRes.data.id, text: "", createdAt: new Date() }, + ...reply.slice(index + 1), + ]); + } catch (err) { + console.log(err); + } + + // 기존 코드 + // comments[index].reply.push(reply[index]); + // setComments([...comments]); + // setReply([ + // ...reply.slice(0, index), + // { text: "", createdAt: new Date() }, + // ...reply.slice(index + 1), + // ]); }; const handleReplyClick = (index: number) => { @@ -117,18 +198,79 @@ export default function CommentModal(props: CommentModalProps) { setEditText(e.target.value); }; - const handleEditSubmit = (e: React.FormEvent) => { + const handleEditSubmit = async (e: React.FormEvent) => { e.preventDefault(); + + // 댓글 수정 if (editing) { if (editing.type === "comment") { - comments[editing.index].comment = editText; - } else if (editing.type === "reply" && editing.replyIndex !== undefined) { - comments[editing.index].reply[editing.replyIndex].text = editText; + try { + const comEdit = await axios({ + method: "PATCH", + url: "https://www.jerneithe.site/comment/modify", + headers: { + Authorization: + "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDEzOTI0NTUsImV4cCI6MTcwMTQwMzI1NSwic3ViIjoi7YWM7Iqk7YSwIn0.IW2xr6LD3aq0Wsxl1kBqce-YtxQBn4whGBAsrTDKNc0", + }, + data: { + id: comments[editing.index].id, // 수정할 댓글 ID + content: editText, // 수정할 내용 + }, + }); + + console.log("댓글 수정 결과: ", comEdit); + + comments[editing.index].comment = editText; // 상태 업데이트 + setComments([...comments]); + setEditing(undefined); + setEditText(""); + } catch (err) { + console.log(err); + } + } + + // 답글 수정 + if (editing) { + if (editing.type === "reply" && editing.replyIndex !== undefined) { + try { + const repEditRes = await axios({ + method: "PATCH", + url: "https://www.jerneithe.site/comment/modify/reply", + headers: { + Authorization: + "Bearer eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE3MDEzOTI0NTUsImV4cCI6MTcwMTQwMzI1NSwic3ViIjoi7YWM7Iqk7YSwIn0.IW2xr6LD3aq0Wsxl1kBqce-YtxQBn4whGBAsrTDKNc0", + }, + data: { + id: comments[editing.index].reply[editing.replyIndex].id, // 수정할 답글 ID + content: editText, // 수정할 내용 + }, + }); + + console.log("답글 수정 결과: ", repEditRes.data); + + comments[editing.index].reply[editing.replyIndex].text = editText; // 상태 업데이트 + setComments([...comments]); + setEditing(undefined); + setEditText(""); + } catch (err) { + console.log(err); + } + } + // 나머지 코드... } - setComments([...comments]); - setEditing(undefined); - setEditText(""); } + + // 기존 코드 + // if (editing) { + // if (editing.type === "comment") { + // comments[editing.index].comment = editText; + // } else if (editing.type === "reply" && editing.replyIndex !== undefined) { + // comments[editing.index].reply[editing.replyIndex].text = editText; + // } + // setComments([...comments]); + // setEditing(undefined); + // setEditText(""); + // } }; const handleDelete = (index: number) => { @@ -146,6 +288,7 @@ export default function CommentModal(props: CommentModalProps) { setComments([...comments]); }; + // 시간 표시 데이터 const getTimeDiff = (date: Date) => { const now = new Date(); const diffInMilliseconds = now.getTime() - date.getTime(); @@ -169,6 +312,8 @@ export default function CommentModal(props: CommentModalProps) { nickname: "김똥이", }; + // ---------------------------------------------------------------------------------------------------------------------- + return ( <>
diff --git a/src/component/MyPost.tsx b/src/component/MyPost.tsx index 049966b..1c0d0c7 100644 --- a/src/component/MyPost.tsx +++ b/src/component/MyPost.tsx @@ -26,16 +26,34 @@ interface MyPostProps { export default function MyPost(props: MyPostProps) { const { myPostData } = props; - // const posts = [1, 2, 3, 4, 5, 6, 7]; - - // const [feedata, setFeedd] = useState([]); - // const email = "user91@test.com"; + console.log("mypost 데이터: ", myPostData); return (
- {/* {myPostData.map((item) => ( -
- {item.images ? ( + {myPostData.length > 0 ? ( + myPostData.map((item) => ( +
+ {item.images && ( + 코디 이미지 + )} +
+ )) + ) : ( + <> +

게시물을 등록해주세요.

+ + )} +
+ ); +} + +{ + /* {item.images ? ( 코디 이미지

게시물을 등록해주세요.

- )} -
- ))} */} -
- ); + )} */ } diff --git a/src/component/MypageProfile.tsx b/src/component/MypageProfile.tsx index 0d3d623..7402b39 100644 --- a/src/component/MypageProfile.tsx +++ b/src/component/MypageProfile.tsx @@ -3,9 +3,12 @@ import React, { useEffect, useState } from "react"; import { useRecoilState } from "recoil"; import AccountCircleOutlinedIcon from "@mui/icons-material/AccountCircleOutlined"; import { FeedContent } from "@/recoilAtom/FeedContents"; +import TabBar from "./TabBar"; interface MyPageProfileProps { nickname: string; + postnum: number; + myPostData: FEEDATA[]; } interface IMAGE { @@ -24,27 +27,7 @@ interface FEEDATA { } export default function MypageProfile(props: MyPageProfileProps) { - const { nickname } = props; - - // const [feedata, setFeedd] = useRecoilState(FeedContent); - - // useEffect(() => { - - // const feed_data = async() => { - // const req = await axios({ - // method: "GET", - // url: "https://www.jerneithe.site/board/list", - // }); - - // console.log("받아온 데이터", req.data); - - // const copy:FEEDATA[] = req.data; - - // console.log("카피" ,copy); - - // } - - // feed_data(); }, []); + const { nickname, postnum, myPostData } = props; return ( <> @@ -56,7 +39,7 @@ export default function MypageProfile(props: MyPageProfileProps) {

내 게시물

-

7

+

{postnum}

좋아요 한 게시물

@@ -64,6 +47,7 @@ export default function MypageProfile(props: MyPageProfileProps) {
+ ); } diff --git a/src/component/ProfileModal.tsx b/src/component/ProfileModal.tsx index 075030c..a7c09db 100644 --- a/src/component/ProfileModal.tsx +++ b/src/component/ProfileModal.tsx @@ -3,6 +3,7 @@ import "../style/modal.scss"; import AccountCircleOutlinedIcon from "@mui/icons-material/AccountCircleOutlined"; import CloseIcon from "@mui/icons-material/Close"; import axios from "axios"; +import bcrypt from "bcryptjs"; // 회원 정보 수정 모달 컴포넌트 @@ -10,7 +11,7 @@ interface handleSettingsClickProps { handleSettingsClick: () => void; email: string; name: string; - password: string | undefined; + password: string; } export default function ProfileModal(props: handleSettingsClickProps) { @@ -72,11 +73,18 @@ export default function ProfileModal(props: handleSettingsClickProps) { e.preventDefault(); // 현재 비밀번호 확인 - if (currentPassword !== password) { + const isPasswordMatch = await bcrypt.compare(currentPassword, password); + + if (!isPasswordMatch) { alert("현재 비밀번호를 다시 입력하세요."); return; } + // if (currentPassword !== password) { + // alert("현재 비밀번호를 다시 입력하세요."); + // return; + // } + // 변경 비밀번호 확인 if (newPassword !== confirmPassword) { alert("비밀번호 재확인을 다시 입력하세요."); @@ -136,7 +144,6 @@ export default function ProfileModal(props: handleSettingsClickProps) { {/* */}

이메일

-

비번: {password}

{email}
diff --git a/src/component/TestCommentModal.tsx b/src/component/TestCommentModal.tsx new file mode 100644 index 0000000..1cb2bad --- /dev/null +++ b/src/component/TestCommentModal.tsx @@ -0,0 +1 @@ +export default function TestCommentModal() {} diff --git a/src/style/modal_comment.scss b/src/style/modal_comment.scss index 682f136..bfbd902 100644 --- a/src/style/modal_comment.scss +++ b/src/style/modal_comment.scss @@ -16,10 +16,10 @@ position: fixed; bottom: 0px; height: 75%; - .comment_form { - position: fixed; - bottom: 30px; - } + // .comment_form { + // position: fixed; + // bottom: 30px; + // } .commentList { height: 80%; overflow-y: auto; diff --git a/src/style/mypage.scss b/src/style/mypage.scss index 5e7167e..a46c074 100644 --- a/src/style/mypage.scss +++ b/src/style/mypage.scss @@ -27,6 +27,7 @@ } .mypage_body { width: 90%; + height: 100%; // ------------ 프로필 부분 .user { display: flex; @@ -97,6 +98,7 @@ font-weight: bold; text-align: center; background-color: thistle; + position: relative; } } }