
스크롤이 계속 위에 고정되어있기 때문에 사용자가 스크롤을 직접 내려야하는 번거로움이 존재했습니다. 따라서 스크롤이 항상 아래에 고정되게끔 자동으로 관리해주려고 합니다.

리액트의 Ref를 이용하면 돔(DOM) 요소들을 직접 조작할 수 있습니다.
Ref는 Ref-erence의 줄임말로 참고라는 뜻입니다.
돔(Document Object Model)
자바스크립트는 웹 문서와 그 안에서 사용한 텍스트, 이미지, 표 등의 모든 요소를 각각 다른 객체로 인지하여 처리합니다. 이렇게 자바스크립트에서 웹 문서의 객체를 다루는 시스템을 DOM이라고 합니다.Do it! 한권으로 끝내는 웹 기본 교과서 HTML+CSS+자바스크립트 웹 표준의 정석 P.601
useEffect와 함께 쓸거라 같이 import해주었습니다.
1 | import React, { useRef, useEffect } from "react"; | cs |
스크롤 위치를 조정하기 위한 객체를 생성해줍니다.
1 | const chatingRef = useRef(null); | cs |
1 2 3 4 5 | useEffect(() => { if (chatingRef.current) { chatingRef.current.scrollTop = chatingRef.current.scrollHeight; } }, [userQuestions, chatbotAnswers]); | cs |
chatingRef.current
current 속성은 useRef를 사용하여 생성한 DOM 요소에 대한 참조입니다.
null로 초기화 했기 때문에 chatingRef.current의 초기값은 null입니다.
scrollTop
스크롤 위치를 나타냅니다.
ex) scrollTop = 0 은 맨 위로 스크롤된 상태
scrollHeight
스크롤 가능한 전체 높이를 나타냅니다.
chatingRef.current.scrollTop = chatingRef.current.scrollHeight
scrollTop의 값을 scrollHeight로 대체해주면서 scrollTop를 최대값으로 만들어줍니다.
이를 통해 스크롤은 맨 아래에 위치하게 됩니다.
돔을 지정해줍니다.
1 2 3 | <Chating ref={chatingRef}> {/* 스크롤 채팅 내용 */} </Chating> | cs |

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | import React, { useRef, useEffect } from "react"; import { ChatContainer, ChatElement, ChatingDiv, Chating, Chatbot, Profile, Answer, User, Question, ChatSend, QuestionInput, SendImg, } from "./ChatPage.style.js"; import { useSelector, useDispatch } from "react-redux"; import { setQuestionInput, addUserQuestion, addChatbotAnswer } from "../../redux/actions"; const ChatPage = () => { const questionInput = useSelector((state) => state.questionInput); const userQuestions = useSelector((state) => state.userQuestions); const chatbotAnswers = useSelector((state) => state.chatbotAnswers); const dispatch = useDispatch(); const chatingRef = useRef(null); const handleSend = () => { if (questionInput.trim() === "") return; console.log(`입력된 질문: ${questionInput}`); dispatch(addUserQuestion(questionInput)); dispatch(addChatbotAnswer("(임시)나는 챗봇이야.")); dispatch(setQuestionInput("")); }; const handleKeyDown = (e) => { if (e.key === "Enter") { handleSend(); } }; useEffect(() => { if (chatingRef.current) { chatingRef.current.scrollTop = chatingRef.current.scrollHeight; } }, [userQuestions, chatbotAnswers]); return ( <> <ChatContainer> <ChatElement> <ChatingDiv> <Chating ref={chatingRef}> {userQuestions.map((question, index) => ( <React.Fragment key={`user-question-${index}`}> <User> <Question>{question}</Question> </User> {chatbotAnswers[index] && ( <Chatbot> <Profile src='/image/Impolite_chatbot.png' alt='챗봇 프로필' /> <Answer>{chatbotAnswers[index]}</Answer> </Chatbot> )} </React.Fragment> ))} </Chating> </ChatingDiv> <ChatSend> <QuestionInput value={questionInput} onChange={(e) => dispatch(setQuestionInput(e.target.value))} onKeyDown={handleKeyDown} placeholder='Chatbot에게 하고싶은 말을 입력해주세요!' /> <SendImg onClick={handleSend} src='/image/Send.png' alt='화살표' /> </ChatSend> </ChatElement> </ChatContainer> </> ); }; export default ChatPage; | cs |