자동 스크롤

올챙이·2024년 8월 19일

ImpoliteChatbot

목록 보기
4/7
post-thumbnail

문제

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

useRef란?

리액트의 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

DOM 요소 조작

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
                                                     
profile
깃허브: https://github.com/sojeong0302

0개의 댓글