From 2e5ed385a8658b88449a74d6cd49259bf7b0cedb Mon Sep 17 00:00:00 2001 From: junjeong Date: Tue, 22 Apr 2025 15:52:14 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20HO=EC=9D=B4=EC=A7=80=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=ED=96=87=EC=9D=84=20?= =?UTF-8?q?=EA=B2=BD=EC=9A=B0=20StrokeBanner=20->=20ProifleContainer?= =?UTF-8?q?=EA=B0=80=20=EB=B3=B4=EC=9D=B4=EA=B2=8C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/axios.ts | 30 +++++++++++++++ src/components/Profile.tsx | 60 +++++++++++++++++++---------- src/components/ProfileContainer.tsx | 27 +++++++++++++ src/pages/Home.tsx | 45 +++++++++++++++++++--- src/store/useAuthStore.ts | 6 ++- src/types/virtualFreind.ts | 9 +++++ 6 files changed, 149 insertions(+), 28 deletions(-) create mode 100644 src/components/ProfileContainer.tsx create mode 100644 src/types/virtualFreind.ts diff --git a/src/api/axios.ts b/src/api/axios.ts index 8d93c97..8e6c154 100644 --- a/src/api/axios.ts +++ b/src/api/axios.ts @@ -1,4 +1,5 @@ import axios from "axios"; +import useAuthStore from "@/store/useAuthStore"; const instance = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, @@ -8,4 +9,33 @@ const instance = axios.create({ } }); +// 인증 절차가 필요한 API는 authInstance로 HTTP요청 +const authInstance = axios.create({ + baseURL: import.meta.env.VITE_API_BASE_URL, + timeout: 10000, + headers: { + "Content-Type": "application/json" + } +}); + +// authInstance 헤더에 accessToken 추가하는 로직 +authInstance.interceptors.request.use( + (config) => { + if (!config.headers) { + config.headers = {}; + } + + const { accessToken } = useAuthStore.getState(); + + if (accessToken) { + config.headers.Authorization = `Bearer ${accessToken}`; + } + return config; + }, + (error) => { + return Promise.reject(error); + } +); + export default instance; +export { authInstance }; diff --git a/src/components/Profile.tsx b/src/components/Profile.tsx index 2e02757..b8d9060 100644 --- a/src/components/Profile.tsx +++ b/src/components/Profile.tsx @@ -1,31 +1,51 @@ -const Profile = () => { - return ( -
- Delete +import { SetStateAction } from "react"; +import { authInstance } from "@/api/axios"; +import { VirtualFriend } from "@/types/virtualFreind"; +interface ProfileProps { + info: VirtualFriend; + deleteIndex: number; + setVirtualFriendList: React.Dispatch>; +} +const Profile = ({ info, deleteIndex, setVirtualFriendList }: ProfileProps) => { + const handleDelete = async () => { + const res = await authInstance.delete( + `/api/virtual-friend/${info.virtualFriendId}` + ); + if (res.status === 200) { + setVirtualFriendList((prevList) => + prevList.filter((_, index) => index !== deleteIndex) + ); + } + }; + return ( +
+ Profile -
-

- 김엠비 - ENTP +
+

+ {info.virtualFriendName} + {info.mbti}

-

- 20대 · 여자 · 직장동료 · 여행 · 사회생활 +

+ {info.virtualFriendAge} · {info.virtualFriendSex} ·{" "} + {info.virtualFriendRelationship}

- -

diff --git a/src/components/ProfileContainer.tsx b/src/components/ProfileContainer.tsx new file mode 100644 index 0000000..dd98c7d --- /dev/null +++ b/src/components/ProfileContainer.tsx @@ -0,0 +1,27 @@ +import Profile from "@/components/Profile"; +import { VirtualFriend } from "@/types/virtualFreind"; +import { SetStateAction } from "react"; + +interface ProfileContainerProps { + list: VirtualFriend[]; + setVirtualFriendList: React.Dispatch>; +} +const ProfileContainer = ({ + list, + setVirtualFriendList +}: ProfileContainerProps) => { + return ( +
+ {list.map((el, index) => ( + + ))} +
+ ); +}; + +export default ProfileContainer; diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 5c4165b..807bd37 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,32 +1,65 @@ +import { useEffect, useState } from "react"; +import { VirtualFriend } from "@/types/virtualFreind"; +import type { AxiosResponse } from "axios"; +import { authInstance } from "@/api/axios"; import Banner from "@/components/Banner"; import StrokeBanner from "@/components/StrokeBanner"; import SubTitle from "@/components/SubTitle"; import ChatStartButton from "@/components/button/ChatStartButton"; import Header from "@/components/header/Header"; +import Error from "@/pages/Error"; +import useAuthStore from "@/store/useAuthStore"; +import ProfileContainer from "@/components/ProfileContainer"; const Home = () => { + const { isLoggedIn } = useAuthStore(); + const [virtualFreindList, setVirtualFriendList] = useState( + [] + ); + + useEffect(() => { + console.log(virtualFreindList.length); + const fetchData = async () => { + try { + const res: AxiosResponse<{ data: VirtualFriend[] }> = + await authInstance.get("/api/virtual-friend"); + setVirtualFriendList(res.data.data); + } catch (err) { + console.error("친구 목록을 불러오지 못했습니다.", err); + return ; + } + }; + fetchData(); + }, []); return ( -
-
+
+
-
+
- +
-
+
- + {isLoggedIn && virtualFreindList.length > 0 ? ( + + ) : ( + + )}
diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts index a8ea6ae..4206b54 100644 --- a/src/store/useAuthStore.ts +++ b/src/store/useAuthStore.ts @@ -16,11 +16,13 @@ const useAuthStore = create( accessToken: null, login: async (code: string) => { try { - const res = await instance.get(`/api/kakao/login?code=${code}`); + const res = await instance.get( + `/api/kakao/login?code=${code}&redirectUrl=https://localhost:5173/kakao-login` + ); if (res.data) { set({ isLoggedIn: true, - accessToken: res.data as string + accessToken: res.data.data as string }); } } catch (error) { diff --git a/src/types/virtualFreind.ts b/src/types/virtualFreind.ts new file mode 100644 index 0000000..3292655 --- /dev/null +++ b/src/types/virtualFreind.ts @@ -0,0 +1,9 @@ +export interface VirtualFriend { + virtualFriendId: number; + conversationId: number; + mbti: string; + virtualFriendName: string; + virtualFriendAge: number; + virtualFriendSex: string; + virtualFriendRelationship: string; +} From 2fb297fe87017c8c13c5a4f14cba11471665d9f4 Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 17:16:58 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20Home=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=EB=90=98?= =?UTF-8?q?=EC=97=88=EB=8B=A4=EB=A9=B4=20=EC=B9=9C=EA=B5=AC=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EB=B6=88=EB=9F=AC=EC=98=A4=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index 807bd37..d3499a0 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -1,36 +1,37 @@ import { useEffect, useState } from "react"; -import { VirtualFriend } from "@/types/virtualFreind"; +import { useNavigate } from "react-router-dom"; import type { AxiosResponse } from "axios"; import { authInstance } from "@/api/axios"; +import { VirtualFriend } from "@/types/virtualFreind"; import Banner from "@/components/Banner"; import StrokeBanner from "@/components/StrokeBanner"; import SubTitle from "@/components/SubTitle"; import ChatStartButton from "@/components/button/ChatStartButton"; import Header from "@/components/header/Header"; -import Error from "@/pages/Error"; import useAuthStore from "@/store/useAuthStore"; import ProfileContainer from "@/components/ProfileContainer"; const Home = () => { + const navigate = useNavigate(); const { isLoggedIn } = useAuthStore(); const [virtualFreindList, setVirtualFriendList] = useState( [] ); useEffect(() => { - console.log(virtualFreindList.length); - const fetchData = async () => { + const fetchFriendList = async () => { try { const res: AxiosResponse<{ data: VirtualFriend[] }> = await authInstance.get("/api/virtual-friend"); setVirtualFriendList(res.data.data); } catch (err) { console.error("친구 목록을 불러오지 못했습니다.", err); - return ; + navigate("/error"); } }; - fetchData(); - }, []); + + if (isLoggedIn) fetchFriendList(); + }, [isLoggedIn]); return (
From e4c99b266d8e5c8d99638df89a582ceeb49dd0e1 Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 17:22:03 +0900 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20Profile=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EB=B2=84=ED=8A=BC=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=ED=99=9C=EC=84=B1=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Profile.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/Profile.tsx b/src/components/Profile.tsx index b8d9060..0dd39c6 100644 --- a/src/components/Profile.tsx +++ b/src/components/Profile.tsx @@ -1,4 +1,5 @@ import { SetStateAction } from "react"; +import { useNavigate } from "react-router-dom"; import { authInstance } from "@/api/axios"; import { VirtualFriend } from "@/types/virtualFreind"; @@ -8,6 +9,8 @@ interface ProfileProps { setVirtualFriendList: React.Dispatch>; } const Profile = ({ info, deleteIndex, setVirtualFriendList }: ProfileProps) => { + const navigate = useNavigate(); + const handleDelete = async () => { const res = await authInstance.delete( `/api/virtual-friend/${info.virtualFriendId}` @@ -18,6 +21,17 @@ const Profile = ({ info, deleteIndex, setVirtualFriendList }: ProfileProps) => { ); } }; + + const handleNavigate = () => { + navigate("/chat", { + state: { + mode: "virtualFriend", + mbti: info.mbti, + id: info.virtualFriendId + } + }); + }; + return (
-
From 2f24b2f1be816849b38f8924e34584f647a9d45d Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 17:35:11 +0900 Subject: [PATCH 4/4] =?UTF-8?q?chore:=20SubTitle=20navigate=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/SubTitle.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SubTitle.tsx b/src/components/SubTitle.tsx index d1343f9..bb165a4 100644 --- a/src/components/SubTitle.tsx +++ b/src/components/SubTitle.tsx @@ -15,8 +15,8 @@ const SubTitle = ({ mode }: { mode: "빠른대화" | "친구목록" }) => { }; const handleNavigate = () => { - const mode = "virtualFriend"; - navigate("/select-info", { state: mode }); + const type = mode === "빠른대화" ? "fastFriend" : "virtualFriend"; + navigate("/select-info", { state: { type: type } }); }; return (