채팅 시스템을 만드는 프로젝트를 만들다가
새로운 채팅이 생겼을 때 해당 채팅으로 스크롤을 내리는 기능이 필요했습니다.
이렇게요!
구현 방법: beforeUpdate() & afterUpdate()
전체 코드를 먼저 보겠습니다.
<script>
import { beforeUpdate, afterUpdate } from "svelte";
let chatLogs = []; // 채팅 로그: 메시지가 쌓이는 배열
$: chatLogs = chatLogs; // 반응형 변수 선언
let previousChatCount = 0; // DOM 이 업데이트 전 채팅 로그 배열의 개수를 저장할 변수
// DOM 업데이트 전에 호출됩니다.
beforeUpdate(() => {
previousChatCount = chatLogs.length - 1; // 메세지가 전송되자마자 chatLogs 가 증가하므로 1을 뺀다.
});
// DOM 업데이트 후에 호출됩니다.
afterUpdate(() => {
// 새로운 채팅 메시지가 추가된 경우
if (chatLogs.length > previousChatCount) {
const newChatId = chatLogs.length - 1;
const newChatElement = document.querySelector(`[data-chat-id="${newChatId}"]`);
if (newChatElement) {
newChatElement.scrollIntoView({ behavior: 'smooth' });
}
}
});
</script>
...
{#each chatLogs as chat, idx}
<!-- 채팅 요소 하나 -->
<div data-chat-id={idx}>
{/each}
...
저는 채팅 메시지를 저장할 배열(chatLogs)을 만들었습니다.
그리고 채팅 메시지 한 건마다 'data-chat-id' 라는 값을 부여해줬습니다. (이 부분은 나중에 백엔드와 연결되면 메시지의 id 가 되겠죠?)
왜냐면 data-chat-id 값을 찾아서 해당 <div> 태그로 스크롤 이동을 해야하기 때문이죠.
마지막으로 새로운 채팅이 배열에 추가될 때 마다 beforeUpdate() 와 afterUpdate() 라는 svelte에서 제공하는 함수를 이용해서 스크롤을 이동합니다.
beforeUpdate() 함수와 afterUpdate() 함수의 조합
beforeUpdate() 함수는 DOM 요소가 새로 생길 때마다 실행됩니다.
afterUpdate() 함수는 DOM 이 data와 동기화 될 때 실행됩니다.
beforeUpdate() 함수에서 chatLogs 배열의 길이보다 1만큼 작은 값을 previousChatCount 변수에 저장합니다.
1만큼 작은 값을 저장하는 이유는 메시지를 전송하자마자(또는 전송받자마자) chatLogs 배열에 메시지가 담기기 때문에 이보다 1작은 값을 빼야합니다.
afterUpdate() 함수에선 chatLogs 배열의 길이와 previousChatCount 값을 비교해서, chatLogs 배열의 길이가 더 길다면 해당 요소를 찾아 스크롤을 이동합니다.
해당 요소를 찾는 방법은 위에서 설명한 data-chat-id 값을 이용해 찾습니다.
도움이 되셨길 바라며, 포스팅을 마치겠습니다.
읽어주셔서 감사합니다.