From f629e87fac1bd7899fe3b4c54bb7c0cdd9dee22d Mon Sep 17 00:00:00 2001 From: ahk0413 Date: Thu, 2 Oct 2025 19:37:34 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[feat]=20=EB=A1=9C=EB=94=A9=20,=20fetch=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=ED=95=A8=EC=88=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recommend/components/ChatSection.tsx | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/domains/recommend/components/ChatSection.tsx b/src/domains/recommend/components/ChatSection.tsx index 2f29080..a8bb131 100644 --- a/src/domains/recommend/components/ChatSection.tsx +++ b/src/domains/recommend/components/ChatSection.tsx @@ -34,6 +34,33 @@ function ChatSection() { selectedCocktailType?: string; }>({}); + const handleSendMessage = async (payload: stepPayload | { message: string; userId: string }) => { + const typingTimer = setTimeout(() => setIsBotTyping(true), 300); + + try { + if (!('currentStep' in payload)) { + const botMessage = await fetchSendTextMessage(payload); + clearTimeout(typingTimer); + setIsBotTyping(false); + + if (!botMessage) return; + setTimeout(() => setMessages((prev) => [...prev, botMessage]), 500); + return; + } + + const botMessage = await fetchSendStepMessage(payload); + clearTimeout(typingTimer); + setIsBotTyping(false); + + if (!botMessage) return; + setTimeout(() => setMessages((prev) => [...prev, botMessage]), 500); + } catch (err) { + clearTimeout(typingTimer); + setIsBotTyping(false); + console.error(err); + } + }; + // 일반 텍스트 보낼 시 const handleSubmitText = async (message: string) => { const userId = useAuthStore.getState().user?.id; @@ -48,8 +75,7 @@ function ChatSection() { { id: tempId, userId, message, sender: 'USER', type: 'text', createdAt: tempCreatedAt }, ]); - const botMessage = await fetchSendTextMessage({ message, userId }); - if (botMessage) setMessages((prev) => [...prev, botMessage]); + await handleSendMessage({ message, userId }); }; // 옵션 클릭 시 @@ -104,22 +130,7 @@ function ChatSection() { ...selectedOptions.current, }; - const typingTimer = setTimeout(() => setIsBotTyping(true), 300); - - try { - const botMessage = await fetchSendStepMessage(payload); - - clearTimeout(typingTimer); - setIsBotTyping(false); - - if (botMessage) { - setMessages((prev) => [...prev, botMessage]); - } - } catch (err) { - clearTimeout(typingTimer); - setIsBotTyping(false); - console.error(err); - } + await handleSendMessage(payload); }; // 채팅 기록 불러오기 없으면 greeting 호출 From 45f9d6b34d8f85cfbcfdc3967e6ff11282cb225a Mon Sep 17 00:00:00 2001 From: ahk0413 Date: Thu, 2 Oct 2025 20:24:43 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[style]=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/recommend/page.tsx | 2 +- src/domains/recommend/components/ChatList.tsx | 2 +- src/domains/recommend/components/ChatSection.tsx | 2 +- src/domains/recommend/components/bot/NewMessageAlert.tsx | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/recommend/page.tsx b/src/app/recommend/page.tsx index c49c438..a324aac 100644 --- a/src/app/recommend/page.tsx +++ b/src/app/recommend/page.tsx @@ -4,7 +4,7 @@ import Bg from '@/shared/assets/images/recommend_bg.webp'; function Page() { return (

취향추천하기

