스크롤을 항상 최하단으로 유지하는 방법

팀 프로젝트 진행 중 채팅 봇 개발을 위해 채팅창을 퍼블리싱 하고 있었는데 채팅이 많아져 스크롤이 생겼을 때 항상 최신의 채팅 내역을 보여주기 위해 채팅창의 최하단을 항상 보여주고 싶어서 이것저것 알아봤는데 내가 떠올리지 못했던 방향으로 해결 하게 되어 포스팅을 하게됐다.

만든 채팅창 모습

import React, { useState, useEffect, useRef } from "react";

const AiChatBtn: React.FC<AiChatBtnProps> = ({
  onClick,
  children,
  className = "",
}) => {
 // 타 상수값들 생략
  const messagesEndRef = useRef<HTMLDivElement>(null);

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
  };

  useEffect(scrollToBottom, [messages]);

  //handleSendMessage 코드 생략

  return (
    <div>
      <div className="bg-white rounded-lg shadow-xl fixed bottom-24 right-10 w-[300px] h-[480px] flex flex-col">
            // 채팅창 헤더 jsx 코드
        <div className="flex-grow p-4 overflow-y-auto bg-gray-100">
          // 채팅창 jsx 코드
          <div ref={messagesEndRef} /> {/* 스크롤을 위한 참조 요소 useRef 이용 */}
        </div>
       // input창 jsx 코드
            />
            <button
             //button jsx 코드
      </button>
    </div>
  );
};

export default AiChatBtn;

useRef

동적인 ui를 만들다보면 특정 요소에만 접근하고 싶은 순간이 있다. tailwind css를 사용하기 전에는 className을 이용하여 접근하곤 했는데 tailwind css를 사용한 후로는 useRef라는 훅을 이용하여 div에 id 값을 매기듯이 지정한 후 참조하는 방식을 사용하고 있다.

위에 제시한 코드에서는 <div ref={messagesEndRef} />로 ref에 이 빈 div를 참조시켰다. 왜 빈 div를 참조시켰는지는 후에 설명하는 다른 내용들을 보면 알 수 있을 것이다.

scrollIntoView()

이번 주제로 포스팅을 하게 된 이유인데 그동안은 window.scrollTo() 이라던가 element.scrollTop 같은 메서드를 사용해왔는데 이번에는 scrollIntoView()를 사용하였다. 이 메서드에 대해 좀 더 알아보자

기본 사용법

element.scrollIntoView(options); 로 사용하고 option 값의 구성은

  • behavior: 스크롤 애니메이션을 정의한다.

    • "auto" (기본값): 즉시 스크롤
    • "smooth": 부드러운 애니메이션과 함께 스크롤
  • block: 요소의 수직 정렬을 정의한다.

    • "start": 요소를 뷰포트의 상단에 맞춘다.
    • "center": 요소를 뷰포트의 중앙에 맞춘다.
    • "end": 요소를 뷰포트의 하단에 맞춘다.
    • "nearest" (기본값): 가장 가까운 위치로 스크롤한다.

라는 하나의 메서드에서 옵션값만을 변경하여 다양하게 활용할 수 있다는 점을 알게되어 이번에 사용하게 되었고 그 중에서도 기본값이 nearest를 활용하기 위하여 채팅창의 최하단에 height가 0 인 빈 div를 선언하고 그것을 참조하는 ref를 만들어 해당 ref에 가까운 위치 즉 채팅창의 최하단으로 이동되게끔 하였다. 그리고 smooth 같은 behavior 값을 사용하면 transition을 적용한 것처럼 작동하여 사용자 경험도 쉽게 향상 시킬 수 있었다.

마치며

그래서 결국 기능이 채팅창의 최하단으로 스크롤을 고정시키는 것이라면 앞서 소개했던 두 개의 메서드가 직관성 측면에선 더 좋은게 아니냐는 생각이 들기도 했는데, useRef와 view를 중심으로 한 scrollIntoView() 를 활용하면 차후에 특정 내용이 담긴 div를 찾아가는 기능을 만드는 등의 확장성에 있어서 해당 메서드를 좀 더 높게 평가했고 이번엔 그것을 위한 경험을 해봤다는 것에 의의를 뒀다. 작은 채팅창 하나를 만드는데도 고려 해야 할 부분이 참 많다는 생각이 든다.

이 글이 도움이 되었나요?

신고하기
0분 전
작성된 댓글이 없습니다. 첫 댓글을 달아보세요!
    댓글을 작성하려면 로그인이 필요합니다.