웹 개발을 하다 보면 스크롤이 생길 때 레이아웃 정렬을 유동적으로 바꿔야 하는 경우가 있습니다. 예를 들어, 콘텐츠가 짧을 때는 flex-start로 위에서부터 시작되길 원하고, 콘텐츠가 길어 스크롤이 생기면 flex-end로 하단에 고정되도록 구성하는 시나리오입니다.
이번 에서는 두 개의 요소 중 하나라도 콘텐츠가 더 길면 해당 요소를 기준으로 flex-end 또는 flex-start 클래스를 토글해주는 방법을 소개합니다.
구현 목적
두 개의 요소를 비교해서 콘텐츠가 더 많은 요소를 기준으로 동적으로 스크롤 상태에 따라 flex-end 또는 flex-start 클래스를 토글하려고 합니다.
이를 통해 레이아웃이 콘텐츠의 길이에 맞춰 자연스럽게 조정될 수 있도록 합니다.
구현 코드
export const dynamicScroll = (element: HTMLElement, element2: HTMLElement) => {
const dynamicElement = element.clientHeight < element2.clientHeight ? element : element2;
const scrollEvent = () => {
// 현재 콘텐츠가 뷰포트보다 짧은지 확인하여 오차 보정값 설정
const isInViewport = dynamicElement.offsetHeight < window.innerHeight ? 1 : -1;
// 스크롤 위치 확인 (bottom에 도달했는지)
if (dynamicElement.scrollTop + dynamicElement.clientHeight < dynamicElement.scrollHeight - isInViewport) {
dynamicElement.classList.remove('flex-start');
dynamicElement.classList.add('flex-end');
} else {
dynamicElement.classList.add('flex-start');
dynamicElement.classList.remove('flex-end');
}
};
// 초기 상태 확인
scrollEvent();
// 스크롤 발생 시 애니메이션 프레임 내에서 이벤트 실행
window.addEventListener('scroll', () => window.requestAnimationFrame(scrollEvent));
};
포인트 설명
- element와 element2 두 개의 DOM 요소 중 clientHeight가 더 큰 요소를 dynamicElement로 설정합니다.
- 이 요소의 스크롤 상태를 기준으로 클래스를 토글합니다.
- scrollTop + clientHeight 값이 scrollHeight에 거의 도달했는지를 기준으로 판단합니다.
- isInViewport를 통해 짧은 콘텐츠일 경우에도 정상적으로 판단할 수 있도록 보정값을 설정합니다.
- requestAnimationFrame을 사용하는 이유는 아래의 문제를 해결하기 위해 사용합니다.
- 스크롤 이벤트는 짧은 시간 동안 매우 자주 발생할 수 있습니다. 만약 scroll 이벤트마다 직접 DOM 조작을 수행하면 성능 저하나 레이아웃 지연 (jank) 문제가 생길 수 있습니다.
- 이 문제를 해결하기 위해 requestAnimationFrame을 사용합니다.
- requestAnimationFrame은 스크롤이 발생하더라도 event() 함수가 프레임 단위로 최적화되어 실행되도록 하여 퍼포먼스를 안정적으로 유지해줍니다.
- 브라우저의 렌더링 주기와 동기화되어, 한 프레임에 한 번만 실행됨
- 불필요한 중복 계산을 줄여 성능 최적화
- DOM 업데이트가 화면 렌더 전에 일어나도록 보장해 더 부드러운 UI
실제 활용 예
이 기능은 다음과 같은 UI에서 유용하게 쓰일 수 있습니다.
- 채팅 UI: 메시지가 많을 때 하단 고정, 적을 땐 상단 고정
- 알림창 또는 로그창: 실시간 데이터가 쌓일 때 하단 정렬
- 동적 콘텐츠 영역: 스크롤에 따라 UX를 유연하게 조정할 때
마무리
이처럼 콘텐츠의 길이에 따라 유연하게 레이아웃을 조정하면 사용자에게 보다 깔끔하고 자연스러운 인터페이스를 제공할 수 있습니다. flex-start와 flex-end를 동적으로 전환하는 방식은 다양한 상황에서 활용 가능하니, 여러분의 프로젝트에서도 한번 도입해 보세요!