Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -28,6 +29,7 @@ function App() {
<Route path="/signup" element={<SignUpPage />} />
{/* sign up */}
<Route path="/signin" element={<SignInPage />} />
<Route path="/mypage" element={<MyPage />} />
</Routes>
<Footer />
</BrowserRouter>
Expand Down
39 changes: 38 additions & 1 deletion src/apis/api.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { instance, instanceWithToken } from "./axios";
import { removeCookie } from "../utils/cookie";

// Account 관련 API들
export const signIn = async (data) => {
Expand Down Expand Up @@ -135,9 +136,45 @@ export const deleteComment = async (id) => {
const response = await instanceWithToken.delete(`/comment/${id}/`);
if (response.status === 204) {
console.log("DELETE COMMENT SUCCESS");
window.location.reload();
// window.location.reload();
} else {
console.log("[ERROR] error while removing comment");
}
}
};


export const updateInfo = async (data) => {
const response = await instanceWithToken.patch("/account/info/", data);
if (response.status === 200) {
console.log("INFO UPDATE SUCCESS");
window.location.reload();
} else {
console.log("[ERROR] error while updating info");
}
};

export const refreshToken = async (token) => {
const response = await instance.post("/account/refresh/", { refresh: token });
if (response.status === 200) {
console.log("REFRESH TOKEN SUCCESS");
} else {
console.log("[ERROR] error while refreshing token");
}
};

export const logOut = async (token) => {
const response = await instanceWithToken.post("/account/logout/", {
refresh: token,
});
if (response.status === 204) {
console.log("REFRESH TOKEN SUCCESS");

removeCookie("refresh_token");
removeCookie("access_token");

window.location.reload();
} else {
console.log("[ERROR] error while refreshing token");
}
};
21 changes: 15 additions & 6 deletions src/apis/axios.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import axios from "axios";
import { getCookie } from "../utils/cookie";
import { getCookie, removeCookie } from "../utils/cookie";
import { refreshToken } from "./api";

// baseURL, credential, 헤더 세팅
axios.defaults.baseURL = 'http://localhost:8000/api';
Expand Down Expand Up @@ -39,16 +40,24 @@ instanceWithToken.interceptors.request.use(
}
);



instanceWithToken.interceptors.response.use(
(response) => {
// 서버 응답 데이터를 프론트에 넘겨주기 전 수행할 일
console.log("Interceptor Response!!");
return response;
},
(error) => {
// 서버가 오류를 응답했을 때 처리 - 콘솔 찍어주고, 프론트에게 보내지 않고 오류를 발생시킴
async (error) => {
console.log("Response Error!!");
console.log(error);

const originalRequest = error.config;
if (error.response.status === 401) { //토큰이 만료됨에 따른 에러인지 확인
const token = getCookie("refresh_token");
await refreshToken(token); //refresh token 을 활용하여 access token 을 refresh

return instanceWithToken(originalRequest); //refresh된 access token 을 활용하여 재요청 보내기
}
return Promise.reject(error);
}
);
);

