실시간 채팅창을 구현하다가 마주한 문제였다. 따로 설정을 해두지 않으면 채팅방을 처음 열었을 때 스크롤이 맨 위에서부터 시작되어 최근 메세지를 보려면 직접 사용자가 스크롤을 내려야했다. 물론 이렇게 구현할 수 없으니 리액트에서 스크롤을 어떻게 조작 할 수 있을까?
우선 JavaScript를 기준으로 생각해보면 컨트롤 하고 싶은 영역의 id나 class로 div를 선택해야한다. 그리고 선택한 요소의 스크롤 위치를 제어할 수 있어야하며, 이 과정들은 유저가 채팅방을 오픈했을 때 한번만 실행되도록 해야한다. 이렇게 3가지를 리액트 환경에서 하나씩 해결해보자.
1. useRef로 DOM 선택하기 / target 설정
우선 스크롤을 조작하고 싶은 DOM을 강제로 가져와야한다. 즉 Real DOM을 직접 선택해야하는 상황이다. 그냥 바닐라 자바스크립트로 조작할 때에는 getElementById나 querySelector를 이용하여 조작하고 싶은 DOM 요소를 선택할 수 있었다. 하지만 리액트에서는 Virtual DOM을 통해 Real DOM을 그리기 때문에 DOM을 직접 건드리는 DOM Selector들을 자주 사용하는 것을 지양도록 권장한다. 그래서 리액트가 제어하는 영역을 벗어나지 않으면서 DOM Selector를 사용하려면 리액트의 라이프 사이클과 함께 동작하는 useRef를 사용할 것이다. 사용법은 아래와 같이 간단하다.
ChatRoom.js
import { useRef } from "react";
function ChatRoom(){
const scrollRef = useRef();
console.log(scrollRef.current); // ChatWrapper 출력
return (
<ChatWrapper ref={scrollRef}>
//Contents
</ChatWrapper>
);
};
export default ChatRoom;
위 예시처럼 스크롤 제어를 하고 싶은 element에 ref 속성을 부여하면 해당 요소를 선택할 수 있다. 참고로 리액트를 사용하는 프로젝트를 하며 특정 element의 크기가 필요하거나 포커스 설정 등 직접 DOM을 선택해야하는 상황이 발생할 때 마찬가지로 useRef를 활용할 수 있다.
2. 스크롤 위치 제어
먼저 순수 JavaScript로 특정 div의 현재 스크롤 위치는 이렇게 구할 수 있다.
const chatDiv = document.getElementById("ChatRoom");
const nowScrollY = chatDiv.scrollTop;
console.log(nowScrollY);
// 0
그리고 스크롤 위치를 맨 아래로 내려주어야 하니까 맨 아래에 있을 때의 스크롤 위치를 구해야한다. 바꾸고 싶은 스크롤 위치는 스크롤 영역 높이로 설정해주면 된다.
const scrollHeight = chatDiv.scrollHeight;
console.log(scrollHeight);
//83
즉 스크롤 위치 조작은 다음과 같다.
const chatDiv = document.getElementById("ChatRoom");
chatDiv.scrollTop = chat.scrollHeight;
// 현재 스크롤 위치를 맨 아래(컨텐츠 전체 높이)로 변경
3. 이벤트 발생 지점 설정
채팅방을 처음 열었을 때 스크롤이 맨 아래에 위치한 상태로 채팅방 UI가 렌더링 되어야한다. 스크롤을 올린채로 채팅방을 닫고 다시 열었을 때에도 마찬가지다. 다시 말하자면 처음 ChatRoom의 렌더링이 완료될 때마다 실행되어야한다. 컴포넌트가 렌더링 된 후 실행시키고 싶은 작업이 있을 때에는 useEffect라는 hook을 사용하여 구현 할 수 있다.
useEffect는 몇가지 사용되는 형식이 있는데 "렌더링이 완료될 때마다 실행"하는 방법을 사용할 것이다. useEffect에 대한 자세한 사항은 따로 정리할 예정이며 앞서 정리한 내용들로 최종 ChatRoom 컴포넌트를 정리하면 다음과 같다.
ChatRoom.js
import { useRef, useEffect } from "react";
function ChatRoom(){
const scrollRef = useRef();
useEffect(() => {
// 현재 스크롤 위치 === scrollRef.current.scrollTop
// 스크롤 길이 === scrollRef.current.scrollHeight
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
});
return (
<ChatWrapper ref={scrollRef}>
//Contents
</ChatWrapper>
);
};
export default ChatRoom;
결론
이와 같은 방법으로 채팅방을 열었을 때 스크롤이 맨 아래부터 시작되어 익숙하게 접하던 다른 채팅 서비스처럼 가장 최신의 채팅 내용을 바로 볼 수 있게 만들 수 있었다. 리액트로 프로젝트를 진행하며 겪었던 문제였고 잘 정리된 다른 글들도 많지만 내가 이해한 것을 바탕으로 좀 더 자세히 정리해보았다.
'React.js' 카테고리의 다른 글
[React] 렌더링 최적화하기 - React.memo 현명하게 사용하기 (0) | 2025.03.17 |
---|---|
[React] 웹 페이지 복귀, 이탈 탐지 (가시성) (0) | 2023.06.28 |
React 앱을 Vite + gh-pages로 빌드 및 배포하기 (0) | 2023.06.17 |
[React] ReferenceError: Buffer is not defined (0) | 2023.06.15 |
[React] 절대경로 설정하기 (create-react-app) (0) | 2022.07.31 |