diff --git a/journey/troubleshooting/251123_frontend_message_duplicate_display_fix.md b/journey/troubleshooting/251123_frontend_message_duplicate_display_fix.md index f78ed2c..63f7fd8 100644 --- a/journey/troubleshooting/251123_frontend_message_duplicate_display_fix.md +++ b/journey/troubleshooting/251123_frontend_message_duplicate_display_fix.md @@ -62,14 +62,12 @@ - `user` 객체는 참조가 변경될 수 있어 불필요한 재실행 발생 - 히스토리는 "로그인 성공 후 1번만" 불러오면 충분한데, `user`를 의존성에 넣어서 "유저 객체 변경 시마다" 다시 불러오는 것이 과함 -**2. `setMessages(sortedMessages)`와 `handleSend`의 `setMessages(prev => [...prev, ...])` 비동기 경쟁** (핵심 원인): -- `loadInitialMessages`의 `setMessages(sortedMessages)`는 전체 덮어쓰기 -- `handleSend`의 `setMessages(prev => [...prev, robeingMessage])`는 추가 -- 두 비동기 작업이 동시에 실행되면: - - 히스토리 로드가 먼저 끝나면 `setMessages(sortedMessages)`로 덮어쓰기 - - 이후 `handleSend`의 `setMessages(prev => [...prev, robeingMessage])`가 실행되면 - - 히스토리에 이미 포함된 메시지와 동일한 내용이 한 번 더 추가됨 - - 결과: 같은 텍스트, 다른 ID → 두 번 표시됨 +**2. `loadInitialMessages`의 두 경로가 모두 `setMessages` 호출** (핵심 원인): +- 경로 1: `getMessages()`로 히스토리 로드 → 병합 로직 실행 후 `return` +- 경로 2: `getMessages()`가 빈 배열 반환 → `getUserHistory()`로 기본 메시지 생성 → `setMessages(initialMessages)` 실행 (덮어쓰기) +- 문제: `getMessages()`가 빈 배열을 반환하면 `return`이 실행되지 않아 기본 메시지가 추가됨 +- `setMessages(initialMessages)`는 덮어쓰기 방식이라, 이미 추가된 메시지와 병합되지 않음 +- 결과: `handleSend`에서 추가한 메시지와 기본 메시지가 중복 표시됨 --- @@ -93,47 +91,66 @@ - `user` 변경 시 `loadInitialMessages`가 재실행되지 않음 - 히스토리는 로그인 성공 시(`isAuthenticated` 변경) 1번만 로드 -### 적용 2: 히스토리 메시지 병합 및 중복 제거 +### 적용 2: 히스토리 로드 실패 시 기본 메시지 추가 방지 -**위치**: `frontend-customer/src/components/chat-interface.tsx:272` +**위치**: `frontend-customer/src/components/chat-interface.tsx:250-306` + +**수정 내용**: +- `getMessages()`가 빈 배열을 반환해도, 이미 메시지가 있으면 기본 메시지를 추가하지 않도록 수정 +- `historyLoadedSuccessfully` 플래그 추가하여 히스토리 로드 성공 여부 추적 +- 히스토리 로드 실패 시에도 이미 메시지가 있으면 `return`하여 기본 메시지 추가 방지 + +**효과**: +- `getMessages()`가 빈 배열을 반환해도 불필요한 기본 메시지 추가 방지 +- 이미 메시지가 있는 상태에서 `loadInitialMessages`가 재실행되어도 중복 방지 + +### 적용 3: 기본 메시지 병합 방식으로 변경 + +**위치**: `frontend-customer/src/components/chat-interface.tsx:394-422` **수정 전**: ```typescript -setMessages(sortedMessages); // 전체 덮어쓰기 +setMessages(initialMessages); // 덮어쓰기 ``` **수정 후**: ```typescript -// 기존 메시지와 병합하면서 중복 제거 (timestamp + sender + text 기준) -setMessages(prev => { - const existingKeys = new Set( - prev.map(m => `${m.timestamp.getTime()}_${m.sender}_${m.text}`) - ); - const newMessages = sortedMessages.filter(msg => { - const msgTimestamp = msg.timestamp instanceof Date - ? msg.timestamp.getTime() - : new Date(msg.timestamp).getTime(); - const key = `${msgTimestamp}_${msg.sender}_${msg.text}`; - return !existingKeys.has(key); - }); - // 기존 메시지와 새 메시지를 시간순으로 병합 - const merged = [...prev, ...newMessages].sort((a, b) => { - const timeA = a.timestamp.getTime(); - const timeB = b.timestamp.getTime(); - if (timeA === timeB) { - return a.sender === 'user' ? -1 : 1; +// initialMessages가 있을 때만 병합 방식으로 추가 (덮어쓰기 방지) +if (initialMessages.length > 0) { + setMessages(prev => { + // 기존 메시지가 있으면 병합, 없으면 새로 설정 + if (prev.length === 0) { + return initialMessages; } - return timeA - timeB; + // 기존 메시지와 병합하면서 중복 제거 + const existingKeys = new Set( + prev.map(m => `${m.timestamp.getTime()}_${m.sender}_${m.text}`) + ); + const newMessages = initialMessages.filter(msg => { + const msgTimestamp = msg.timestamp instanceof Date + ? msg.timestamp.getTime() + : new Date(msg.timestamp).getTime(); + const key = `${msgTimestamp}_${msg.sender}_${msg.text}`; + return !existingKeys.has(key); + }); + // 기존 메시지와 새 메시지를 시간순으로 병합 + const merged = [...prev, ...newMessages].sort((a, b) => { + const timeA = a.timestamp.getTime(); + const timeB = b.timestamp.getTime(); + if (timeA === timeB) { + return a.sender === 'user' ? -1 : 1; + } + return timeA - timeB; + }); + return merged; }); - return merged; -}); +} ``` **효과**: -- `setMessages(sortedMessages)`의 전체 덮어쓰기 문제 해결 -- `handleSend`의 `setMessages(prev => [...prev, robeingMessage])`와 비동기 경쟁 상황에서도 중복 방지 +- `setMessages(initialMessages)`의 덮어쓰기 문제 해결 +- `handleSend`에서 추가한 메시지와 기본 메시지를 병합하여 중복 방지 - timestamp + sender + text 기준으로 중복 체크하여 같은 메시지가 두 번 추가되지 않음 -- 기존 메시지와 히스토리 메시지를 시간순으로 병합하여 정상 표시 ---