From 4812cd914a5ed864f59200767bad16df8b3c59dc Mon Sep 17 00:00:00 2001 From: junjeong Date: Thu, 10 Apr 2025 15:53:42 +0900 Subject: [PATCH 01/32] =?UTF-8?q?feat:=20=EA=B3=B5=EC=9C=A0=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20ui=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icon/Instagram.svg | 15 +++++++++++++++ public/icon/close.svg | 5 ++++- public/icon/kakaotalk.svg | 15 +++++++++++++++ src/components/button/UrlCopyBar.tsx | 14 ++++++++++++++ src/components/button/UrlCopyButton.tsx | 23 +++++++++++++++++++++++ src/components/modal/ShareModal.tsx | 21 +++++++++++++++++++++ src/main.tsx | 2 +- 7 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 public/icon/Instagram.svg create mode 100644 public/icon/kakaotalk.svg create mode 100644 src/components/button/UrlCopyBar.tsx create mode 100644 src/components/button/UrlCopyButton.tsx create mode 100644 src/components/modal/ShareModal.tsx diff --git a/public/icon/Instagram.svg b/public/icon/Instagram.svg new file mode 100644 index 0000000..364adac --- /dev/null +++ b/public/icon/Instagram.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/icon/close.svg b/public/icon/close.svg index 5f7e135..b3751f1 100644 --- a/public/icon/close.svg +++ b/public/icon/close.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + + diff --git a/public/icon/kakaotalk.svg b/public/icon/kakaotalk.svg new file mode 100644 index 0000000..a40a8bd --- /dev/null +++ b/public/icon/kakaotalk.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/components/button/UrlCopyBar.tsx b/src/components/button/UrlCopyBar.tsx new file mode 100644 index 0000000..670a682 --- /dev/null +++ b/src/components/button/UrlCopyBar.tsx @@ -0,0 +1,14 @@ +import UrlCopyButton from "./UrlCopyButton"; + +const UrlCopyBar = () => { + const currentUrl = window.location.href; + + return ( +
+ {currentUrl} + +
+ ); +}; + +export default UrlCopyBar; \ No newline at end of file diff --git a/src/components/button/UrlCopyButton.tsx b/src/components/button/UrlCopyButton.tsx new file mode 100644 index 0000000..f174e38 --- /dev/null +++ b/src/components/button/UrlCopyButton.tsx @@ -0,0 +1,23 @@ +const UrlCopyButton = ({currentUrl} : {currentUrl : string}) => { + const handleCopy = () => { + navigator.clipboard + .writeText(currentUrl) + .then(() => { + alert("URL이 복사되었습니다!"); // toast로 바꾸어야 함 -> 4.10 정준영 + }) + .catch((err) => { + console.error("URL 복사 실패:", err); + }); + }; + + return ( + + ); + }; + + export default UrlCopyButton; \ No newline at end of file diff --git a/src/components/modal/ShareModal.tsx b/src/components/modal/ShareModal.tsx new file mode 100644 index 0000000..cf54f9e --- /dev/null +++ b/src/components/modal/ShareModal.tsx @@ -0,0 +1,21 @@ +import UrlCopyBar from "@/components/button/UrlCopyBar"; + +const ShareModal = () => { + return ( +
+
+

게시글 공유

+
+ 카카오통 공유하기 버튼 + 인스타그램 공유하기 버튼 +
+ 닫기 버튼 +
+ +
+
+
+ ); +}; + +export default ShareModal; \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 12fa35b..f8cfbf0 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,6 +5,6 @@ import App from "./App"; createRoot(document.getElementById("root")!).render( - + ); From d15ba037afc4ee7e57e21a49159c6c52338865ab Mon Sep 17 00:00:00 2001 From: junjeong Date: Mon, 14 Apr 2025 13:52:36 +0900 Subject: [PATCH 02/32] =?UTF-8?q?feat:=20kakao=20=EA=B3=B5=EC=9C=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 1 + src/components/button/KakaoShareButton.tsx | 45 +++++++++++++++++ src/components/modal/ShareModal.tsx | 58 ++++++++++++++++------ src/global.d.ts | 4 ++ src/main.tsx | 4 +- vite.config.ts | 4 +- 6 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 src/components/button/KakaoShareButton.tsx create mode 100644 src/global.d.ts diff --git a/index.html b/index.html index f1b5528..6a448a2 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@
+ diff --git a/src/components/button/KakaoShareButton.tsx b/src/components/button/KakaoShareButton.tsx new file mode 100644 index 0000000..3f24be0 --- /dev/null +++ b/src/components/button/KakaoShareButton.tsx @@ -0,0 +1,45 @@ +import { useEffect } from "react"; + +interface KakoShareButtonProps { + title : string; + description : string; + imageUrl : string; +} + +const KakaoShareButton = ({title, description, imageUrl} : KakoShareButtonProps) => { + useEffect(() => { + // 카카오톡 SDK 초기화 + if (window.Kakao && !window.Kakao.isInitialized()) { + window.Kakao.cleanup(); + window.Kakao.init(import.meta.env.VITE_KAKAO_APP_KEY); + } + }, []); + + const handleShare = () => { + if (window.Kakao) { + // 카카오톡 공유 기능 호출 + window.Kakao.Share.sendDefault({ + objectType: 'feed', + content: { + title: title, + description: description, + imageUrl: + imageUrl, + link: { + mobileWebUrl: window.location.href, + webUrl: window.location.href, + }, + }, + }); + } + }; + return ( +
+ +
+ ); +}; + +export default KakaoShareButton; \ No newline at end of file diff --git a/src/components/modal/ShareModal.tsx b/src/components/modal/ShareModal.tsx index cf54f9e..47dbdb4 100644 --- a/src/components/modal/ShareModal.tsx +++ b/src/components/modal/ShareModal.tsx @@ -1,21 +1,51 @@ import UrlCopyBar from "@/components/button/UrlCopyBar"; +import { MouseEvent } from "react"; +import KakaoShareButton from "../button/KakaoShareButton"; -const ShareModal = () => { +interface ShareModalProps { + title: string; + description: string; + imageUrl: string; + closeModal: (e: MouseEvent) => void; +} + +const ShareModal = ({ + title, + description, + imageUrl, + closeModal +}: ShareModalProps) => { return ( -
-
-

게시글 공유

-
- 카카오통 공유하기 버튼 - 인스타그램 공유하기 버튼 -
- 닫기 버튼 -
- -
-
+
+
+

+ 게시글 공유 +

+
+ + +
+ +
+ +
+
); }; -export default ShareModal; \ No newline at end of file +export default ShareModal; diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..ec76eb5 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,4 @@ +// TypeScript 사용 시에는 TypeScript 가 window객체에 존재하는 Kakao객체를 인식할 수 있도록 src내에 global.d.ts를 설정해줘야 오류가 발생하지 않는다. +interface Window { + Kakao: any; +} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index f8cfbf0..cff6bc0 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -2,9 +2,11 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; import App from "./App"; +import ShareModal from "./components/modal/ShareModal"; createRoot(document.getElementById("root")!).render( - + {/* */} + ); diff --git a/vite.config.ts b/vite.config.ts index 94140d3..61d7be4 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -14,11 +14,11 @@ export default defineConfig(({ mode }: { mode: string }) => { return { server: { - port: 3000, + // port: 3000, host: true, // 외부에서 접속 가능하도록 설정 strictPort: true, allowedHosts: ["mbtips.kr"], - hmr: isProduction + hmr: isProduction ? false : { host: "localhost", From c210f143148dda884e5721999c004ff8740ed155 Mon Sep 17 00:00:00 2001 From: junjeong Date: Mon, 14 Apr 2025 14:04:27 +0900 Subject: [PATCH 03/32] =?UTF-8?q?feat:=20kakao=20=EA=B3=B5=EC=9C=A0=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoShareButton.tsx | 61 ++++++++++++---------- src/components/button/UrlCopyBar.tsx | 6 +-- src/components/modal/ShareModal.tsx | 5 +- src/main.tsx | 4 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/components/button/KakaoShareButton.tsx b/src/components/button/KakaoShareButton.tsx index 3f24be0..ad263b4 100644 --- a/src/components/button/KakaoShareButton.tsx +++ b/src/components/button/KakaoShareButton.tsx @@ -1,38 +1,41 @@ import { useEffect } from "react"; interface KakoShareButtonProps { - title : string; - description : string; - imageUrl : string; + title: string; + description: string; + imageUrl: string; } -const KakaoShareButton = ({title, description, imageUrl} : KakoShareButtonProps) => { - useEffect(() => { - // 카카오톡 SDK 초기화 - if (window.Kakao && !window.Kakao.isInitialized()) { - window.Kakao.cleanup(); - window.Kakao.init(import.meta.env.VITE_KAKAO_APP_KEY); - } - }, []); +const KakaoShareButton = ({ + title, + description, + imageUrl +}: KakoShareButtonProps) => { + useEffect(() => { + // 카카오톡 SDK 초기화 + if (window.Kakao && !window.Kakao.isInitialized()) { + window.Kakao.cleanup(); + window.Kakao.init(import.meta.env.VITE_KAKAO_REST_API_KEY); + } + }, []); - const handleShare = () => { - if (window.Kakao) { - // 카카오톡 공유 기능 호출 - window.Kakao.Share.sendDefault({ - objectType: 'feed', - content: { - title: title, - description: description, - imageUrl: - imageUrl, - link: { - mobileWebUrl: window.location.href, - webUrl: window.location.href, - }, - }, - }); + const handleShare = () => { + if (window.Kakao) { + // 카카오톡 공유 기능 호출 + window.Kakao.Share.sendDefault({ + objectType: "feed", + content: { + title: title, + description: description, + imageUrl: imageUrl, + link: { + mobileWebUrl: window.location.href, + webUrl: window.location.href + } } - }; + }); + } + }; return (
-
{ const handleConfirm = () => { if (modalType === "logout") { + trackEvent("Click", { + page: "내 정보", + element: "로그아웃" + }); logout(); navigate("/login"); } else if (modalType === "withdraw") { + trackEvent("Click", { + page: "내 정보", + element: "회원탈퇴" + }); console.log("회원탈퇴 실행"); //TODO: 회원탈퇴 기능 구현 시 추가 필요 } setModalType(null); diff --git a/src/pages/SelectInfo.tsx b/src/pages/SelectInfo.tsx index f7a1e99..28f795c 100644 --- a/src/pages/SelectInfo.tsx +++ b/src/pages/SelectInfo.tsx @@ -5,6 +5,7 @@ import Header from "@/components/header/Header"; import { getMBTIgroup, mapAgeToNumber } from "@/utils/helpers"; import instance from "@/api/axios"; import ToastMessage from "@/components/ToastMessage"; +import { trackEvent } from "@/libs/analytics"; type FastFriendResponse = { header: { @@ -182,6 +183,10 @@ const SelectInfo = () => { const responseData = response.data.data; if (mode === "virtualFriend" && isVirtualFriendResponse(responseData)) { + trackEvent("Click", { + page: "친구 저장", + element: "대화 시작하기" + }); navigate("/chat", { state: { mbti, @@ -190,6 +195,10 @@ const SelectInfo = () => { } }); } else if (mode === "fastFriend" && typeof responseData === "number") { + trackEvent("Click", { + page: "빠른 대화 설정", + element: "대화 시작하기" + }); navigate("/chat", { state: { mbti, From 18f5eb78e4ee454e182969825fb4507fb542ed13 Mon Sep 17 00:00:00 2001 From: soohyuniii Date: Mon, 21 Apr 2025 13:56:26 +0900 Subject: [PATCH 08/32] =?UTF-8?q?fix:=20GA=20pageview=20element=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 26 +++++++++++++------------- src/libs/analytics.ts | 9 ++------- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index b2de307..9c0a221 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,36 +26,36 @@ const PageTracker = () => { const { pathname, state } = location; const trackedPaths = [ - { path: "/", page: "홈", element: "" }, - { path: "/login", page: "로그인/회원가입", element: "로그인" }, - { path: "/contents", page: "일반콘텐츠", element: "" }, - { path: "/my-info", page: "내 정보", element: "" }, - { path: "/chat", page: "채팅방", element: "" }, - { path: "/select-info", page: "빠른 대화 설정", element: "" }, - { path: "/select-info", page: "친구 저장", element: "대화 시작하기" } + { path: "/", page: "홈" }, + { path: "/login", page: "로그인/회원가입" }, + { path: "/contents", page: "일반콘텐츠" }, + { path: "/my-info", page: "내 정보" }, + { path: "/chat", page: "채팅방" }, + { path: "/select-info", page: "빠른 대화 설정" }, + { path: "/select-info", page: "친구 저장" } ]; useEffect(() => { const trackedContentPaths = ["/contents/1", "/contents/2"]; - trackedPaths.forEach(({ path, page, element }) => { + trackedPaths.forEach(({ path, page }) => { // 콘텐츠 상세 페이지 (일반 콘텐츠만 추적) if (trackedContentPaths.includes(pathname)) { if (path === "/contents") { - trackPageView(path, { page, element }); + trackPageView(path, page); } } // select-info 페이지에서 state로 분기 else if (pathname === "/select-info" && path === pathname) { if (state === "fastFriend" && page === "빠른 대화 설정") { - trackPageView(path, { page, element }); + trackPageView(path, page); } else if (state === "virtualFriend" && page === "친구 저장") { - trackPageView(path, { page, element }); + trackPageView(path, page); } } // 나머지 일반 path else if (pathname === path && path !== "/select-info") { - trackPageView(path, { page, element }); + trackPageView(path, page); } }); }, [location.pathname, location.state]); @@ -84,7 +84,7 @@ const App = () => { } /> } /> } /> - } /> + } /> } /> } /> diff --git a/src/libs/analytics.ts b/src/libs/analytics.ts index 667c669..a89920a 100644 --- a/src/libs/analytics.ts +++ b/src/libs/analytics.ts @@ -5,27 +5,22 @@ const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID || ""; const isProduction = import.meta.env.MODE === "production"; export const initGA = () => { - console.log("isProduction", isProduction); - console.log("ID", GA_MEASUREMENT_ID); if (isProduction && GA_MEASUREMENT_ID) { - console.log("initGA"); ReactGA.initialize(GA_MEASUREMENT_ID); } }; -export const trackPageView = (url: string, params?: Record) => { +export const trackPageView = (url: string, page: string) => { if (isProduction && GA_MEASUREMENT_ID) { - console.log("trackPageView"); ReactGA.gtag("event", "page_view", { page_path: url, - ...params + page: page }); } }; export const trackEvent = (name: string, params?: Record) => { if (isProduction && GA_MEASUREMENT_ID) { - console.log("trackEvent"); ReactGA.event(name, params); } }; From edede06e18da2bf84dd500ac6136e83e3f0af107 Mon Sep 17 00:00:00 2001 From: junjeong Date: Mon, 21 Apr 2025 14:32:41 +0900 Subject: [PATCH 09/32] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=95=88=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=201=EC=B0=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoLoginButton.tsx | 4 ++-- src/pages/KaKaoLogin.tsx | 4 ++-- src/store/useAuthStore.ts | 19 ++++++++++++------- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/components/button/KakaoLoginButton.tsx b/src/components/button/KakaoLoginButton.tsx index 426f22d..4eaf4f9 100644 --- a/src/components/button/KakaoLoginButton.tsx +++ b/src/components/button/KakaoLoginButton.tsx @@ -1,5 +1,5 @@ const KakaoLoginButton = () => { - const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=${import.meta.env.VITE_KAKAO_REST_API_KEY}&redirect_uri=${import.meta.env.VITE_KAKAO_REDIRECT_URI}&response_type=code`; + const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=3e5cfa29037d1bd11eb5448f9b298bfe&redirect_uri=https://localhost:5173/kakao-login&response_type=code`; const handleClick = () => { window.location.href = kakaoURL; @@ -9,7 +9,7 @@ const KakaoLoginButton = () => { diff --git a/src/pages/KaKaoLogin.tsx b/src/pages/KaKaoLogin.tsx index a23f734..508f6ec 100644 --- a/src/pages/KaKaoLogin.tsx +++ b/src/pages/KaKaoLogin.tsx @@ -11,8 +11,8 @@ const KaKaoLogin = () => { const getTokenAndLogin = async () => { if (typeof code === "string") { try { - await login(code); - navigate("/"); + const res = await login(code); + if (res.ok) navigate("/"); } catch (err) { console.error("카카오 로그인에 실패했습니다."); } diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts index a8ea6ae..2feff47 100644 --- a/src/store/useAuthStore.ts +++ b/src/store/useAuthStore.ts @@ -5,7 +5,7 @@ import instance from "@/api/axios"; interface AuthStore { isLoggedIn: boolean; accessToken: string | null; - login: (code: string) => Promise; + login: (code: string) => Promise<{ ok: boolean }>; logout: () => void; } @@ -17,14 +17,18 @@ const useAuthStore = create( login: async (code: string) => { try { const res = await instance.get(`/api/kakao/login?code=${code}`); - if (res.data) { - set({ - isLoggedIn: true, - accessToken: res.data as string - }); - } + set({ + isLoggedIn: true, + accessToken: res.data as string + }); + return { + ok: true + }; } catch (error) { console.error("Error during login:", error); + return { + ok: false + }; } }, logout: () => { @@ -32,6 +36,7 @@ const useAuthStore = create( isLoggedIn: false, accessToken: null }); + return { ok: true }; } }), { From 19ecd5868f59722c59ad04effc57df26f45c6e80 Mon Sep 17 00:00:00 2001 From: junjeong Date: Mon, 21 Apr 2025 15:18:35 +0900 Subject: [PATCH 10/32] =?UTF-8?q?chore:=20redirect-url=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EC=A3=BC=EC=86=8C=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoLoginButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/button/KakaoLoginButton.tsx b/src/components/button/KakaoLoginButton.tsx index 4eaf4f9..07f6afe 100644 --- a/src/components/button/KakaoLoginButton.tsx +++ b/src/components/button/KakaoLoginButton.tsx @@ -1,5 +1,5 @@ const KakaoLoginButton = () => { - const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=3e5cfa29037d1bd11eb5448f9b298bfe&redirect_uri=https://localhost:5173/kakao-login&response_type=code`; + const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=3e5cfa29037d1bd11eb5448f9b298bfe&redirect_uri=https://mbtips.kr/kakao-login&response_type=code`; const handleClick = () => { window.location.href = kakaoURL; From 6801a892caccdc485d74ec51a6f3ecf6f6a769c2 Mon Sep 17 00:00:00 2001 From: junjeong Date: Mon, 21 Apr 2025 15:22:13 +0900 Subject: [PATCH 11/32] =?UTF-8?q?chore:=20mode=20->=20statuscode=20?= =?UTF-8?q?=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 2 +- src/pages/Error.tsx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index bc03d66..9cb4ef2 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -94,7 +94,7 @@ const App = () => { } /> } /> } /> - } /> + } /> diff --git a/src/pages/Error.tsx b/src/pages/Error.tsx index 6c86022..b43fc9a 100644 --- a/src/pages/Error.tsx +++ b/src/pages/Error.tsx @@ -1,7 +1,7 @@ import PrimaryButton from "@/components/button/PrimaryButton"; import { useNavigate } from "react-router-dom"; -const Error = ({ mode }: { mode: "401" | "404" | "500" }) => { +const Error = ({ statusCode }: { statusCode: "401" | "404" | "500" }) => { const navigate = useNavigate(); const content = { @@ -34,14 +34,14 @@ const Error = ({ mode }: { mode: "401" | "404" | "500" }) => {
에러 아이콘 -

{content[mode].title}

+

{content[statusCode].title}

- {content[mode].description} + {content[statusCode].description}

- - {content[mode].buttonTitle} + + {content[statusCode].buttonTitle}
From dfcc70e80fe49b238d9bd6b7c47a2f885b7dec4c Mon Sep 17 00:00:00 2001 From: soohyuniii Date: Mon, 21 Apr 2025 18:51:52 +0900 Subject: [PATCH 12/32] =?UTF-8?q?fix:=20=EC=B1=84=ED=8C=85=20input=20?= =?UTF-8?q?=ED=95=98=EB=8B=A8=20=EA=B3=A0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chat.tsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/pages/Chat.tsx b/src/pages/Chat.tsx index bded1be..a47b0ef 100644 --- a/src/pages/Chat.tsx +++ b/src/pages/Chat.tsx @@ -99,14 +99,11 @@ const Chat = () => { }; return ( -
+
-
-
- -
- +
+ {/* 메시지 리스트 */} {messages.map((msg, index) => (
{ className="mr-[9px] h-[36px] w-[36px] shrink-0 rounded-full border border-gray-200 object-cover" /> )} - {/* 채팅 메시지 */}
{
+ { onKeyUp={handleKeyup} onSend={() => handleSend(input)} /> + {isOpen && }
); From c632e2d7f3a134a4cad325a694ddcca1dd4eb201 Mon Sep 17 00:00:00 2001 From: junjeong Date: Mon, 21 Apr 2025 18:55:36 +0900 Subject: [PATCH 13/32] =?UTF-8?q?feat:=20login=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=97=90=EB=9F=AC=20=EB=B0=9C=EC=83=9D?= =?UTF-8?q?=EC=8B=9C=20error=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=EC=8B=9C=ED=82=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoLoginButton.tsx | 2 +- src/pages/KaKaoLogin.tsx | 2 +- src/store/useAuthStore.ts | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/button/KakaoLoginButton.tsx b/src/components/button/KakaoLoginButton.tsx index 07f6afe..4eaf4f9 100644 --- a/src/components/button/KakaoLoginButton.tsx +++ b/src/components/button/KakaoLoginButton.tsx @@ -1,5 +1,5 @@ const KakaoLoginButton = () => { - const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=3e5cfa29037d1bd11eb5448f9b298bfe&redirect_uri=https://mbtips.kr/kakao-login&response_type=code`; + const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=3e5cfa29037d1bd11eb5448f9b298bfe&redirect_uri=https://localhost:5173/kakao-login&response_type=code`; const handleClick = () => { window.location.href = kakaoURL; diff --git a/src/pages/KaKaoLogin.tsx b/src/pages/KaKaoLogin.tsx index 508f6ec..ee1bfa6 100644 --- a/src/pages/KaKaoLogin.tsx +++ b/src/pages/KaKaoLogin.tsx @@ -14,7 +14,7 @@ const KaKaoLogin = () => { const res = await login(code); if (res.ok) navigate("/"); } catch (err) { - console.error("카카오 로그인에 실패했습니다."); + navigate("/error"); } } }; diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts index 2feff47..9381a2f 100644 --- a/src/store/useAuthStore.ts +++ b/src/store/useAuthStore.ts @@ -25,10 +25,7 @@ const useAuthStore = create( ok: true }; } catch (error) { - console.error("Error during login:", error); - return { - ok: false - }; + throw error; } }, logout: () => { From 2e5ed385a8658b88449a74d6cd49259bf7b0cedb Mon Sep 17 00:00:00 2001 From: junjeong Date: Tue, 22 Apr 2025 15:52:14 +0900 Subject: [PATCH 14/32] =?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 24f48560993336510207efc5fdafc368fc36a65c Mon Sep 17 00:00:00 2001 From: soohyuniii Date: Wed, 23 Apr 2025 08:27:19 +0900 Subject: [PATCH 15/32] =?UTF-8?q?fix:=20confict=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/ChatStartButton.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/button/ChatStartButton.tsx b/src/components/button/ChatStartButton.tsx index 3014a4c..556c660 100644 --- a/src/components/button/ChatStartButton.tsx +++ b/src/components/button/ChatStartButton.tsx @@ -1,5 +1,6 @@ import { useNavigate } from "react-router-dom"; import { trackEvent } from "@/libs/analytics"; + type ChatStartButtonProps = { mode: "go-fast" | "go-virtual" | "go-chat"; mbti?: string; @@ -7,6 +8,7 @@ type ChatStartButtonProps = { const ChatStartButton = ({ mode, mbti }: ChatStartButtonProps) => { const navigate = useNavigate(); + const handleNavigate = () => { switch (mode) { case "go-fast": @@ -14,14 +16,14 @@ const ChatStartButton = ({ mode, mbti }: ChatStartButtonProps) => { page: "홈", element: "빠른 대화 시작" }); - navigate("/select-info", { state: "fastFriend" }); + navigate("/select-info", { state: { type: "fastFriend", mbti } }); break; case "go-virtual": trackEvent("Click", { page: "홈", element: "친구 - 바로 대화하기" }); - navigate("/select-info", { state: "virtualFriend" }); + navigate("/select-info", { state: { type: "virtualFriend", mbti } }); break; case "go-chat": navigate("/chat"); From 0a63f0ac5f3f95358e6024439e0b14584406973e Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 15:24:19 +0900 Subject: [PATCH 16/32] =?UTF-8?q?feat:=20=EA=B3=B5=EC=9C=A0=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20=EC=99=84=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoShareButton.tsx | 2 +- src/main.tsx | 5 +---- src/types/virtualFreind.ts | 9 +++++++++ 3 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 src/types/virtualFreind.ts diff --git a/src/components/button/KakaoShareButton.tsx b/src/components/button/KakaoShareButton.tsx index 56ee559..899d3c6 100644 --- a/src/components/button/KakaoShareButton.tsx +++ b/src/components/button/KakaoShareButton.tsx @@ -14,7 +14,7 @@ const KakaoShareButton = ({ useEffect(() => { // 카카오톡 SDK 초기화 if (window.Kakao && !window.Kakao.isInitialized()) { - window.Kakao.init("3e5cfa29037d1bd11eb5448f9b298bfe"); + window.Kakao.init("a4816c8b85267871796431d92ed23451"); } }, []); diff --git a/src/main.tsx b/src/main.tsx index 44d6cb5..a00259d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,12 +1,9 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; -import App from "./App"; -import ShareModal from "@/components/modal/ShareModal"; createRoot(document.getElementById("root")!).render( - {/* */} - + ); 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 59b731818bf39cf00e725b42cf793d0450b42784 Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 16:28:47 +0900 Subject: [PATCH 17/32] =?UTF-8?q?fix:=20=EA=B3=B5=EC=9C=A0=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/icon/Instagram.svg | 15 ---------- src/components/button/ShareButton.tsx | 22 +++++++++++---- src/components/button/TwitterShareButton.tsx | 1 - src/components/header/Header.tsx | 2 +- src/components/header/SubHeader.tsx | 29 +++++++++++--------- src/components/modal/ShareModal.tsx | 15 ++++------ src/index.css | 4 --- src/main.tsx | 1 + 8 files changed, 40 insertions(+), 49 deletions(-) delete mode 100644 public/icon/Instagram.svg diff --git a/public/icon/Instagram.svg b/public/icon/Instagram.svg deleted file mode 100644 index 364adac..0000000 --- a/public/icon/Instagram.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/components/button/ShareButton.tsx b/src/components/button/ShareButton.tsx index 04f1998..e461aa3 100644 --- a/src/components/button/ShareButton.tsx +++ b/src/components/button/ShareButton.tsx @@ -1,9 +1,19 @@ +import { useState } from "react"; +import ShareModal from "@/components/modal/ShareModal"; + const ShareButton = () => { - return ( - - ) -} + {shareModalIsOpen && ( + setShareModalIsOpen(false)} /> + )} + + ); +}; -export default ShareButton; \ No newline at end of file +export default ShareButton; diff --git a/src/components/button/TwitterShareButton.tsx b/src/components/button/TwitterShareButton.tsx index 606fd6e..432f2f0 100644 --- a/src/components/button/TwitterShareButton.tsx +++ b/src/components/button/TwitterShareButton.tsx @@ -6,7 +6,6 @@ const TwitterShareButton = ({ title }: { title: string }) => { href={`https://twitter.com/intent/tweet?text=${title}&url=${currentUrl}`} className="flex flex-col items-center gap-1" > - {/* 디자인 시안 나오면 고칠 예정 -> 4.17 정준영 */} 트위터 아이콘

트위터

diff --git a/src/components/header/Header.tsx b/src/components/header/Header.tsx index d245cc3..26c198c 100644 --- a/src/components/header/Header.tsx +++ b/src/components/header/Header.tsx @@ -14,7 +14,7 @@ type HeaderProps = { const Header = ({ title = "", showPreviousIcon = true, - showShareIcon = false, + showShareIcon = true, children }: HeaderProps) => { const { pathname } = useLocation(); diff --git a/src/components/header/SubHeader.tsx b/src/components/header/SubHeader.tsx index b387f1e..124539b 100644 --- a/src/components/header/SubHeader.tsx +++ b/src/components/header/SubHeader.tsx @@ -13,7 +13,7 @@ type SubHeaderProps = { const SubHeader = ({ title = "", showPreviousIcon = true, - showShareIcon = false + showShareIcon = true }: SubHeaderProps) => { const navigate = useNavigate(); const { pathname, state } = useLocation(); @@ -49,40 +49,35 @@ const SubHeader = ({ navigate("/"); }; - const openShareModal = () => { - setShareModalIsOpen(true); - }; - return ( <> -
+
{showPreviousIcon && ( Go To Back )} -

+

{title}

{showShareIcon && ( - )}
@@ -97,6 +92,14 @@ const SubHeader = ({ onConfirm={handleConfirm} /> )} + + {shareModalIsOpen && ( + { + setShareModalIsOpen(false); + }} + /> + )} ); }; diff --git a/src/components/modal/ShareModal.tsx b/src/components/modal/ShareModal.tsx index 0c70785..0148a63 100644 --- a/src/components/modal/ShareModal.tsx +++ b/src/components/modal/ShareModal.tsx @@ -4,7 +4,7 @@ import KakaoShareButton from "../button/KakaoShareButton"; import TwitterShareButton from "@/components/button/TwitterShareButton"; interface ShareModalProps { - closeModal: React.Dispatch>; + closeModal: () => void; } const ShareModal = ({ closeModal }: ShareModalProps) => { @@ -46,14 +46,11 @@ const ShareModal = ({ closeModal }: ShareModalProps) => { />
-
diff --git a/src/index.css b/src/index.css index d1e36de..6b64713 100644 --- a/src/index.css +++ b/src/index.css @@ -63,10 +63,6 @@ button { cursor: pointer; } -button:hover { - opacity: 80%; -} - @keyframes pulse-custom { 0%, 100% { diff --git a/src/main.tsx b/src/main.tsx index a00259d..12fa35b 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,7 @@ import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import "./index.css"; +import App from "./App"; createRoot(document.getElementById("root")!).render( From 1c8c4bf43fa36266e414e90211f82f40b1e0b8cf Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 16:32:02 +0900 Subject: [PATCH 18/32] =?UTF-8?q?chore:=20comment=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoShareButton.tsx | 4 +++- src/components/button/UrlCopyBar.tsx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/button/KakaoShareButton.tsx b/src/components/button/KakaoShareButton.tsx index 899d3c6..e5ef16f 100644 --- a/src/components/button/KakaoShareButton.tsx +++ b/src/components/button/KakaoShareButton.tsx @@ -11,10 +11,12 @@ const KakaoShareButton = ({ description, imageUrl }: KakoShareButtonProps) => { + const kakaoJavascriptKey = import.meta.env.VITE_KAKAO_JAVASCRIPT_KEY; + useEffect(() => { // 카카오톡 SDK 초기화 if (window.Kakao && !window.Kakao.isInitialized()) { - window.Kakao.init("a4816c8b85267871796431d92ed23451"); + window.Kakao.init(kakaoJavascriptKey); } }, []); diff --git a/src/components/button/UrlCopyBar.tsx b/src/components/button/UrlCopyBar.tsx index f8b3982..f52429d 100644 --- a/src/components/button/UrlCopyBar.tsx +++ b/src/components/button/UrlCopyBar.tsx @@ -1,4 +1,4 @@ -import UrlCopyButton from "./UrlCopyButton"; +import UrlCopyButton from "@/components/button/UrlCopyButton"; const UrlCopyBar = () => { const currentUrl = window.location.href; From 7363c80f8686d35967e5771e52525b8f49612e51 Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 16:38:28 +0900 Subject: [PATCH 19/32] =?UTF-8?q?chore:=20PR=20comment=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/useAuthStore.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts index 9381a2f..406e557 100644 --- a/src/store/useAuthStore.ts +++ b/src/store/useAuthStore.ts @@ -16,7 +16,9 @@ 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" + ); set({ isLoggedIn: true, accessToken: res.data as string From 8820e2c6d6ae42ca69ab4ec399390a1f5f3b0207 Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 16:49:35 +0900 Subject: [PATCH 20/32] =?UTF-8?q?chore:=20pr=20comment=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoLoginButton.tsx | 5 ++++- src/store/useAuthStore.ts | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/button/KakaoLoginButton.tsx b/src/components/button/KakaoLoginButton.tsx index 4eaf4f9..cec87b4 100644 --- a/src/components/button/KakaoLoginButton.tsx +++ b/src/components/button/KakaoLoginButton.tsx @@ -1,5 +1,8 @@ const KakaoLoginButton = () => { - const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=3e5cfa29037d1bd11eb5448f9b298bfe&redirect_uri=https://localhost:5173/kakao-login&response_type=code`; + const kakaoRestApiKey = import.meta.env.VITE_KAKAO_REST_API_KEY; + const kakaoRedirectUrl = import.meta.env.VITE_KAKAO_REDIRECT_URI; + + const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=${kakaoRestApiKey}&redirect_uri=${kakaoRedirectUrl}&response_type=code`; const handleClick = () => { window.location.href = kakaoURL; diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts index 406e557..5ad10d4 100644 --- a/src/store/useAuthStore.ts +++ b/src/store/useAuthStore.ts @@ -17,7 +17,8 @@ const useAuthStore = create( login: async (code: string) => { try { const res = await instance.get( - `/api/kakao/login?code=${code}` + // "&redirectUrl=https://localhost:5173/kakao-login" + `/api/kakao/login?code=${code}` + + "&redirectUrl=https://localhost:5173/kakao-login" ); set({ isLoggedIn: true, From d8f24eb7ad643c99ef29f72c347d4651c271c99b Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 16:56:27 +0900 Subject: [PATCH 21/32] =?UTF-8?q?chore:=20=EC=A0=88=EB=8C=80=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20alt=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoShareButton.tsx | 2 +- src/components/modal/ShareModal.tsx | 2 +- src/main.tsx | 7 +------ 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/components/button/KakaoShareButton.tsx b/src/components/button/KakaoShareButton.tsx index e5ef16f..1422ab3 100644 --- a/src/components/button/KakaoShareButton.tsx +++ b/src/components/button/KakaoShareButton.tsx @@ -42,7 +42,7 @@ const KakaoShareButton = ({
-
From 2f24b2f1be816849b38f8924e34584f647a9d45d Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 17:35:11 +0900 Subject: [PATCH 24/32] =?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 ( From 57049e5fe87ac01b9e87cc1e00df7b62bb49498f Mon Sep 17 00:00:00 2001 From: junjeong Date: Wed, 23 Apr 2025 19:36:08 +0900 Subject: [PATCH 25/32] =?UTF-8?q?chore:=20useAuthStore=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/store/useAuthStore.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts index 5ad10d4..92673b0 100644 --- a/src/store/useAuthStore.ts +++ b/src/store/useAuthStore.ts @@ -17,8 +17,8 @@ const useAuthStore = create( login: async (code: string) => { try { const res = await instance.get( - `/api/kakao/login?code=${code}` + - "&redirectUrl=https://localhost:5173/kakao-login" + `/api/kakao/login?code=${code}` + // + "&redirectUrl=https://localhost:5173/kakao-login" ); set({ isLoggedIn: true, From 976cf01d58ef6e95a925bab865ae6f4a6b6ab40a Mon Sep 17 00:00:00 2001 From: soohyuniii Date: Thu, 24 Apr 2025 16:36:25 +0900 Subject: [PATCH 26/32] =?UTF-8?q?feat:=20=EC=B1=84=ED=8C=85=20=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A0=80=EC=9E=A5=ED=95=98=EA=B8=B0=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/header/SubHeader.tsx | 5 +++++ src/pages/Chat.tsx | 12 +++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/components/header/SubHeader.tsx b/src/components/header/SubHeader.tsx index 124539b..325bc49 100644 --- a/src/components/header/SubHeader.tsx +++ b/src/components/header/SubHeader.tsx @@ -24,6 +24,7 @@ const SubHeader = ({ const isChatPage = pathname === "/chat"; const isFirstQuestionPage = currentPage === 1; const mode = state?.mode; + const chatId = state?.id; const handleGoBack = () => { if (isProgressPage && !isFirstQuestionPage) { @@ -45,6 +46,10 @@ const SubHeader = ({ const handleCancel = () => setIsLeaveChatModalOpen(false); const handleConfirm = () => { + if (chatId) { + sessionStorage.removeItem(`chatMessages_${chatId}`); + } + setIsLeaveChatModalOpen(false); navigate("/"); }; diff --git a/src/pages/Chat.tsx b/src/pages/Chat.tsx index a47b0ef..ce1f32d 100644 --- a/src/pages/Chat.tsx +++ b/src/pages/Chat.tsx @@ -20,7 +20,7 @@ interface ChatResponse { const Chat = () => { const { state } = useLocation(); - const { mbti, mode, id } = state; + const { mbti, mode, id = Date.now().toString() } = state; const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); @@ -30,11 +30,21 @@ const Chat = () => { const chatTitle = `${mbti}와 대화`; const assistantInfo = mbti; const assistantImgUrl = pickMbtiImage(assistantInfo); + const storageKey = `chatMessages_${id}`; useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages, isOpen]); + useEffect(() => { + const stored = sessionStorage.getItem(storageKey); + if (stored) setMessages(JSON.parse(stored)); + }, [storageKey]); + + useEffect(() => { + sessionStorage.setItem(storageKey, JSON.stringify(messages)); + }, [messages, storageKey]); + const handleToggleTips = () => { const nextAction = !isOpen; From a63f53ef618cefdecf599aa83058979b063064d4 Mon Sep 17 00:00:00 2001 From: soohyuniii Date: Thu, 24 Apr 2025 18:10:02 +0900 Subject: [PATCH 27/32] =?UTF-8?q?feat:=20=ED=83=88=ED=87=B4=20api=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/user.ts | 6 ++++++ src/pages/MyInfo.tsx | 13 +++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/api/user.ts diff --git a/src/api/user.ts b/src/api/user.ts new file mode 100644 index 0000000..9f4b63f --- /dev/null +++ b/src/api/user.ts @@ -0,0 +1,6 @@ +import { authInstance } from "@/api/axios"; + +export const deleteUser = async () => { + const response = await authInstance.delete("/api/users"); + return response.data; +}; diff --git a/src/pages/MyInfo.tsx b/src/pages/MyInfo.tsx index 2dd2e71..03a2037 100644 --- a/src/pages/MyInfo.tsx +++ b/src/pages/MyInfo.tsx @@ -5,6 +5,7 @@ import useAuthStore from "@/store/useAuthStore"; import { useNavigate } from "react-router-dom"; import TermsAndPrivacyModal from "@/components/modal/TermsAndPrivacyModal"; import { trackEvent } from "@/libs/analytics"; +import { deleteUser } from "@/api/user"; type ModalType = "logout" | "withdraw" | "terms" | "privacy" | null; @@ -32,7 +33,7 @@ const MyInfo = () => { setModalType(null); }; - const handleConfirm = () => { + const handleConfirm = async () => { if (modalType === "logout") { trackEvent("Click", { page: "내 정보", @@ -45,7 +46,15 @@ const MyInfo = () => { page: "내 정보", element: "회원탈퇴" }); - console.log("회원탈퇴 실행"); //TODO: 회원탈퇴 기능 구현 시 추가 필요 + + try { + await deleteUser(); + logout(); + navigate("/"); + } catch (error) { + console.error(error); + navigate("/error"); + } } setModalType(null); }; From bf6de4a95974ef3100437698e9cd26973e52fcdc Mon Sep 17 00:00:00 2001 From: junjeong Date: Thu, 24 Apr 2025 18:33:01 +0900 Subject: [PATCH 28/32] =?UTF-8?q?fix:=201.=ED=99=88=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EC=97=90=EC=84=9C=20token=EC=9D=B4=20=EC=97=86?= =?UTF-8?q?=EC=96=B4=20error=20=EB=82=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C,=20?= =?UTF-8?q?2.StrokeBanner=20=ED=81=B4=EB=A6=AD=EC=8B=9C=20fast-freind?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EB=8A=94=EB=AC=B8=EC=A0=9C,=203.productio?= =?UTF-8?q?n=20mode=EC=97=90=20=EB=94=B0=EB=9D=BC=EC=84=9C=20kakao-login?= =?UTF-8?q?=20redirectUrl=20=EB=8B=A4=EB=A5=B4=EA=B2=8C=20=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=9E=90=EB=8F=99=ED=99=94=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/StrokeBanner.tsx | 2 +- src/components/button/KakaoLoginButton.tsx | 5 ++++- src/pages/SelectInfo.tsx | 4 ++-- src/store/useAuthStore.ts | 13 ++++++++----- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/StrokeBanner.tsx b/src/components/StrokeBanner.tsx index 092041f..dfd6983 100644 --- a/src/components/StrokeBanner.tsx +++ b/src/components/StrokeBanner.tsx @@ -5,7 +5,7 @@ const StrokeBanner = () => { const handleNavigate = () => { const mode = "virtualFriend"; - navigate("/select-info", { state: mode }); + navigate("/select-info", { state: { type: mode } }); }; return ( diff --git a/src/components/button/KakaoLoginButton.tsx b/src/components/button/KakaoLoginButton.tsx index cec87b4..c141073 100644 --- a/src/components/button/KakaoLoginButton.tsx +++ b/src/components/button/KakaoLoginButton.tsx @@ -1,6 +1,9 @@ const KakaoLoginButton = () => { const kakaoRestApiKey = import.meta.env.VITE_KAKAO_REST_API_KEY; - const kakaoRedirectUrl = import.meta.env.VITE_KAKAO_REDIRECT_URI; + const kakaoRedirectUrl = + import.meta.env.MODE === "production" + ? import.meta.env.VITE_KAKAO_PRODUCTION_REDIRECT_URI + : import.meta.env.VITE_KAKAO_DEVELOP_REDIRECT_URI; const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=${kakaoRestApiKey}&redirect_uri=${kakaoRedirectUrl}&response_type=code`; diff --git a/src/pages/SelectInfo.tsx b/src/pages/SelectInfo.tsx index cf4c96c..87be9b8 100644 --- a/src/pages/SelectInfo.tsx +++ b/src/pages/SelectInfo.tsx @@ -3,7 +3,7 @@ import { useLocation, useNavigate } from "react-router-dom"; import FormButton from "@/components/button/FormButton"; import Header from "@/components/header/Header"; import { getMBTIgroup, mapAgeToNumber } from "@/utils/helpers"; -import instance from "@/api/axios"; +import { authInstance } from "@/api/axios"; import ToastMessage from "@/components/ToastMessage"; import { trackEvent } from "@/libs/analytics"; @@ -192,7 +192,7 @@ const SelectInfo = () => { type === "virtualFriend" ? "api/virtual-friend" : "api/fast-friend"; try { - const response = await instance.post( + const response = await authInstance.post( `/${apiUrl}`, selectedData ); diff --git a/src/store/useAuthStore.ts b/src/store/useAuthStore.ts index 92673b0..3bd3cca 100644 --- a/src/store/useAuthStore.ts +++ b/src/store/useAuthStore.ts @@ -16,13 +16,16 @@ const useAuthStore = create( accessToken: null, login: async (code: string) => { try { - const res = await instance.get( - `/api/kakao/login?code=${code}` - // + "&redirectUrl=https://localhost:5173/kakao-login" - ); + const requestURI = + // 아래 코드는 백엔드팀에서 작업해주시면 동일한 uri로 바뀔 예정 -> 4.24 정준영 + import.meta.env.MODE === "production" + ? `/api/kakao/login?code=${code}` + : `/api/kakao/login?code=${code}&redirectUrl=https://localhost:5173/kakao-login`; + + const res = await instance.get(requestURI); set({ isLoggedIn: true, - accessToken: res.data as string + accessToken: res.data.data as string }); return { ok: true From b70fa7e0a35965a1a7b9d16d156c9e5bb74fb70f Mon Sep 17 00:00:00 2001 From: junjeong Date: Thu, 24 Apr 2025 22:19:07 +0900 Subject: [PATCH 29/32] =?UTF-8?q?fix:=20cicd.yml=20->=20=ED=99=98=EA=B2=BD?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/cicd.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index f37e765..82fd9ba 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -27,7 +27,11 @@ jobs: - name: Create .env file # env 파일 생성 단계 추가 run: | - echo "VITE_GA_MEASUREMENT_ID=${{ secrets.VITE_GA_MEASUREMENT_ID }}" > .env + echo "VITE_KAKAO_REST_API_KEY=${{ secrets.VITE_KAKAO_REST_API_KEY }}" >> .env + echo "VITE_KAKAO_JAVASCRIPT_KEY=${{ secrets.VITE_KAKAO_JAVASCRIPT_KEY }}" >> .env + echo "VITE_KAKAO_PRODUCTION_REDIRECT_URI=${{ secrets.VITE_KAKAO_PRODUCTION_REDIRECT_URI }}" >> .env + echo "VITE_KAKAO_DEVELOPE_REDIRECT_URI=${{ secrets.VITE_KAKAO_DEVELOP_REDIRECT_URI }}" >> .env + echo "VITE_GA_MEASUREMENT_ID=${{ secrets.VITE_GA_MEASUREMENT_ID }}" >> .env - name: Run build run: npm run build # 프로젝트에 맞는 빌드 명령어 From a208be8b8539efbd815c006b0a2f559741f22867 Mon Sep 17 00:00:00 2001 From: junjeong Date: Thu, 24 Apr 2025 22:33:18 +0900 Subject: [PATCH 30/32] =?UTF-8?q?chore:=20=EC=98=A4=ED=83=80=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 --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 82fd9ba..acd3fff 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -30,7 +30,7 @@ jobs: echo "VITE_KAKAO_REST_API_KEY=${{ secrets.VITE_KAKAO_REST_API_KEY }}" >> .env echo "VITE_KAKAO_JAVASCRIPT_KEY=${{ secrets.VITE_KAKAO_JAVASCRIPT_KEY }}" >> .env echo "VITE_KAKAO_PRODUCTION_REDIRECT_URI=${{ secrets.VITE_KAKAO_PRODUCTION_REDIRECT_URI }}" >> .env - echo "VITE_KAKAO_DEVELOPE_REDIRECT_URI=${{ secrets.VITE_KAKAO_DEVELOP_REDIRECT_URI }}" >> .env + echo "VITE_KAKAO_DEVELOPE_REDIRECT_URI=${{ secrets.VITE_KAKAO_DEVELOPE_REDIRECT_URI }}" >> .env echo "VITE_GA_MEASUREMENT_ID=${{ secrets.VITE_GA_MEASUREMENT_ID }}" >> .env - name: Run build From 98794c124ca8f016583f222ba62a4380fd6d4e68 Mon Sep 17 00:00:00 2001 From: junjeong Date: Thu, 24 Apr 2025 23:05:02 +0900 Subject: [PATCH 31/32] =?UTF-8?q?chore:=20DEVELOP=20->=20DEVELOPE=20?= =?UTF-8?q?=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/button/KakaoLoginButton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/button/KakaoLoginButton.tsx b/src/components/button/KakaoLoginButton.tsx index c141073..bb44de2 100644 --- a/src/components/button/KakaoLoginButton.tsx +++ b/src/components/button/KakaoLoginButton.tsx @@ -3,7 +3,7 @@ const KakaoLoginButton = () => { const kakaoRedirectUrl = import.meta.env.MODE === "production" ? import.meta.env.VITE_KAKAO_PRODUCTION_REDIRECT_URI - : import.meta.env.VITE_KAKAO_DEVELOP_REDIRECT_URI; + : import.meta.env.VITE_KAKAO_DEVELOPE_REDIRECT_URI; const kakaoURL = `https://kauth.kakao.com/oauth/authorize?client_id=${kakaoRestApiKey}&redirect_uri=${kakaoRedirectUrl}&response_type=code`; From 98a358d01ff0e69a5dcc1e01462f35867f68173b Mon Sep 17 00:00:00 2001 From: junjeong Date: Thu, 24 Apr 2025 23:23:54 +0900 Subject: [PATCH 32/32] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EC=9D=B4=20=ED=95=84=EC=9A=94=ED=95=A9=EB=8B=88=EB=8B=A4=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/StrokeBanner.tsx | 23 +++++++++++++++++++-- src/components/SubTitle.tsx | 21 +++++++++++++++++-- src/components/modal/ActionConfirmModal.tsx | 2 +- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/components/StrokeBanner.tsx b/src/components/StrokeBanner.tsx index dfd6983..87ea8a6 100644 --- a/src/components/StrokeBanner.tsx +++ b/src/components/StrokeBanner.tsx @@ -1,11 +1,20 @@ +import { useState } from "react"; import { useNavigate } from "react-router-dom"; +import ActionConfirmModal from "@/components/modal/ActionConfirmModal"; +import useAuthStore from "@/store/useAuthStore"; const StrokeBanner = () => { + const [needLoginModalIsOpen, setNeedLoginModalIsOpen] = useState(false); + const { isLoggedIn } = useAuthStore(); const navigate = useNavigate(); const handleNavigate = () => { - const mode = "virtualFriend"; - navigate("/select-info", { state: { type: mode } }); + if (isLoggedIn) { + const mode = "virtualFriend"; + navigate("/select-info", { state: { type: mode } }); + } else { + setNeedLoginModalIsOpen(true); + } }; return ( @@ -27,6 +36,16 @@ const StrokeBanner = () => {
입력해서 빠르게 대화할 수 있어요

+ {needLoginModalIsOpen && ( + setNeedLoginModalIsOpen(false)} + onConfirm={() => navigate("/login")} + /> + )}
); }; diff --git a/src/components/SubTitle.tsx b/src/components/SubTitle.tsx index bb165a4..f55ebc1 100644 --- a/src/components/SubTitle.tsx +++ b/src/components/SubTitle.tsx @@ -1,6 +1,11 @@ +import { useState } from "react"; import { useNavigate } from "react-router-dom"; +import ActionConfirmModal from "@/components/modal/ActionConfirmModal"; +import useAuthStore from "@/store/useAuthStore"; const SubTitle = ({ mode }: { mode: "빠른대화" | "친구목록" }) => { + const [needLoginModalIsOpen, setNeedLoginModalIsOpen] = useState(false); + const { isLoggedIn } = useAuthStore(); const navigate = useNavigate(); const titleList = { @@ -15,8 +20,10 @@ const SubTitle = ({ mode }: { mode: "빠른대화" | "친구목록" }) => { }; const handleNavigate = () => { - const type = mode === "빠른대화" ? "fastFriend" : "virtualFriend"; - navigate("/select-info", { state: { type: type } }); + if (isLoggedIn) { + const type = mode === "빠른대화" ? "fastFriend" : "virtualFriend"; + navigate("/select-info", { state: { type: type } }); + } else setNeedLoginModalIsOpen(true); }; return ( @@ -37,6 +44,16 @@ const SubTitle = ({ mode }: { mode: "빠른대화" | "친구목록" }) => { /> )} + {needLoginModalIsOpen && ( + setNeedLoginModalIsOpen(false)} + onConfirm={() => navigate("/login")} + /> + )}
); }; diff --git a/src/components/modal/ActionConfirmModal.tsx b/src/components/modal/ActionConfirmModal.tsx index cc2f447..6564d55 100644 --- a/src/components/modal/ActionConfirmModal.tsx +++ b/src/components/modal/ActionConfirmModal.tsx @@ -27,7 +27,7 @@ const ActionConfirmModal = ({ onClick={(e) => e.stopPropagation()} >

{title}

-

+

{message.map((line, index) => ( {line}