diff --git a/src/app/(route)/chat/[id]/ChatPage.tsx b/src/app/(route)/chat/[id]/ChatPage.tsx index 6fa1cdc8..7af6ed0b 100644 --- a/src/app/(route)/chat/[id]/ChatPage.tsx +++ b/src/app/(route)/chat/[id]/ChatPage.tsx @@ -10,6 +10,7 @@ import CopyButton from '@/components/wrappers/CopyButton'; import LinkCardDetailPanel from '@/components/wrappers/LinkCardDetailPanel/LinkCardDetailPanel'; import ReportModal from '@/components/wrappers/ReportModal/ReportModal'; import { useChatStream } from '@/hooks/server/Chats/useChatStream'; +import useKeyboardInset from '@/hooks/util/useKeyboardInset'; import { trackEvent, trackQueryFeedback } from '@/lib/client/analytics'; import { useModalStore } from '@/stores/modalStore'; import { showToast } from '@/stores/toastStore'; @@ -106,6 +107,8 @@ export default function Chat() { const [historyBootstrapped, setHistoryBootstrapped] = useState(false); const [showResponseBottomGap, setShowResponseBottomGap] = useState(false); const [responseBottomGapHeight, setResponseBottomGapHeight] = useState(0); + // iOS Safari에서 소프트 키보드가 가린 높이. Android는 dvh가 처리하므로 0이다. + const keyboardInset = useKeyboardInset(); const scrollRootRef = useRef(null); const latestUserMessageRef = useRef(null); @@ -456,7 +459,8 @@ export default function Chat() { const nextGapHeight = Math.max(root.clientHeight - latestUserMessage.offsetHeight, 0); setResponseBottomGapHeight(prev => (Math.abs(prev - nextGapHeight) < 1 ? prev : nextGapHeight)); scrollLatestQuestionToTop('auto'); - }, [messages, scrollLatestQuestionToTop, showResponseBottomGap]); + // keyboardInset이 바뀌면 스크롤 영역 높이도 바뀌므로 스페이서를 다시 계산한다. + }, [messages, scrollLatestQuestionToTop, showResponseBottomGap, keyboardInset]); useEffect(() => { const adjust = pendingScrollAdjustRef.current; @@ -654,12 +658,21 @@ export default function Chat() { }, [latestUserMessageIndex, messages]); return ( -
-
+
+
+ {/* 스크롤되는 메시지가 좌상단 고정 메뉴 버튼 뒤로 비치지 않도록 가리는 스크림 */} +