@@ -16,6 +16,7 @@ import ChatMessage from "@/components/ChatMessage";
1616import ChatActionBar from "@/components/ChatActionBar" ;
1717import TipsMenuContainer from "@/components/tips/TipsMenuContainer" ;
1818import pickMbtiImage from "@/utils/pickMbtiImage" ;
19+ import pickMbtiProfileImage from "@/utils/pickMbtiProfileImage" ;
1920import websocketService from "@/services/websocket" ;
2021import { getOpenChatMessages } from "@/api/openChat" ;
2122import { WebSocketMessage } from "@/types/openChat" ;
@@ -71,6 +72,8 @@ const Chat = () => {
7172 openChatTitle || ( name ? `${ name } 과 대화` : `${ mbti } 와 대화` ) ;
7273 const assistantImgUrl = pickMbtiImage ( mbti ) ;
7374 const storageKey = `chatMessages_${ id } ` ;
75+ const topicChatStorageKey = `topicChatMessages_${ openChatId } _${ nickname } ` ;
76+ const topicChatMbtiCacheKey = `topicChatMbti_${ openChatId } ` ;
7477
7578 const isTopicChat = mode === "topicChat" ;
7679
@@ -133,23 +136,59 @@ const Chat = () => {
133136 wsMessage . message
134137 ) ;
135138
136- const newMessage : Message = {
137- role : "assistant" ,
138- content : wsMessage . message ,
139- nickname : wsMessage . nickname ,
140- mbti : wsMessage . mbti || undefined ,
141- messageType : "text"
142- } ;
139+ // 웹소켓에서 받은 mbti가 없으면 기존 메시지에서 찾아서 사용
140+ setMessages ( ( prev ) => {
141+ // 같은 닉네임의 이전 메시지에서 mbti 찾기
142+ const existingMsg = prev
143+ . slice ( )
144+ . reverse ( )
145+ . find ( ( m ) => m . nickname === wsMessage . nickname ) ;
146+ // localStorage에서 mbti 찾기
147+ let cachedMbti = null ;
148+ if ( topicChatMbtiCacheKey && wsMessage . nickname ) {
149+ const mbtiCache = localStorage . getItem ( topicChatMbtiCacheKey ) ;
150+ if ( mbtiCache ) {
151+ try {
152+ const cacheData = JSON . parse ( mbtiCache ) ;
153+ cachedMbti = cacheData [ wsMessage . nickname ] ;
154+ } catch ( e ) {
155+ console . error ( "Failed to parse mbti cache" , e ) ;
156+ }
157+ }
158+ }
159+
160+ const userMbti =
161+ wsMessage . mbti ?? existingMsg ?. mbti ?? cachedMbti ?? undefined ;
162+
163+ // mbti가 있으면 localStorage에 저장
164+ if ( userMbti && topicChatMbtiCacheKey && wsMessage . nickname ) {
165+ const mbtiCache = localStorage . getItem ( topicChatMbtiCacheKey ) ;
166+ const cacheData = mbtiCache ? JSON . parse ( mbtiCache ) : { } ;
167+ cacheData [ wsMessage . nickname ] = userMbti ;
168+ localStorage . setItem (
169+ topicChatMbtiCacheKey ,
170+ JSON . stringify ( cacheData )
171+ ) ;
172+ }
173+
174+ const newMessage : Message = {
175+ role : "assistant" ,
176+ content : wsMessage . message ,
177+ nickname : wsMessage . nickname ?? undefined ,
178+ mbti : userMbti ,
179+ messageType : "text"
180+ } ;
143181
144- setMessages ( ( prev ) => [ ...prev , newMessage ] ) ;
182+ return [ ...prev , newMessage ] ;
183+ } ) ;
145184 }
146185
147186 // 예상치 못한 메시지 형식 로그
148187 else {
149188 console . warn ( "알 수 없는 WebSocket 메시지:" , wsMessage ) ;
150189 }
151190 } ,
152- [ nickname ]
191+ [ nickname , topicChatMbtiCacheKey ]
153192 ) ;
154193
155194 useEffect ( ( ) => {
@@ -180,14 +219,65 @@ const Chat = () => {
180219 role : msg . nickname === nickname ? "user" : "assistant" ,
181220 content : msg . message ,
182221 nickname : msg . nickname ,
183- mbti : msg . mbti || undefined ,
222+ mbti : msg . mbti ?? undefined ,
184223 messageType : msg . messageType || "text"
185224 } ;
186225 }
187226 ) ;
188227
189228 // 메시지 순서: API에서 최신순으로 오므로 reverse()로 시간순 정렬
190- setMessages ( convertedMessages . reverse ( ) ) ;
229+ const apiMessages = convertedMessages . reverse ( ) ;
230+
231+ // sessionStorage에서 이전 MBTI 정보 가져오기
232+ let cachedMessages : Message [ ] = [ ] ;
233+ if ( topicChatStorageKey ) {
234+ const storedMessage = sessionStorage . getItem ( topicChatStorageKey ) ;
235+ if ( storedMessage ) {
236+ cachedMessages = JSON . parse ( storedMessage ) ;
237+ }
238+ }
239+
240+ // API 메시지에 mbti가 없으면 캐시에서 찾아서 채우기
241+ const enhancedMessages = apiMessages . map ( ( msg ) => {
242+ if ( ! msg . mbti && msg . nickname ) {
243+ // sessionStorage에서 찾기 (우선순위 1)
244+ const cachedMsg = cachedMessages
245+ . slice ( )
246+ . reverse ( )
247+ . find ( ( m ) => m . nickname === msg . nickname ) ;
248+
249+ // localStorage에서 찾기 (우선순위 2)
250+ let localStorageMbti = null ;
251+ if ( topicChatMbtiCacheKey ) {
252+ const mbtiCache = localStorage . getItem ( topicChatMbtiCacheKey ) ;
253+ if ( mbtiCache ) {
254+ try {
255+ const cacheData = JSON . parse ( mbtiCache ) ;
256+ localStorageMbti = cacheData [ msg . nickname ] ;
257+ } catch ( e ) {
258+ console . error ( "Failed to parse mbti cache" , e ) ;
259+ }
260+ }
261+ }
262+
263+ const foundMbti = cachedMsg ?. mbti ?? localStorageMbti ;
264+ if ( foundMbti ) {
265+ return { ...msg , mbti : foundMbti } ;
266+ }
267+ } else if ( msg . mbti && topicChatMbtiCacheKey && msg . nickname ) {
268+ // mbti가 있으면 localStorage에 저장
269+ const mbtiCache = localStorage . getItem ( topicChatMbtiCacheKey ) ;
270+ const cacheData = mbtiCache ? JSON . parse ( mbtiCache ) : { } ;
271+ cacheData [ msg . nickname ] = msg . mbti ;
272+ localStorage . setItem (
273+ topicChatMbtiCacheKey ,
274+ JSON . stringify ( cacheData )
275+ ) ;
276+ }
277+ return msg ;
278+ } ) ;
279+
280+ setMessages ( enhancedMessages ) ;
191281 } else {
192282 setMessages ( [ ] ) ;
193283 }
@@ -281,17 +371,27 @@ const Chat = () => {
281371 if ( storedMessage ) {
282372 setMessages ( JSON . parse ( storedMessage ) ) ;
283373 }
374+ } else if ( isTopicChat && topicChatStorageKey ) {
375+ // sessionStorage에서 로드 (탭이 닫히지 않은 경우)
376+ const storedMessage = sessionStorage . getItem ( topicChatStorageKey ) ;
377+ if ( storedMessage ) {
378+ setMessages ( JSON . parse ( storedMessage ) ) ;
379+ }
380+ // sessionStorage가 없으면 API에서 로드된 데이터 사용
284381 }
285382 } ;
286383
287384 fetchMessages ( ) ;
288- } , [ mode , id , storageKey , isTopicChat ] ) ;
385+ } , [ mode , id , storageKey , isTopicChat , topicChatStorageKey ] ) ;
289386
290387 useEffect ( ( ) => {
291388 if ( mode !== "virtualFriend" && ! isTopicChat ) {
292389 sessionStorage . setItem ( storageKey , JSON . stringify ( messages ) ) ;
390+ } else if ( isTopicChat && topicChatStorageKey ) {
391+ // topicChat은 sessionStorage에 저장 (탭이 닫히면 날아감)
392+ sessionStorage . setItem ( topicChatStorageKey , JSON . stringify ( messages ) ) ;
293393 }
294- } , [ messages , mode , storageKey , isTopicChat ] ) ;
394+ } , [ messages , mode , storageKey , topicChatStorageKey , isTopicChat ] ) ;
295395
296396 useEffect ( ( ) => {
297397 bottomRef . current ?. scrollIntoView ( { behavior : "smooth" } ) ;
@@ -463,13 +563,16 @@ const Chat = () => {
463563 < div className = "mr-[9px] shrink-0" >
464564 { isTopicChat && msg . nickname ? (
465565 < div className = "text-center" >
466- < div className = "flex h-[36px] w-[36px] items-center justify-center rounded-full bg-primary-pale text-xs font-bold text-primary-normal" >
467- { msg . nickname . charAt ( 0 ) }
468- </ div >
469- { msg . mbti && (
470- < span className = "mt-1 text-xs text-gray-500" >
471- { msg . mbti }
472- </ span >
566+ { msg . mbti ? (
567+ < img
568+ src = { pickMbtiProfileImage ( msg . mbti ) }
569+ alt = "MBTI Profile"
570+ className = "h-[36px] w-[36px] rounded-full border border-gray-200 object-cover"
571+ />
572+ ) : (
573+ < div className = "flex h-[36px] w-[36px] items-center justify-center rounded-full bg-primary-pale text-xs font-bold text-primary-normal" >
574+ { msg . nickname . charAt ( 0 ) }
575+ </ div >
473576 ) }
474577 </ div >
475578 ) : (
0 commit comments