# 프론트엔드 메시지 중복 표시 문제 해결 ## 작성일: 2025-11-23 ## 작성자: admin ## 관련 서비스: frontend-customer, rb8001 ## 상태: ✅ 해결 완료 --- ## 문제 상황 ### 증상 - 사용자가 메시지 전송 시 RoBeing 응답이 **두 번** 표시됨 - 동일한 응답 메시지가 연속으로 나타남 - 예시: ``` 11/23 오전 12:08 김종태: hi 11/23 오전 12:08 RoBeing: 안녕하세요! 무엇을 도와드릴까요? 😊 11/23 오전 12:08 RoBeing: 안녕하세요! 무엇을 도와드릴까요? 😊 ← 중복 ``` --- ## 원인 분석 ### 핵심 문제: 메시지 ID 불일치로 인한 중복 표시 1. **새 메시지 전송 시** (`chat-interface.tsx:456-464`): - API 응답 받음 → `handleSend`에서 메시지 추가 - ID 생성: `id: (Date.now() + 1).toString()` (예: `"1732300800001"`) - DB 저장: `save_message_conversation` 호출 (main.py:110-115) 2. **히스토리 API 응답 구조** (`rb8001/main.py:717-744`): - DB에서 대화를 가져와 `user_message`와 `robeing_response`로 분리 - 각각 별도 메시지로 반환: - `id: f"{msg_id}_user"` (예: `"123_user"`) - `id: f"{msg_id}_robeing"` (예: `"123_robeing"`) 3. **중복 발생 시나리오**: ``` 사용자: "hi" 전송 ↓ handleSend 실행: - API 호출 → 응답 받음 - DB에 저장 (conversation_log_id = 123) - 프론트엔드에 메시지 추가: id = "1732300800001", text = "안녕하세요! 무엇을 도와드릴까요? 😊" ↓ (동시 또는 이후) loadInitialMessages useEffect 재실행: - user 객체 변경 감지 - /api/history 호출 - DB에서 방금 저장된 대화 조회 (id=123) - 히스토리 응답: id = "123_robeing", text = "안녕하세요! 무엇을 도와드릴까요? 😊" ↓ 결과: 같은 텍스트, 다른 ID → 두 번 표시됨 ``` ### 근본 원인 **1. `loadInitialMessages` useEffect가 `user` 변경 시마다 재실행** (1차 원인, 해결됨): - 의존성 배열: `[isAuthenticated, user]` (line 378) - `user` 객체는 참조가 변경될 수 있어 불필요한 재실행 발생 - 히스토리는 "로그인 성공 후 1번만" 불러오면 충분한데, `user`를 의존성에 넣어서 "유저 객체 변경 시마다" 다시 불러오는 것이 과함 **2. `loadInitialMessages`의 두 경로가 모두 `setMessages` 호출** (핵심 원인): - 경로 1: `getMessages()`로 히스토리 로드 → 병합 로직 실행 후 `return` - 경로 2: `getMessages()`가 빈 배열 반환 → `getUserHistory()`로 기본 메시지 생성 → `setMessages(initialMessages)` 실행 (덮어쓰기) - 문제: `getMessages()`가 빈 배열을 반환하면 `return`이 실행되지 않아 기본 메시지가 추가됨 - `setMessages(initialMessages)`는 덮어쓰기 방식이라, 이미 추가된 메시지와 병합되지 않음 - 결과: `handleSend`에서 추가한 메시지와 기본 메시지가 중복 표시됨 --- ## 해결 방안 ### 적용 1: useEffect 의존성에서 `user` 제거 **위치**: `frontend-customer/src/components/chat-interface.tsx:378` **수정 전**: ```typescript }, [isAuthenticated, user]); // config와 historyLoaded 제거 - 무한 루프 방지 ``` **수정 후**: ```typescript }, [isAuthenticated]); // user 제거 - 히스토리 중복 로드 방지 ``` **효과**: - `user` 변경 시 `loadInitialMessages`가 재실행되지 않음 - 히스토리는 로그인 성공 시(`isAuthenticated` 변경) 1번만 로드 ### 적용 2: 히스토리 로드 실패 시 기본 메시지 추가 방지 **위치**: `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(initialMessages); // 덮어쓰기 ``` **수정 후**: ```typescript // initialMessages가 있을 때만 병합 방식으로 추가 (덮어쓰기 방지) if (initialMessages.length > 0) { setMessages(prev => { // 기존 메시지가 있으면 병합, 없으면 새로 설정 if (prev.length === 0) { return initialMessages; } // 기존 메시지와 병합하면서 중복 제거 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; }); } ``` **효과**: - `setMessages(initialMessages)`의 덮어쓰기 문제 해결 - `handleSend`에서 추가한 메시지와 기본 메시지를 병합하여 중복 방지 - timestamp + sender + text 기준으로 중복 체크하여 같은 메시지가 두 번 추가되지 않음 --- ### 적용 4: 비로그인 사용자 기본 메시지 중복 추가 방지 **위치**: `frontend-customer/src/components/chat-interface.tsx:228-243` **문제**: - 비로그인 사용자가 메시지를 보낸 후 페이지 새로고침 시 기본 인사 메시지가 중복으로 추가됨 **수정 내용**: - 비로그인 사용자일 때도 이미 메시지가 있으면 기본 메시지를 추가하지 않도록 수정 - `messages.length > 0` 체크 추가하여 메시지가 있을 때는 `return`하여 기본 메시지 추가 방지 **효과**: - 페이지 새로고침 후에도 기본 메시지가 중복으로 추가되지 않음 - 비로그인 사용자도 메시지가 있을 때는 기본 메시지를 추가하지 않음 ## 검증 ### 테스트 시나리오 1. 로그인 후 메시지 전송 → 응답이 한 번만 표시되는지 확인 2. 페이지 새로고침 후 히스토리가 정상 로드되는지 확인 3. **비로그인 상태에서 메시지 전송 → 페이지 새로고침 → 기본 메시지가 중복으로 추가되지 않는지 확인** --- ## 교훈 1. **useEffect 의존성 최소화**: 필요한 경우에만 의존성 배열에 포함 2. **히스토리 로드 타이밍**: 로그인 완료 시 한 번만 로드하는 것이 일반적 3. **메시지 ID 통일**: 향후 백엔드 `message_id`를 프론트엔드에서 활용하여 ID 기반 중복 체크 고려 --- ## 관련 파일 - `frontend-customer/src/components/chat-interface.tsx` (수정됨) - `rb8001/main.py` (히스토리 API) - `rb8001/app/state/database.py` (대화 조회) --- **문서 끝**