diff --git a/src/domains/recommend/components/ChatList.tsx b/src/domains/recommend/components/ChatList.tsx index 56eb6f3..bd0ffcb 100644 --- a/src/domains/recommend/components/ChatList.tsx +++ b/src/domains/recommend/components/ChatList.tsx @@ -53,8 +53,8 @@ function ChatList({ {isBotTyping && }
- {showNewMessageAlert && }
+ {showNewMessageAlert && } ); } diff --git a/src/domains/recommend/components/ChatSection.tsx b/src/domains/recommend/components/ChatSection.tsx index a8bb131..335556e 100644 --- a/src/domains/recommend/components/ChatSection.tsx +++ b/src/domains/recommend/components/ChatSection.tsx @@ -156,7 +156,7 @@ function ChatSection() { }; return ( -
+

대화 목록 및 입력 창

Date: Thu, 2 Oct 2025 20:35:18 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[refactor]=20scroll=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?chatlist=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/recommend/components/ChatList.tsx | 16 ++++++---------- src/domains/recommend/components/ChatSection.tsx | 12 +----------- src/domains/recommend/types/recommend.ts | 6 ------ 3 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/domains/recommend/components/ChatList.tsx b/src/domains/recommend/components/ChatList.tsx index bd0ffcb..a5dd219 100644 --- a/src/domains/recommend/components/ChatList.tsx +++ b/src/domains/recommend/components/ChatList.tsx @@ -1,3 +1,4 @@ +import { useChatScroll } from '../hook/useChatScroll'; import { ChatListProps } from '../types/recommend'; import BotMessage from './bot/BotMessage'; import NewMessageAlert from './bot/NewMessageAlert'; @@ -9,13 +10,11 @@ function ChatList({ userCurrentStep, onSelectedOption, getRecommendations, - chatListRef, - chatEndRef, - showNewMessageAlert, - handleCheckBottom, - handleScrollToBottom, isBotTyping, }: ChatListProps) { + const { chatListRef, chatEndRef, showNewMessageAlert, handleCheckBottom, handleScrollToBottom } = + useChatScroll(messages.length); + return (
- {messages.map((msg, idx) => { - const isLastMessage = idx === messages.length - 1; - const showTyping = isLastMessage && msg.sender === 'CHATBOT' && isBotTyping; - + {messages.map((msg) => { if (msg.sender === 'USER') { return ; } @@ -53,8 +49,8 @@ function ChatList({ {isBotTyping && }
+ {showNewMessageAlert && }
- {showNewMessageAlert && }
); } diff --git a/src/domains/recommend/components/ChatSection.tsx b/src/domains/recommend/components/ChatSection.tsx index 335556e..0519821 100644 --- a/src/domains/recommend/components/ChatSection.tsx +++ b/src/domains/recommend/components/ChatSection.tsx @@ -1,11 +1,7 @@ 'use client'; import { useEffect, useRef, useState } from 'react'; -import BotMessage from './bot/BotMessage'; -import UserMessage from './user/UserMessage'; -import NewMessageAlert from './bot/NewMessageAlert'; import MessageInput from './user/MessageInput'; -import { useChatScroll } from '../hook/useChatScroll'; import { fetchChatHistory, fetchGreeting, @@ -23,8 +19,7 @@ import ChatList from './ChatList'; function ChatSection() { const [messages, setMessages] = useState([]); - const { chatListRef, chatEndRef, showNewMessageAlert, handleCheckBottom, handleScrollToBottom } = - useChatScroll(messages.length); + const [userCurrentStep, setUserCurrentStep] = useState(0); const [isBotTyping, setIsBotTyping] = useState(false); @@ -163,11 +158,6 @@ function ChatSection() { userCurrentStep={userCurrentStep} onSelectedOption={handleSelectedOption} getRecommendations={getRecommendations} - chatListRef={chatListRef} - chatEndRef={chatEndRef} - showNewMessageAlert={showNewMessageAlert} - handleCheckBottom={handleCheckBottom} - handleScrollToBottom={handleScrollToBottom} isBotTyping={isBotTyping} /> diff --git a/src/domains/recommend/types/recommend.ts b/src/domains/recommend/types/recommend.ts index fb98747..2ee1dde 100644 --- a/src/domains/recommend/types/recommend.ts +++ b/src/domains/recommend/types/recommend.ts @@ -60,11 +60,5 @@ export interface ChatListProps { stepData?: StepRecommendation | null ) => RecommendationItem[]; - chatListRef: React.RefObject; - chatEndRef: React.RefObject; - showNewMessageAlert: boolean; - handleCheckBottom: (e: React.UIEvent) => void; - handleScrollToBottom: () => void; - isBotTyping: boolean; } From f109e422627a5d52867d8fa8a934204c3cd21e46 Mon Sep 17 00:00:00 2001 From: ahk0413 Date: Thu, 2 Oct 2025 21:33:22 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[feat]=20input=20=EB=B0=B1=EA=B7=B8?= =?UTF-8?q?=EB=9D=BC=EC=9A=B4=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/recommend/components/ChatList.tsx | 4 ++-- .../recommend/components/ChatSection.tsx | 5 +---- .../recommend/components/user/MessageInput.tsx | 6 +++--- src/domains/recommend/hook/useChatScroll.ts | 17 ++++++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/domains/recommend/components/ChatList.tsx b/src/domains/recommend/components/ChatList.tsx index a5dd219..ae2286a 100644 --- a/src/domains/recommend/components/ChatList.tsx +++ b/src/domains/recommend/components/ChatList.tsx @@ -13,13 +13,13 @@ function ChatList({ isBotTyping, }: ChatListProps) { const { chatListRef, chatEndRef, showNewMessageAlert, handleCheckBottom, handleScrollToBottom } = - useChatScroll(messages.length); + useChatScroll(messages[messages.length - 1]?.id); return (
{messages.map((msg) => { diff --git a/src/domains/recommend/components/ChatSection.tsx b/src/domains/recommend/components/ChatSection.tsx index 0519821..6aff917 100644 --- a/src/domains/recommend/components/ChatSection.tsx +++ b/src/domains/recommend/components/ChatSection.tsx @@ -113,9 +113,6 @@ function ChatSection() { case 3: selectedOptions.current.selectedAlcoholBaseType = value; break; - case 4: - selectedOptions.current.selectedCocktailType = value; - break; } const payload: stepPayload = { @@ -151,7 +148,7 @@ function ChatSection() { }; return ( -
+

대화 목록 및 입력 창

-
e.preventDefault()}> -
+
+ e.preventDefault()} className="w-full max-w-[64rem]"> +
diff --git a/src/domains/recommend/hook/useChatScroll.ts b/src/domains/recommend/hook/useChatScroll.ts index 5c91aa9..abf5502 100644 --- a/src/domains/recommend/hook/useChatScroll.ts +++ b/src/domains/recommend/hook/useChatScroll.ts @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from 'react'; -export const useChatScroll = (messagesLength: number) => { +export const useChatScroll = (lastMessageId: string) => { const chatEndRef = useRef(null); const chatListRef = useRef(null); const isScrollBottom = useRef(true); @@ -17,13 +17,16 @@ export const useChatScroll = (messagesLength: number) => { // 새 메시지가 들어오면 자동 스크롤 useEffect(() => { - if (isScrollBottom.current) { - chatEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - setShowNewMessageAlert(false); // 새메세지 숨김 - } else { - setShowNewMessageAlert(true); // 새메세지 보여줌 + if (!isScrollBottom.current) { + setShowNewMessageAlert(true); + return; } - }, [messagesLength]); + + requestAnimationFrame(() => { + chatEndRef.current?.scrollIntoView({ behavior: 'auto' }); + setShowNewMessageAlert(false); + }); + }, [lastMessageId]); // 스크롤 제일 아래로 const handleScrollToBottom = () => { From e5423c6f2b3cbf83cb0845fc6582a4213170d15f Mon Sep 17 00:00:00 2001 From: ahk0413 Date: Sat, 4 Oct 2025 15:13:09 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[fix]=20cancelanimation=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/domains/recommend/hook/useChatScroll.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/domains/recommend/hook/useChatScroll.ts b/src/domains/recommend/hook/useChatScroll.ts index abf5502..96a093e 100644 --- a/src/domains/recommend/hook/useChatScroll.ts +++ b/src/domains/recommend/hook/useChatScroll.ts @@ -22,10 +22,12 @@ export const useChatScroll = (lastMessageId: string) => { return; } - requestAnimationFrame(() => { + const frameId = requestAnimationFrame(() => { chatEndRef.current?.scrollIntoView({ behavior: 'auto' }); setShowNewMessageAlert(false); }); + + return () => cancelAnimationFrame(frameId); }, [lastMessageId]); // 스크롤 제일 아래로