2 changes: 1 addition & 1 deletion src/components/Comment/CommentElement.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const CommentElement = (props) => {
day = day < 10 ? `0${day}` : day;

// 추가
const handleEditComment = (e) => {
const handleEditComment = () => {
updateComment(comment.id, { content: content });
};
// updateComment 활용
Expand Down
12 changes: 8 additions & 4 deletions src/components/Header/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Link } from "react-router-dom";
import lion from "../../assets/images/lion.jpeg";
import { getCookie, removeCookie } from "../../utils/cookie";
import { getCookie } from "../../utils/cookie";
import { useEffect, useState } from "react";
import { logOut } from "../../apis/api";


const Header = () => {
const [isLoggedIn, setIsLoggedIn] = useState("");
Expand All @@ -14,9 +16,8 @@ const Header = () => {
// getCookie를 통해 access token을 가져올 수 있으면 로그인 된 것으로 설정

const handleLogout = () => {
removeCookie("access_token");
removeCookie("refresh_token");
window.location.href = "/"; // 새로고침 - 로그아웃 되었다는 것을 인지시켜주기 위해
const token = getCookie("refresh_token");
logOut(token);
};

return (
Expand All @@ -42,6 +43,9 @@ const Header = () => {
</>
) : (
<>
<Link to="/mypage" className="mr-10 p-3 uppercase">
MY PAGE
</Link>
<Link to="/" onClick={handleLogout} className="mr-10 p-3 uppercase">
log out
</Link>
Expand Down
58 changes: 58 additions & 0 deletions src/components/Info/InfoElement.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useEffect, useState } from "react";
import { updateInfo } from "../../apis/api";

export const InfoElement = (props) => {
const { infoLabel, infoContent } = props;
const [content, setContent] = useState(infoContent);
const [isEdit, setIsEdit] = useState(false);

const handleEditInfo = (e) => {
e.preventDefault();
updateInfo({ [infoLabel]: content });
setContent(infoContent);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기서 setContent를 한 이유가 무엇인가요?

};

useEffect(() => {
setContent(infoContent);
}, [infoContent]);
Comment on lines +15 to +17
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분이 왜 있는지 설명해주실 수 있나요?


return (
<div className="w-full flex-col mb-2">
<p>{infoLabel}: </p>
<div className="w-full flex mb-2 justify-between">
<div className="w-full">
{isEdit ? (
<div className="w-full flex flex-col items-center">
<input
className="input m-4"
value={content}
onChange={(e) => setContent(e.target.value)}
/>
<div className="flex items-center gap-4">
<button className="button" onClick={(e) => handleEditInfo(e)}>
수정하기
</button>
<button
className="button mr-3"
onClick={() => {
setIsEdit(!isEdit);
setContent(infoContent);
}}
>
취소하기
</button>
</div>
</div>
) : (
<div className="w-full flex flex-row justify-center p-2 mb-5 border-b-2">
<p className="w-full text-lg mr-5 p-3">{infoContent}</p>
<button className="button w-20" onClick={() => setIsEdit(!isEdit)}>
변경
</button>
</div>
)}
</div>
</div>
</div>
);
};
60 changes: 60 additions & 0 deletions src/routes/MyPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { useState, useEffect } from "react";
import { InfoElement } from "../components/Info/InfoElement";
import { getUser, getPosts } from "../apis/api";
import { SmallPost } from "../components/Posts";

const MyPage = () => {
const [email, setEmail] = useState("");
const [username, setUsername] = useState("");
const [college, setCollege] = useState("");
const [major, setMajor] = useState("");
Comment on lines +7 to +10
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

굳이 여러 state를 쓰지말고, 하나의 formdata에 넣는게 효율적으로 보입니다!

const [postList, setPostList] = useState([]);
const [user, setUser] = useState(null);

useEffect(() => {
const getPostsAPI = async () => {
const posts = await getPosts();
setPostList(posts);
console.log(posts);
};
getPostsAPI();
}, []);


useEffect(() => {
const getInfoAPI = async () => {
const info = await getUser();
setEmail(info.user.email);
setUsername(info.user.username);
setCollege(info.college);
setMajor(info.major);
Comment on lines +27 to +30
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
setEmail(info.user.email);
setUsername(info.user.username);
setCollege(info.college);
setMajor(info.major);
set�FormData({
email : info.user.email,
username: info.user.username,
college : info.college,
major : info.major,
});

위에서 이어진 내용으로, 저라면 이렇게 할 것 같습니다!

setUser(info.user);
};
getInfoAPI();
}, []);

return (
<>
<div className="flex flex-col items-center w-1/3">
<h3 className=" font-bold text-4xl mb-10">My Info</h3>
<InfoElement infoLabel="email" infoContent={email}/>
<InfoElement infoLabel="username" infoContent={username}/>
<InfoElement infoLabel="college" infoContent={college}/>
<InfoElement infoLabel="major" infoContent={major}/>
</div>
<div className="flex flex-col items-center mb-20 w-2/3">
<h3 className=" font-bold text-4xl mt-10 mb-10">My Posts</h3>
<div className="grid grid-cols-2 px-10 gap-4">
{postList.filter((post) => post?.author.id === user?.id
).map((post) => (
<SmallPost key={post.id} post={post} />
))}
</div>

</div>

</>
);
};

export default MyPage;
5 changes: 3 additions & 2 deletions src/routes/PostDetailPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const PostDetailPage = () => {
if (getCookie("access_token")) {
const getUserAPI = async () => {
const user = await getUser();
setUser(user);
setUser(user.user);
};
getUserAPI();
}
Expand All @@ -40,7 +40,8 @@ const PostDetailPage = () => {
const navigate = useNavigate();
const onClickDelete = () => {
deletePost(postId, navigate);
};
};


return (
post && (
Expand Down