From 0a585cc4dd23ca34bc54cf37d8647593e1168126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=95=9C=EC=9E=88=EB=8A=94-=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EB=84=A4=EC=9E=84?= Date: Thu, 30 Oct 2025 13:48:19 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EC=98=A4=ED=94=88=EC=B1=84?= =?UTF-8?q?=ED=8C=85=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chat.tsx | 140 ++++++++++++++++++++++++++---- src/utils/pickMbtiProfileImage.ts | 62 +++++++++++++ 2 files changed, 186 insertions(+), 16 deletions(-) create mode 100644 src/utils/pickMbtiProfileImage.ts diff --git a/src/pages/Chat.tsx b/src/pages/Chat.tsx index 02c359c..7e30fdf 100644 --- a/src/pages/Chat.tsx +++ b/src/pages/Chat.tsx @@ -16,6 +16,7 @@ import ChatMessage from "@/components/ChatMessage"; import ChatActionBar from "@/components/ChatActionBar"; import TipsMenuContainer from "@/components/tips/TipsMenuContainer"; import pickMbtiImage from "@/utils/pickMbtiImage"; +import pickMbtiProfileImage from "@/utils/pickMbtiProfileImage"; import websocketService from "@/services/websocket"; import { getOpenChatMessages } from "@/api/openChat"; import { WebSocketMessage } from "@/types/openChat"; @@ -71,6 +72,8 @@ const Chat = () => { openChatTitle || (name ? `${name}과 대화` : `${mbti}와 대화`); const assistantImgUrl = pickMbtiImage(mbti); const storageKey = `chatMessages_${id}`; + const topicChatStorageKey = `topicChatMessages_${openChatId}_${nickname}`; + const topicChatMbtiCacheKey = `topicChatMbti_${openChatId}`; const isTopicChat = mode === "topicChat"; @@ -133,15 +136,51 @@ const Chat = () => { wsMessage.message ); - const newMessage: Message = { - role: "assistant", - content: wsMessage.message, - nickname: wsMessage.nickname, - mbti: wsMessage.mbti || undefined, - messageType: "text" - }; + // 웹소켓에서 받은 mbti가 없으면 기존 메시지에서 찾아서 사용 + setMessages((prev) => { + // 같은 닉네임의 이전 메시지에서 mbti 찾기 + const existingMsg = prev + .slice() + .reverse() + .find((m) => m.nickname === wsMessage.nickname); + // localStorage에서 mbti 찾기 + let cachedMbti = null; + if (topicChatMbtiCacheKey && wsMessage.nickname) { + const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey); + if (mbtiCache) { + try { + const cacheData = JSON.parse(mbtiCache); + cachedMbti = cacheData[wsMessage.nickname]; + } catch (e) { + console.error("Failed to parse mbti cache", e); + } + } + } + + const userMbti = + wsMessage.mbti ?? existingMsg?.mbti ?? cachedMbti ?? undefined; + + // mbti가 있으면 localStorage에 저장 + if (userMbti && topicChatMbtiCacheKey && wsMessage.nickname) { + const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey); + const cacheData = mbtiCache ? JSON.parse(mbtiCache) : {}; + cacheData[wsMessage.nickname] = userMbti; + localStorage.setItem( + topicChatMbtiCacheKey, + JSON.stringify(cacheData) + ); + } - setMessages((prev) => [...prev, newMessage]); + const newMessage: Message = { + role: "assistant", + content: wsMessage.message, + nickname: wsMessage.nickname ?? undefined, + mbti: userMbti, + messageType: "text" + }; + + return [...prev, newMessage]; + }); } // 예상치 못한 메시지 형식 로그 @@ -149,7 +188,7 @@ const Chat = () => { console.warn("알 수 없는 WebSocket 메시지:", wsMessage); } }, - [nickname] + [nickname, topicChatMbtiCacheKey] ); useEffect(() => { @@ -180,14 +219,65 @@ const Chat = () => { role: msg.nickname === nickname ? "user" : "assistant", content: msg.message, nickname: msg.nickname, - mbti: msg.mbti || undefined, + mbti: msg.mbti ?? undefined, messageType: msg.messageType || "text" }; } ); // 메시지 순서: API에서 최신순으로 오므로 reverse()로 시간순 정렬 - setMessages(convertedMessages.reverse()); + const apiMessages = convertedMessages.reverse(); + + // sessionStorage에서 이전 MBTI 정보 가져오기 + let cachedMessages: Message[] = []; + if (topicChatStorageKey) { + const storedMessage = sessionStorage.getItem(topicChatStorageKey); + if (storedMessage) { + cachedMessages = JSON.parse(storedMessage); + } + } + + // API 메시지에 mbti가 없으면 캐시에서 찾아서 채우기 + const enhancedMessages = apiMessages.map((msg) => { + if (!msg.mbti && msg.nickname) { + // sessionStorage에서 찾기 (우선순위 1) + const cachedMsg = cachedMessages + .slice() + .reverse() + .find((m) => m.nickname === msg.nickname); + + // localStorage에서 찾기 (우선순위 2) + let localStorageMbti = null; + if (topicChatMbtiCacheKey) { + const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey); + if (mbtiCache) { + try { + const cacheData = JSON.parse(mbtiCache); + localStorageMbti = cacheData[msg.nickname]; + } catch (e) { + console.error("Failed to parse mbti cache", e); + } + } + } + + const foundMbti = cachedMsg?.mbti ?? localStorageMbti; + if (foundMbti) { + return { ...msg, mbti: foundMbti }; + } + } else if (msg.mbti && topicChatMbtiCacheKey && msg.nickname) { + // mbti가 있으면 localStorage에 저장 + const mbtiCache = localStorage.getItem(topicChatMbtiCacheKey); + const cacheData = mbtiCache ? JSON.parse(mbtiCache) : {}; + cacheData[msg.nickname] = msg.mbti; + localStorage.setItem( + topicChatMbtiCacheKey, + JSON.stringify(cacheData) + ); + } + return msg; + }); + + setMessages(enhancedMessages); } else { setMessages([]); } @@ -281,17 +371,27 @@ const Chat = () => { if (storedMessage) { setMessages(JSON.parse(storedMessage)); } + } else if (isTopicChat && topicChatStorageKey) { + // sessionStorage에서 로드 (탭이 닫히지 않은 경우) + const storedMessage = sessionStorage.getItem(topicChatStorageKey); + if (storedMessage) { + setMessages(JSON.parse(storedMessage)); + } + // sessionStorage가 없으면 API에서 로드된 데이터 사용 } }; fetchMessages(); - }, [mode, id, storageKey, isTopicChat]); + }, [mode, id, storageKey, isTopicChat, topicChatStorageKey]); useEffect(() => { if (mode !== "virtualFriend" && !isTopicChat) { sessionStorage.setItem(storageKey, JSON.stringify(messages)); + } else if (isTopicChat && topicChatStorageKey) { + // topicChat은 sessionStorage에 저장 (탭이 닫히면 날아감) + sessionStorage.setItem(topicChatStorageKey, JSON.stringify(messages)); } - }, [messages, mode, storageKey, isTopicChat]); + }, [messages, mode, storageKey, topicChatStorageKey, isTopicChat]); useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: "smooth" }); @@ -463,9 +563,17 @@ const Chat = () => {
{isTopicChat && msg.nickname ? (
-
- {msg.nickname.charAt(0)} -
+ {msg.mbti ? ( + MBTI Profile + ) : ( +
+ {msg.nickname.charAt(0)} +
+ )} {msg.mbti && ( {msg.mbti} diff --git a/src/utils/pickMbtiProfileImage.ts b/src/utils/pickMbtiProfileImage.ts new file mode 100644 index 0000000..377b531 --- /dev/null +++ b/src/utils/pickMbtiProfileImage.ts @@ -0,0 +1,62 @@ +const pickMbtiProfileImage = (mbti?: string) => { + if (!mbti) return ""; + + let mbtiProfileImage = ""; + + switch (mbti) { + case "ISTJ": + mbtiProfileImage = "/image/ISTJ_profile.png"; + break; + case "ISFJ": + mbtiProfileImage = "/image/ISFJ_profile.png"; + break; + case "INFJ": + mbtiProfileImage = "/image/INFJ_profile.png"; + break; + case "INTJ": + mbtiProfileImage = "/image/INTJ_profile.png"; + break; + case "ISTP": + mbtiProfileImage = "/image/ISTP_profile.png"; + break; + case "ISFP": + mbtiProfileImage = "/image/ISFP_profile.png"; + break; + case "INFP": + mbtiProfileImage = "/image/INFP_profile.png"; + break; + case "INTP": + mbtiProfileImage = "/image/INTP_profile.png"; + break; + case "ESTP": + mbtiProfileImage = "/image/ESTP_profile.png"; + break; + case "ESFP": + mbtiProfileImage = "/image/ESFP_profile.png"; + break; + case "ENFP": + mbtiProfileImage = "/image/ENFP_profile.png"; + break; + case "ENTP": + mbtiProfileImage = "/image/ENTP_profile.png"; + break; + case "ESTJ": + mbtiProfileImage = "/image/ESTJ_profile.png"; + break; + case "ESFJ": + mbtiProfileImage = "/image/ESFJ_profile.png"; + break; + case "ENFJ": + mbtiProfileImage = "/image/ENFJ_profile.png"; + break; + case "ENTJ": + mbtiProfileImage = "/image/ENTJ_profile.png"; + break; + default: + break; + } + + return mbtiProfileImage; +}; + +export default pickMbtiProfileImage; From 3eb75db921ef92c4dc14f3ae90ab2c4defb1efeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B6=8C=ED=95=9C=EC=9E=88=EB=8A=94-=EC=9C=A0=EC=A0=80?= =?UTF-8?q?=EB=84=A4=EC=9E=84?= Date: Tue, 4 Nov 2025 18:55:14 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20mbti=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Chat.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/Chat.tsx b/src/pages/Chat.tsx index 7e30fdf..008e3bc 100644 --- a/src/pages/Chat.tsx +++ b/src/pages/Chat.tsx @@ -574,11 +574,6 @@ const Chat = () => { {msg.nickname.charAt(0)}
)} - {msg.mbti && ( - - {msg.mbti} - - )}
) : (