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

- Author: @kimyoungjo
- Published: 2024-09-05
- Updated: 2024-09-05
- Source: http://blex.me/@kimyoungjo/%EC%8A%A4%ED%81%AC%EB%A1%A4%EC%9D%84-%ED%95%AD%EC%83%81-%EC%B5%9C%ED%95%98%EB%8B%A8%EC%9C%BC%EB%A1%9C-%EC%9C%A0%EC%A7%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95
- Tags: 미분류

---

팀 프로젝트 진행 중 채팅 봇 개발을 위해 채팅창을 퍼블리싱 하고 있었는데 채팅이 많아져 스크롤이 생겼을 때 항상 최신의 채팅 내역을 보여주기 위해 채팅창의 최하단을 항상 보여주고 싶어서 이것저것 알아봤는데 내가 떠올리지 못했던 방향으로 해결 하게 되어 포스팅을 하게됐다.

![](https://static.blex.me/images/content/2024/9/5/20249514_yJFVUWMyNyrXL7EPfcis.png)

*만든 채팅창 모습*

```
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를 찾아가는 기능을 만드는 등의 확장성에 있어서 해당 메서드를 좀 더 높게 평가했고 이번엔 그것을 위한 경험을 해봤다는 것에 의의를 뒀다. 작은 채팅창 하나를 만드는데도 고려 해야 할 부분이 참 많다는 생각이 든다.
