Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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
264 changes: 242 additions & 22 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"react-dom": "^18.2.0",
"react-router-dom": "^6.8.1",
"react-scripts": "5.0.1",
"styled-components": "^5.3.11",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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 @@ -27,6 +28,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
50 changes: 50 additions & 0 deletions src/apis/api.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { removeCookie } from "../utils/cookie";
import { instance, instanceWithToken } from "./axios";

// Account 관련 API들
Expand Down Expand Up @@ -30,6 +31,26 @@ export const getUser = async () => {
return response.data;
};

export const getUserInfo = async () => {
const response = await instanceWithToken.get("/account/profile/");
if (response.status === 200) {
console.log("GET USERINFO SUCCESS");
} else {
console.log("[ERROR] error while updating userinfo");
}
return response.data;
};

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

// Post 관련 API들
export const getPosts = async () => {
const response = await instance.get("/post/");
Expand Down Expand Up @@ -130,3 +151,32 @@ export const deleteComment = async (id) => {
console.log("[ERROR] error while deleting comment");
}
};

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");
}
};

// 추가!!
22 changes: 15 additions & 7 deletions src/apis/axios.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import axios from "axios";
import { getCookie } from "../utils/cookie";
import { refreshToken } from "./api";

axios.defaults.baseURL = 'http://localhost:8000/api';
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');
axios.defaults.headers.post["Content-Type"] = "application/json";
axios.defaults.headers.common["X-CSRFToken"] = getCookie("csrftoken");

// 누구나 접근 가능한 API들
export const instance = axios.create();
Expand All @@ -15,7 +16,7 @@ export const instanceWithToken = axios.create();
instanceWithToken.interceptors.request.use(
// 요청을 보내기전 수행할 일
(config) => {
const accessToken = getCookie('access_token');
const accessToken = getCookie("access_token");

if (!accessToken) {
// token 없으면 리턴
Expand All @@ -37,13 +38,20 @@ instanceWithToken.interceptors.request.use(

instanceWithToken.interceptors.response.use(
(response) => {
// 서버 응답 데이터를 프론트에 넘겨주기 전 수행할 일
console.log("Interceptor Response!!");
return response;
},
(error) => {
// 서버가 오류를 응답했을 때 처리 - 콘솔 찍어주고, 프론트에게 보내지 않고 오류를 발생시킴
async (error) => {
console.log("Response 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);
}
);
4 changes: 3 additions & 1 deletion src/components/Comment/CommentElement.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ const CommentElement = (props) => {
month = month < 10 ? `0${month}` : month;
let day = date.getDate();
day = day < 10 ? `0${day}` : day;
const hour = date.getHours();
const minute = date.getMinutes();

const handleEditComment = () => {
updateComment(comment.id, { content: content });
Expand Down Expand Up @@ -44,7 +46,7 @@ const CommentElement = (props) => {
<p className="text-lg mr-4">{comment.content}</p>
)}
<span className="text-base mr-1 text-gray-300">
{year}.{month}.{day}
{year}.{month}.{day} {hour}:{minute}
</span>
</div>
{user?.id === comment.author ? (
Expand Down
2 changes: 1 addition & 1 deletion src/components/Form/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const SignUpForm = ({ formData, setFormData, handleSignUpSubmit }) => {
/>

<label htmlFor="confirm_password" className="label">
*passoword 확인:{" "}
*password 확인:{" "}
</label>
<input
required
Expand Down
12 changes: 9 additions & 3 deletions src/components/Header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useEffect, useState } from "react";
import lion from "../../assets/images/lion.jpeg";
import { Link } from "react-router-dom";
import { getCookie, removeCookie } from "../../utils/cookie";
import { logOut } from "../../apis/api";

const Header = () => {
const [isUser, setIsUser] = useState("");

Expand All @@ -11,9 +13,10 @@ const Header = () => {
}, []);

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

return (
<div
id="header-wrapper"
Expand All @@ -30,14 +33,17 @@ const Header = () => {
{!isUser ? (
<>
<Link to="/signin" className="mr-10 p-3 uppercase">
sign In
sign in
</Link>
<Link to="/signup" className="mr-10 p-3 uppercase">
sign up
</Link>
</>
) : (
<>
<Link to="/mypage" className="mr-5 p-3 uppercase">
my page
</Link>
<Link to="/" onClick={handleLogout} className="mr-10 p-3 uppercase">
log out
</Link>
Expand Down
18 changes: 0 additions & 18 deletions src/data/comments.js

This file was deleted.

9 changes: 9 additions & 0 deletions src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,12 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}

.underline-line {
border-bottom: 1px solid white;
width: 100%;
}

.custom-button {
height: 35px;
}
66 changes: 66 additions & 0 deletions src/routes/Home.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { useState, useEffect } from "react";
import { SmallPost } from "../components/Posts";
import posts from "../data/posts";

const Home = () => {
// hi my name is eojin
const [postList, setPostList] = useState(posts);
const [tags, setTags] = useState([]);
const [searchTags, setSearchTags] = useState([]);
const [searchValue, setSearchValue] = 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]);
}, []);

const handleChange = (e) => {
const { value } = e.target;
const newTags = tags.filter((tag) => tag.includes(value));
setSearchTags(newTags);
};

const handleTagFilter = (e) => {};

return (
<div>
<div className="flex flex-col justify-center items-center mb-5">
<div className="w-full h-72 pb-5 flex justify-center bg-[url('https://www.codelion.net/codelion_thumb.jpg')] bg-center bg-cover">
<h1 className="uppercase text-6xl text-white">my blog</h1>
</div>
<input
type="text"
placeholder="Tag Search"
onChange={handleChange}
className="border border-orange-400 outline-none rounded-2xl text-center py-2 px-20 text-orange-400 bg-transparent"
/>
<div className="flex mt-5">
{searchTags.map((tag) => {
return (
<button
key={tag}
className={tag === searchValue ? "tag active mr-2" : "tag mr-2"}
onClick={handleTagFilter}
>
#{tag}
</button>
);
})}
</div>
</div>

<div className="grid grid-cols-4 px-10 mt-10">
{postList.map((post) => (
<SmallPost key={post.id} post={post} />
))}
</div>
</div>
);
};

export default Home;
Loading