diff --git a/package-lock.json b/package-lock.json
index cc88270..044defe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,10 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^1.4.0",
+ "lodash": "^4.17.21",
"react": "^18.2.0",
+ "react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.1",
"react-scripts": "5.0.1",
@@ -3820,6 +3823,11 @@
"@types/node": "*"
}
},
+ "node_modules/@types/cookie": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
+ "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
+ },
"node_modules/@types/eslint": {
"version": "8.37.0",
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz",
@@ -3873,6 +3881,15 @@
"@types/node": "*"
}
},
+ "node_modules/@types/hoist-non-react-statics": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+ "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
+ "dependencies": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ }
+ },
"node_modules/@types/html-minifier-terser": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz",
@@ -5096,6 +5113,29 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz",
+ "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==",
+ "dependencies": {
+ "follow-redirects": "^1.15.0",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
+ "node_modules/axios/node_modules/form-data": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
@@ -8702,6 +8742,19 @@
"he": "bin/he"
}
},
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics/node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+ },
"node_modules/hoopy": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz",
@@ -14078,6 +14131,11 @@
"node": ">= 0.10"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -14222,6 +14280,19 @@
"node": ">=14"
}
},
+ "node_modules/react-cookie": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.1.1.tgz",
+ "integrity": "sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==",
+ "dependencies": {
+ "@types/hoist-non-react-statics": "^3.0.1",
+ "hoist-non-react-statics": "^3.0.0",
+ "universal-cookie": "^4.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.3.0"
+ }
+ },
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
@@ -16266,6 +16337,23 @@
"node": ">=8"
}
},
+ "node_modules/universal-cookie": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
+ "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
+ "dependencies": {
+ "@types/cookie": "^0.3.3",
+ "cookie": "^0.4.0"
+ }
+ },
+ "node_modules/universal-cookie/node_modules/cookie": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/universalify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
diff --git a/package.json b/package.json
index ac7db0d..57c7a74 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,10 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
+ "axios": "^1.4.0",
+ "lodash": "^4.17.21",
"react": "^18.2.0",
+ "react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.1",
"react-scripts": "5.0.1",
diff --git a/src/App.js b/src/App.js
index 3e34fc7..9f752e2 100644
--- a/src/App.js
+++ b/src/App.js
@@ -9,6 +9,7 @@ import PostEditPage from "./routes/PostEditPage";
import SignUpPage from "./routes/SignUpPage";
import PostDetailPage from "./routes/PostDetailPage";
import SignInPage from "./routes/SignInPage";
+import MyPage from "./routes/MyPage";
function App() {
return (
@@ -26,8 +27,10 @@ function App() {
} />
{/* sign up */}
} />
- {/* sign up */}
+ {/* sign in */}
} />
+ {/* my page */}
+ } />
diff --git a/src/apis/api.js b/src/apis/api.js
new file mode 100644
index 0000000..5a9a12d
--- /dev/null
+++ b/src/apis/api.js
@@ -0,0 +1,170 @@
+import { instance, instanceWithToken } from "./axios";
+
+// signup and login
+export const signIn = async (data) => {
+ const response = await instance.post("/account/signin/", data);
+ if (response.status === 200) {
+ window.location.href = "/";
+ } else {
+ console.log("Error");
+ alert("로그인 실패");
+ }
+};
+
+export const signUp = async (data) => {
+ const response = await instance.post("/account/signup/", data);
+ if (response.status === 200) {
+ window.location.href = "/";
+ }
+ return response;
+};
+
+//posts
+export const getPosts = async () => {
+ const response = await instance.get("/post/");
+ console.log(response.data);
+ return response.data;
+};
+
+export const getPost = async (id) => {
+ const response = await instance.get(`/post/${id}/`);
+ return response.data;
+};
+
+export const createPost = async (data, navigate) => {
+ const response = await instanceWithToken.post("/post/", data);
+ if (response.status === 201) {
+ console.log("POST SUCCESS");
+ navigate("/");
+ } else {
+ console.log("[ERROR] error while creating post");
+ }
+};
+
+export const updatePost = async (id, data, navigate) => {
+ const response = await instanceWithToken.patch(`/post/${id}/`, data);
+ if (response.status === 200) {
+ console.log("POST UPDATE SUCCESS");
+ navigate(-1);
+ } else {
+ console.log("[ERROR] error while updating post");
+ }
+};
+
+// 과제!!
+export const deletePost = async (id, navigate) => {
+ const confirm = window.confirm("정말 삭제하시겠습니까?");
+ if (!confirm) return;
+
+ const response = await instanceWithToken.delete(`/post/${id}/`);
+ if (response.status === 204) {
+ console.log("POST DELETE SUCCESS");
+ navigate(-1);
+ } else {
+ console.log("[ERROR] error while deleting post");
+ }
+};
+
+// 과제!!
+export const likePost = async (postId) => {
+ const response = await instanceWithToken.post(`/post/${postId}/like/`);
+ if (response.status === 201) {
+ console.log("LIKE SUCCESS");
+ // window.location.reload();
+ } else {
+ console.log("[ERROR] error while liking post");
+ }
+ return response;
+};
+
+// tags
+export const getTags = async () => {
+ const response = await instance.get("/tag/");
+ return response.data;
+};
+
+export const createTag = async (data) => {
+ const response = await instanceWithToken.post("/tag/", data);
+ if (response.status === 201) {
+ console.log("TAG SUCCESS");
+ } else {
+ console.log("[ERROR] error while creating tag");
+ }
+ return response; // response 받아서 그 다음 처리
+};
+
+// comments
+export const getComments = async (postId) => {
+ const response = await instance.get(`/comment/?post=${postId}`);
+ return response.data;
+};
+
+export const createComment = async (data) => {
+ const response = await instanceWithToken.post("/comment/", data);
+ if (response.status === 201) {
+ console.log("COMMENT SUCCESS");
+ window.location.reload(); // 새로운 코멘트 생성시 새로고침으로 반영
+ } else {
+ console.log("[ERROR] error while creating comment");
+ }
+};
+
+export const updateComment = async (id, data) => {
+ const response = await instanceWithToken.patch(`/comment/${id}/`, data);
+ if (response.status === 200) {
+ console.log("COMMENT UPDATE SUCCESS");
+ window.location.reload();
+ } else {
+ console.log("[ERROR] error while updating comment");
+ }
+};
+
+// 과제 !!
+export const deleteComment = async (id) => {
+ const confirm = window.confirm("정말 삭제하시겠습니까?");
+ if (!confirm) return;
+
+ const response = await instanceWithToken.delete(`/comment/${id}/`);
+ if (response.status === 204) {
+ console.log("COMMENT DELETE SUCCESS");
+ window.location.reload();
+ } else {
+ console.log("[ERROR] error while deleting comment");
+ }
+};
+
+export const getUser = async () => {
+ const response = await instanceWithToken.get(`/account/info/`);
+ if (response.status === 200) {
+ // console.log("USER GET SUCCESS");
+ // // console.log(response.data);
+ } else {
+ console.log("[ERROR] error while getting user");
+ }
+ return response.data;
+};
+
+export const getUserProfile = async (id) => {
+ const response = await instanceWithToken.get(`/account/info/profile/`);
+ if (response.status === 200) {
+ // console.log("USER GET SUCCESS");
+ // console.log(response.data);
+ } else {
+ console.log("[ERROR] error while getting user profile");
+ }
+ return response.data;
+};
+
+export const updateUserProfile = async (data) => {
+ const response = await instanceWithToken.patch(
+ `/account/info/profile/`,
+ data
+ );
+ console.log(response.data, "request로 날린 userProfileInfo");
+ if (response.status === 200) {
+ console.log("USER UPDATE SUCCESS");
+ // window.location.reload();
+ } else {
+ console.log("[ERROR] error while updating user");
+ }
+};
diff --git a/src/apis/axios.js b/src/apis/axios.js
new file mode 100644
index 0000000..857476d
--- /dev/null
+++ b/src/apis/axios.js
@@ -0,0 +1,52 @@
+import axios from "axios";
+import { getCookie } from "../utils/cookie";
+
+// baseURL, credential, 헤더 세팅
+axios.defaults.baseURL = "http://localhost:8000/api";
+axios.defaults.withCredentials = true;
+axios.defaults.headers.post["Content-Type"] = "application/json";
+axios.defaults.headers.common["X-CSRFToken"] = getCookie("csrftoken");
+
+// 누구나 접근 가능한 API들
+export const instance = axios.create();
+
+// Token 있어야 접근 가능한 API들 - 얘는 토큰을 넣어줘야 해요
+export const instanceWithToken = axios.create();
+
+// instanceWithToken에는 쿠키에서 토큰을 찾고 담아줍시다!
+instanceWithToken.interceptors.request.use(
+ // 요청을 보내기전 수행할 일
+ // 사실상 이번 세미나에 사용할 부분은 이거밖에 없어요
+ (config) => {
+ const accessToken = getCookie("access_token");
+
+ if (!accessToken) {
+ // token 없으면 리턴
+ return;
+ } else {
+ // token 있으면 헤더에 담아주기 (Authorization은 장고에서 JWT 토큰을 인식하는 헤더 key)
+ config.headers["Authorization"] = `Bearer ${accessToken}`;
+ }
+ return config;
+ },
+
+ // 클라이언트 요청 오류 났을 때 처리
+ (error) => {
+ // 콘솔에 찍어주고, 요청을 보내지 않고 오류를 발생시킴
+ console.log("Request Error!!");
+ return Promise.reject(error);
+ }
+);
+
+instanceWithToken.interceptors.response.use(
+ (response) => {
+ // 서버 응답 데이터를 프론트에 넘겨주기 전 수행할 일
+ console.log("Interceptor Response!!");
+ return response;
+ },
+ (error) => {
+ // 서버가 오류를 응답했을 때 처리 - 콘솔 찍어주고, 프론트에게 보내지 않고 오류를 발생시킴
+ console.log("Response Error!!");
+ return Promise.reject(error);
+ }
+);
diff --git a/src/components/Comment/CommentElement.jsx b/src/components/Comment/CommentElement.jsx
index d4208cd..e3b7aa8 100644
--- a/src/components/Comment/CommentElement.jsx
+++ b/src/components/Comment/CommentElement.jsx
@@ -1,12 +1,14 @@
-import { useState } from "react";
+import { useEffect, useState } from "react";
+import { getUser, updateComment } from "../../apis/api";
+import { getCookie } from "../../utils/cookie";
+
+const CommentElement = (props) => {
+ const { comment, handleCommentDelete } = props;
+ const [content, setContent] = useState(comment.content);
+ const [isEdit, setIsEdit] = useState(false);
+
+ const [user, setUser] = useState(null);
-const CommentElement = ({
- key,
- comment,
- handleCommentChange,
- handleCommentDelete,
-}) => {
- // comment created_at 전처리
const date = new Date(comment.created_at);
const year = date.getFullYear();
let month = date.getMonth() + 1;
@@ -14,81 +16,71 @@ const CommentElement = ({
let day = date.getDate();
day = day < 10 ? `0${day}` : day;
- // edit 버튼 누른지 여부 확인
- const [isEdited, setIsEdited] = useState(false);
- // edit 인풋창 상태 반영함
- const [editedComment, setEditedComment] = useState(comment);
-
- // input 값의 value를 editedComment.content에 저장
- const handleInputChange = (e) => {
- const newContent = e.target.value;
- setEditedComment((prevComment) => ({
- ...prevComment,
- content: newContent,
- }));
+ // 추가
+ const handleEditComment = () => {
+ updateComment(comment.id, { content: content });
};
+ // updateComment 활용
- const onClickEdit = (e) => {
- if (isEdited === false) {
- setIsEdited(true);
- return;
+ useEffect(() => {
+ if (getCookie("access_token")) {
+ const getUserAPI = async () => {
+ const user = await getUser();
+ setUser(user);
+ };
+ getUserAPI();
}
- handleCommentChange(editedComment.id, editedComment.content);
- setIsEdited(false);
- };
-
- // props로 내려준 delete함수 실행
- const onClickDel = (e) => {
- handleCommentDelete(comment.id);
- };
+ }, []);
return (
-
-
- {/* 수정중일때 다르게 보여줌 */}
-
- {isEdited ? (
-
- ) : (
-
{comment.content}
- )}
-
-
- {/* 날짜 */}
-
- {year}.{month}.{day}
-
-
+
+ {isEdit ? (
+
setContent(e.target.value)}
+ />
+ ) : (
+
{comment.content}
+ )}
+
+ {year}.{month}.{day}
+
+
+ {/* 수정 */}
- {/* 버튼 */}
-
-
- {comment.author.username}
-
-
- {isEdited ? (
-
Del
- ) : (
-
Del
- )}
+ {user?.id === comment.author.id ? (
+
+ {isEdit ? (
+ <>
+ Done
-
- {isEdited ? "Done" : "Edit"}
-
-
+
{
+ setIsEdit(!isEdit);
+ setContent(comment.content);
+ }}
+ >
+ Back
+
+ >
+ ) : (
+ <>
+
handleCommentDelete(comment.id)}>
+ Del
+
+
setIsEdit(!isEdit)}>
+ Edit
+
+ >
+ )}
-
+ ) : null}
+
+ {/* 수정 */}
);
};
-
export default CommentElement;
diff --git a/src/components/Comment/index.jsx b/src/components/Comment/index.jsx
index cc60d7e..e53c8d0 100644
--- a/src/components/Comment/index.jsx
+++ b/src/components/Comment/index.jsx
@@ -1,17 +1,18 @@
import { useState, useEffect } from "react";
-import allComments from "../../data/comments";
import CommentElement from "./CommentElement";
+import { getComments, createComment, deleteComment } from "../../apis/api";
export const Comment = ({ post }) => {
- // data 폴더에 comments를 allComments로 불러오고, post 아이디에 적합한 comment만을 comments에 state로 저장
- const [comments, setComments] = useState([]);
-
+ // 1. comment list 관리
+ const [commentList, setCommentList] = useState([]);
+ console.log(post);
useEffect(() => {
- const commentList = allComments.filter(
- (comment) => comment.post === post.id
- );
- setComments([...commentList]);
- }, []);
+ const getCommentAPI = async (post) => {
+ const comments = await getComments(post);
+ setCommentList(comments);
+ };
+ getCommentAPI(post);
+ }, [post]);
// comment추가 하는 input 관리
const [newCommentInputValue, setNewCommentInputValue] = useState("");
@@ -23,59 +24,27 @@ export const Comment = ({ post }) => {
// comment Form 제출됐을때 실행되는 함수 만들어줘
- // * id number update위해서 tempIdNumber 선언
- const [tempIdNumber, setTempIdNumber] = useState(allComments.length + 1);
-
// * 새 comment submit되면 기존 코멘트 리스트인 comments에 넣어줌.
const onClickNewComment = (e) => {
- if (newCommentInputValue === "") {
- alert("댓글 내용을 입력해주세요.");
- return;
- }
- const currentTime = new Date();
- const currentTimeStr = currentTime.toISOString();
- const newComment = {
- id: tempIdNumber,
- content: newCommentInputValue,
- created_at: currentTimeStr,
- post: post,
- author: { id: allComments.length, username: "초기값(아무거나 넣어둠)" },
- };
- const newCommentList = [...comments, newComment];
- setComments(newCommentList);
+ e.preventDefault();
+ createComment({ post: post, content: newCommentInputValue });
setNewCommentInputValue("");
- setTempIdNumber((prevId) => prevId + 1); // 임시적으로 id number 1씩 증가시켜줌
};
// comment 삭제 - commentElement에서 요청할 때 id 같은거 삭제해줌
const handleCommentDelete = (commentId) => {
- const updatedArray = comments.filter((comment) => comment.id !== commentId);
- setComments(updatedArray);
- };
-
- // 5. CommentElement에서 edit되었을 때, comments 업데이트 해줌
- const handleCommentChange = (commentId, newContent) => {
- const updatedArray = comments.map((comment) => {
- if (commentId === comment.id) {
- const currentTime = new Date(); // 날짜도 업데이트 해줌
- const currentTimeStr = currentTime.toISOString();
- return { ...comment, content: newContent, created_at: currentTimeStr };
- }
- return comment;
- });
- setComments(updatedArray);
+ deleteComment(commentId);
};
return (
Comments
- {comments.map((comment) => {
+ {commentList.map((comment) => {
return (
);
diff --git a/src/components/Form/MyPageComponent.jsx b/src/components/Form/MyPageComponent.jsx
new file mode 100644
index 0000000..487e43d
--- /dev/null
+++ b/src/components/Form/MyPageComponent.jsx
@@ -0,0 +1,89 @@
+import { useEffect, useState } from "react";
+import { updateUserProfile } from "../../apis/api";
+import _ from "lodash";
+
+export const MyPageComponent = ({ dataInfo, userProfileInfo }) => {
+ // edit 버튼 눌렀는지 여부
+ const [isEditing, setIsEditing] = useState(false);
+ const [tempUserProfileInfo, setTempUserProfileInfo] =
+ useState(userProfileInfo);
+
+ const handleInput = (e) => {
+ const { id, value } = e.target;
+ // emai, username input 변경
+ if (id === "email" || id === "username") {
+ setTempUserProfileInfo({
+ ...userProfileInfo,
+ user: {
+ ...userProfileInfo.user,
+ [id]: value,
+ },
+ });
+ }
+ // college, major input 변경
+ if (id === "college" || id === "major") {
+ setTempUserProfileInfo({
+ ...userProfileInfo,
+ [id]: value,
+ });
+ }
+ };
+
+ const handleIsEditing = () => {
+ setIsEditing(!isEditing);
+ };
+
+ const handleBack = (e) => {
+ e.preventDefault();
+ setIsEditing(!isEditing);
+ setTempUserProfileInfo(userProfileInfo);
+ };
+
+ // 변경사항을 submit하면 실행되는 함수
+ const handleUpdate = (e) => {
+ e.preventDefault();
+ const updatedUserProfileInfo = tempUserProfileInfo;
+ // user가 변경되지 않았으면 user를 빼고 update (user 변경 없이 patch 요청 보내면 400 에러 발생)
+ if (_.isEqual(userProfileInfo.user, tempUserProfileInfo.user)) {
+ console.log("user 빠짐");
+ delete updatedUserProfileInfo.user;
+ } else {
+ console.log("user 안빠짐");
+ }
+ console.log(updatedUserProfileInfo, "update 날리기 직전 userProfileInfo");
+ updateUserProfile(updatedUserProfileInfo);
+ };
+
+ return (
+
+ {isEditing ? (
+
+ ) : (
+
+
{dataInfo}
+
+ {dataInfo === "email" || dataInfo === "username"
+ ? tempUserProfileInfo.user[dataInfo]
+ : tempUserProfileInfo[dataInfo]}
+
+
Edit
+
+ )}
+
+ );
+};
diff --git a/src/components/Form/index.jsx b/src/components/Form/index.jsx
index 6c7ab1c..50427a0 100644
--- a/src/components/Form/index.jsx
+++ b/src/components/Form/index.jsx
@@ -158,7 +158,7 @@ export const PostForm = ({ onSubmit, tags, formData, setFormData }) => {
setAutoCompletes([]);
};
- // 추가 버튼 혹인 엔터 누르면 태그 생성
+ // 추가 버튼 혹은 엔터 누르면 태그 생성
const addTag = (e) => {
e.preventDefault();
diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx
index c8b9907..346a526 100644
--- a/src/components/Header/index.jsx
+++ b/src/components/Header/index.jsx
@@ -1,8 +1,24 @@
+import { useEffect } from "react";
import lion from "../../assets/images/lion.jpeg";
import "./Header.css";
import { Link } from "react-router-dom";
+import { useState } from "react";
+import { getCookie, removeCookie } from "../../utils/cookie";
const Header = () => {
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+ useEffect(() => {
+ const isToken = getCookie("access_token") ? true : false;
+ setIsLoggedIn(isToken);
+ }, [isLoggedIn]);
+
+ const handleLogout = () => {
+ removeCookie("access_token");
+ removeCookie("refresh_token");
+ setIsLoggedIn(false);
+ window.location.href = "/";
+ };
+
return (
-
- sign in
-
-
- sign up
-
+ {isLoggedIn ? (
+ <>
+
+ My Page
+
+
+ log out
+
+ >
+ ) : (
+
+
+ sign in
+
+
+ sign up
+
+
+ )}
);
diff --git a/src/components/Posts/index.jsx b/src/components/Posts/index.jsx
index 653e039..0768087 100644
--- a/src/components/Posts/index.jsx
+++ b/src/components/Posts/index.jsx
@@ -1,33 +1,32 @@
import { Link } from "react-router-dom";
+import { likePost } from "../../apis/api";
import { useState } from "react";
export const SmallPost = ({ post }) => {
const [likeNum, setLikeNum] = useState(post.like_users.length);
- const [isLiked, setIsLiked] = useState(false);
- const onClickLike = () => {
- console.log("나도 좋아!");
- if (isLiked) {
- setLikeNum((prevNum) => prevNum - 1);
- setIsLiked(false);
- return;
- }
- setLikeNum((prevNum) => prevNum + 1);
- setIsLiked(true);
- // add api call for liking post here
+ const onClickLike = async () => {
+ const response = await likePost(post.id);
+ const data = response.data;
+ setLikeNum(data.like_users.length);
};
return (
{post.title}
{post.author.username}
-
+
{post.tags.map((tag) => (
#{tag.content}
))}
-
{likeNum > 0 && `❤️ ${likeNum}`}
+
+ ❤️ {likeNum}
+
detail
@@ -39,17 +38,10 @@ export const SmallPost = ({ post }) => {
export const BigPost = ({ post }) => {
const [likeNum, setLikeNum] = useState(post.like_users.length);
- const [isLiked, setIsLiked] = useState(false);
- const onClickLike = () => {
- console.log("나도 좋아!");
- if (isLiked) {
- setLikeNum((prevNum) => prevNum - 1);
- setIsLiked(false);
- return;
- }
- setLikeNum((prevNum) => prevNum + 1);
- setIsLiked(true);
- // add api call for liking post here
+ const onClickLike = async () => {
+ const response = await likePost(post.id);
+ const data = response.data;
+ setLikeNum(data.like_users.length);
};
return (
@@ -66,8 +58,8 @@ export const BigPost = ({ post }) => {
))}
-
- ❤️ {likeNum > 0 && `${likeNum}`}
+
+ ❤️ {likeNum}
diff --git a/src/routes/HomePage.jsx b/src/routes/HomePage.jsx
index ccc55fd..6b842e8 100644
--- a/src/routes/HomePage.jsx
+++ b/src/routes/HomePage.jsx
@@ -1,25 +1,36 @@
import { useEffect, useState } from "react";
import { SmallPost } from "../components/Posts";
import { Link } from "react-router-dom";
-import posts from "../data/posts";
+import { getPosts, getTags } from "../apis/api";
+import { getCookie } from "../utils/cookie";
const HomePage = () => {
const [tags, setTags] = useState([]);
const [searchTags, setSearchTags] = useState([]);
const [searchValue, setSearchValue] = useState("");
- const [postList, setPostList] = useState(posts);
+ const [postList, setPostList] = useState([]);
useEffect(() => {
- const tagList = posts.reduce((acc, post) => {
- for (let tag of post.tags) {
- acc.add(tag.content);
- }
- return acc;
- }, new Set());
- setTags([...tagList]);
- setSearchTags([...tagList]);
+ // TODO : api call(get all posts)
+ const getPostAPI = async () => {
+ const posts = await getPosts();
+ setPostList(posts);
+ };
+ getPostAPI();
+
+ // TODO : api call(get all tags)
+ const getTagAPI = async () => {
+ const tags = await getTags();
+ const tagContents = tags.map((tag) => {
+ return tag.content;
+ });
+ setTags(tagContents);
+ setSearchTags(tagContents);
+ };
+ getTagAPI();
}, []);
+ // TODO : catch change of input value
const handleChange = (e) => {
// console.log(e.target, e)
const { value } = e.target;
@@ -27,19 +38,15 @@ const HomePage = () => {
setSearchTags(newTags);
};
+ // TODO : catch click event of tag button(to filter posts)
const handleTagFilter = (e) => {
- const selectedTag = e.target.textContent.replace("#", "");
- console.log(selectedTag);
- if (searchValue !== selectedTag) {
- setSearchValue(selectedTag);
- const newPostList = posts.filter((post) =>
- post.tags.some((tag) => tag.content === selectedTag)
- );
- setPostList(newPostList);
- } else {
+ const { innerText } = e.target;
+ if (searchValue === innerText.substring(1)) {
setSearchValue("");
- setPostList(posts);
+ return;
}
+ const activeTag = innerText.substring(1);
+ setSearchValue(activeTag);
};
return (
@@ -52,6 +59,7 @@ const HomePage = () => {
type="text"
placeholder="Tag Search"
onChange={handleChange}
+ // if input value change, tags will be filtered automatically
className="border border-orange-400 outline-none rounded-2xl text-center py-2 px-20 text-orange-400 bg-transparent"
/>
@@ -70,15 +78,26 @@ const HomePage = () => {
- {postList.map((post) => (
-
- ))}
+ {
+ // if we click tag button(searchValue will be assigned), only posts with the tag will be shown
+ postList
+ .filter((post) =>
+ searchValue
+ ? post.tags.find((tag) => tag.content === searchValue)
+ : post
+ )
+ .map((post) => (
+
+ ))
+ }{" "}
-
- Post
-
+ {getCookie("access_token") ? (
+
+ Post
+
+ ) : null}
);
diff --git a/src/routes/MyPage.jsx b/src/routes/MyPage.jsx
new file mode 100644
index 0000000..dbaa73e
--- /dev/null
+++ b/src/routes/MyPage.jsx
@@ -0,0 +1,71 @@
+import { useState, useEffect } from "react";
+import { getCookie } from "../utils/cookie";
+import { getUserProfile, getPosts } from "../apis/api";
+import { MyPageComponent } from "../components/Form/MyPageComponent";
+import { SmallPost } from "../components/Posts";
+
+const MyPage = () => {
+ const [userPosts, setUserPosts] = useState([]); // [post1, post2, ...
+ const [userProfileInfo, setUserProfileInfo] = useState(null);
+
+ useEffect(() => {
+ if (getCookie("access_token")) {
+ const getUserProfileAPI = async () => {
+ const userProfile = await getUserProfile();
+ setUserProfileInfo(userProfile);
+ };
+ getUserProfileAPI();
+ }
+ }, []);
+
+ useEffect(() => {
+ // userProfileInfo를 받아오는게 완료되면 post 가져옴
+ if (userProfileInfo) {
+ const getPostsAPI = async () => {
+ const allPosts = await getPosts();
+ setUserPosts(
+ allPosts.filter((post) => post.author.id === userProfileInfo.user.id)
+ );
+ };
+ getPostsAPI();
+ }
+ }, []);
+
+ return (
+
+
+
My Page
+ <>
+ {userProfileInfo && (
+ <>
+
+
+
+
+ >
+ )}
+ >
+ {userPosts.length > 0 &&
+ userPosts.map((post) => )}
+
+
+ );
+};
+
+export default MyPage;
diff --git a/src/routes/PostCreatePage.jsx b/src/routes/PostCreatePage.jsx
index f4af6d3..18685cc 100644
--- a/src/routes/PostCreatePage.jsx
+++ b/src/routes/PostCreatePage.jsx
@@ -1,16 +1,14 @@
import { useEffect, useState } from "react";
-import { BigPost } from "../components/Posts";
-import posts from "../data/posts";
import { PostForm } from "../components/Form";
+import { getTags, createPost } from "../apis/api";
+import { useNavigate } from "react-router-dom";
const PostCreatePage = () => {
- const [isSubmitted, setIsSubmitted] = useState(false);
// 화면그리기
+ const navigate = useNavigate();
const [formData, setFormData] = useState({
- id: posts.length,
title: "",
content: "",
- author: { id: posts.length, username: "베이비" },
tags: [],
});
@@ -18,53 +16,32 @@ const PostCreatePage = () => {
// TODO : api call(get all tags)
const [tags, setTags] = useState([]);
useEffect(() => {
- const duplicatedTagList = posts.reduce((acc, post) => {
- for (let tag of post.tags) {
- acc.add(tag.content);
- }
-
- return acc;
- }, new Set());
-
- const tagList = [...duplicatedTagList];
-
- setTags([...tagList]);
+ const getTagsAPI = async () => {
+ const tags = await getTags();
+ const tagContents = tags.map((tag) => {
+ return tag.content;
+ });
+ setTags(tagContents);
+ };
+ getTagsAPI();
}, []);
const onSubmit = (e) => {
//TODO : api connect
e.preventDefault();
- console.log(formData);
-
- const createdPost = {
- ...formData,
- like_users: [],
- tags: formData.tags.map((tag, idx) => {
- return { id: idx + 1, content: tag };
- }),
- };
- setFormData(createdPost);
- setIsSubmitted(true);
+ createPost(formData, navigate);
};
return (
- <>
- {isSubmitted ? (
-
-
-
- ) : (
-
- )}
- >
+
);
};
diff --git a/src/routes/PostDetailPage.jsx b/src/routes/PostDetailPage.jsx
index d9fa18f..786150c 100644
--- a/src/routes/PostDetailPage.jsx
+++ b/src/routes/PostDetailPage.jsx
@@ -1,24 +1,42 @@
import { useEffect, useState } from "react";
-import { useParams } from "react-router-dom";
+import { useParams, useNavigate } from "react-router-dom";
import { BigPost } from "../components/Posts";
import { Link } from "react-router-dom";
import { Comment } from "../components/Comment";
-import posts from "../data/posts";
+import { getCookie } from "../utils/cookie";
+import { getPost, getUser, deletePost } from "../apis/api";
const PostDetailPage = () => {
// parameter로 받은 id에 해당하는 post를 찾아서 넣자
// TODO : api call(get post by id)
const { postId } = useParams();
const [post, setPost] = useState();
+ const [user, setUser] = useState();
+
useEffect(() => {
- const post = posts.find((post) => post.id === parseInt(postId));
- setPost(post);
+ const getPostAPI = async () => {
+ const post = await getPost(postId);
+ setPost(post);
+ };
+ getPostAPI();
}, [postId]);
+ useEffect(() => {
+ if (getCookie("access_token")) {
+ const getUserAPI = async () => {
+ const user = await getUser();
+ setUser(user);
+ };
+ getUserAPI();
+ }
+ }, []);
+
+ const navigate = useNavigate();
const onClickDelete = () => {
- console.log("delete");
- // add api call for deleting post here
- // add redirect to home page
+ const deletePostAPI = async () => {
+ await deletePost(postId, navigate);
+ };
+ deletePostAPI();
};
return (
@@ -26,18 +44,22 @@ const PostDetailPage = () => {
{/* post detail component */}
-
+
-
- Edit
-
-
- Delete
-
+ {user?.id === post?.author.id ? (
+ <>
+
+ Edit
+
+
+ Delete
+
+ >
+ ) : null}
)
diff --git a/src/routes/PostEditPage.jsx b/src/routes/PostEditPage.jsx
index 238b033..44dca87 100644
--- a/src/routes/PostEditPage.jsx
+++ b/src/routes/PostEditPage.jsx
@@ -1,76 +1,65 @@
import { useEffect, useState } from "react";
-import { useParams } from "react-router-dom";
-import { BigPost } from "../components/Posts";
-import posts from "../data/posts";
+import { useNavigate, useParams } from "react-router-dom";
import { PostForm } from "../components/Form";
import { Link } from "react-router-dom";
+import { getPost, getTags, updatePost } from "../apis/api";
const PostEditPage = () => {
- const [isSubmitted, setIsSubmitted] = useState(false);
const { postId } = useParams();
- const [formData, setFormData] = useState({});
+ const [formData, setFormData] = useState({
+ title: "",
+ content: "",
+ tags: [],
+ });
+ // 수정될 포스트 불러오기
useEffect(() => {
- const post = posts.find((post) => post.id === parseInt(postId));
- const postFormData = { ...post, tags: post.tags.map((tag) => tag.content) };
- setFormData(postFormData);
+ const getPostAPI = async () => {
+ const post = await getPost(postId);
+ const postFormData = {
+ ...post,
+ tags: post.tags.map((tag) => tag.content),
+ };
+ setFormData(postFormData);
+ };
+ getPostAPI();
}, [postId]);
// 기존 태그 불러오기
// TODO : api call(get all tags)
const [tags, setTags] = useState([]);
useEffect(() => {
- const duplicatedTagList = posts.reduce((acc, post) => {
- for (let tag of post.tags) {
- acc.add(tag.content);
- }
-
- return acc;
- }, new Set());
-
- const tagList = [...duplicatedTagList];
-
- setTags([...tagList]);
+ const getTagsAPI = async () => {
+ const tags = await getTags();
+ const tagContents = tags.map((tag) => tag.content);
+ setTags(tagContents);
+ };
+ getTagsAPI();
}, []);
+ const navigate = useNavigate();
const onSubmit = (e) => {
//TODO : api connect
e.preventDefault();
- console.log(formData);
-
- const createdPost = {
- ...formData,
- like_users: [],
- tags: formData.tags.map((tag, idx) => {
- return { id: idx + 1, content: tag };
- }),
- };
- setFormData(createdPost);
- setIsSubmitted(true);
+ updatePost(postId, formData, navigate);
};
return (
<>
- {isSubmitted ? (
-
-
-
- ) : (
-
-
-
- {`< Back`}
-
-
Edit Post
-
-
+
+
+
+ {`< Back`}
+
+
Edit Post
- )}
+
+
>
);
};
diff --git a/src/routes/SignInPage.jsx b/src/routes/SignInPage.jsx
index 8374e50..61b197f 100644
--- a/src/routes/SignInPage.jsx
+++ b/src/routes/SignInPage.jsx
@@ -1,5 +1,6 @@
import { useState } from "react";
import { SignInForm } from "../components/Form";
+import { signIn } from "../apis/api";
const SignInPage = () => {
const [formData, setFormData] = useState({
@@ -7,10 +8,10 @@ const SignInPage = () => {
password: "",
});
- const handleSignInSubmit = () => {
+ const handleSignInSubmit = (e) => {
+ e.preventDefault();
+ signIn(formData);
console.log(formData);
- alert("로그인 완 료!");
- // add api call for sign in here
};
return (
diff --git a/src/routes/SignUpPage.jsx b/src/routes/SignUpPage.jsx
index 79fd416..3789eed 100644
--- a/src/routes/SignUpPage.jsx
+++ b/src/routes/SignUpPage.jsx
@@ -1,5 +1,6 @@
import { useState } from "react";
import { SignUpForm } from "../components/Form";
+import { signUp } from "../apis/api";
const SignUpPage = () => {
const [formData, setFormData] = useState({
@@ -11,10 +12,10 @@ const SignUpPage = () => {
major: "",
});
- const handleSignUpSubmit = () => {
+ const handleSignUpSubmit = (e) => {
+ e.preventDefault();
+ signUp(formData);
console.log(formData);
- alert(`${formData.email}로 회원가입 해 줘`);
- // add api call for sign up here
};
return (
diff --git a/src/utils/cookie.js b/src/utils/cookie.js
new file mode 100644
index 0000000..158d822
--- /dev/null
+++ b/src/utils/cookie.js
@@ -0,0 +1,19 @@
+import { Cookies } from "react-cookie";
+
+const cookies = new Cookies();
+
+// 쿠키 설정하는 함수
+// 궁금하실까봐 만들긴 했는데, 우리는 안 쓸거에요!! (쿠키에 토큰 넣어주는 건 서버에서 해주니까요)
+export const setCookie = (name, value, option) => {
+ return cookies.set(name, value, { ...option });
+};
+
+// 쿠키 정보 가져오는 함수
+export const getCookie = (name) => {
+ return cookies.get(name);
+};
+
+// 쿠키 정보 삭제하는 함수
+export const removeCookie = (name) => {
+ cookies.remove(name);
+};