무한 스크롤(Infinite scroll) 구현하기

eunoia·2021년 6월 14일
39

JavaScript

목록 보기
5/6
post-thumbnail

게시판의 많은 양의 글을 편리하게 보기 위해 무한 스크롤 방법으로 페이징 기능을 구현해 봤습니다.

🤔무한 스크롤(Infinite scroll)이란?

무한 스크롤은 사용자가 페이지 하단에 도달했을 때, 콘텐츠가 계속 로드되는 사용자 경험(UX) 방식입니다. 한 페이지 아래로 스크롤 하면 끝없이 새로운 화면을 보여주게 되고 이로 인해 많은 양의 콘텐츠를 스크롤 해서 볼 수 있습니다.

무한 스크롤의 장점

  • 사용자 참여 및 콘텐츠 탐색이 쉽습니다.

  • 무한 스크롤이 클릭하는 것보다 더 나은 사용자 경험을 제공합니다.

    → 다음 콘텐츠를 보기 위한 추가 클릭이 필요없고 페이지 로드 시간이 짧습니다.

  • 터치스크린(모바일)일때 더 유용하게 적용됩니다.

    → 화면이 작을수록 스크롤은 길어지기에 모바일 환경에서 콘텐츠를 보여주기 직관적이고 사용하기 쉬운 형식 입니다.

무한 스크롤의 단점

  • 페이지 성능이 느려집니다.
  • 특정 항목 검색 및 원래 위치로 되돌아오기 힘듭니다.
  • 스크롤 막대가 실제 데이터양을 반영하지 못합니다.
  • 푸터를 찾기 어려워집니다.

페이지 네이션 VS 무한 스크롤 VS 더보기 버튼

다양한 페이징 기능들 중 제가 무한 스크롤 기능을 택한 이유는 일단, 사용자의 클릭을 최소화 하면서 한 번에 많은 양의 데이터를 보여주고 싶다는 생각을 했고 페이지 네이션 기능을 사용했을 때 보다 무한 스크롤을 사용하면 사용자들이 더 쉽게 다양한 콘텐츠를 볼 수 있기 때문에 무한 스크롤 기능을 택하게 되었습니다.

✍무한 스크롤 구현하기

  • 스크롤이 바닥에 닿게 되면 20개의 아이템을 볼러온다.
  • 20개의 아이템을 불러오는 시간 동안 스켈레톤 UI와 로딩 UI를 띄운다.
  • 해당 아이템의 상세보기 창에서 뒤로 가기를 누르면 기존의 스크롤이 유지된다.
  • 아이템이 다 불러와지면 무한 스크롤은 멈추게 된다.

제가 생각한 무한 스크롤을 구현했을 때 가져야할 기능들입니다. 하나씩 차근차근 구현해보도록 하겠습니다.

📌IntersectionObserver을 이용한 무한스크롤 구현

JS로 무한스크롤을 구현하는 방법은 다양합니다. 그 중 저는 IntersectionObserver API를 이용했습니다.

🤔IntersectionObserver란?

IntersectionObserver 인터페이스는 대상 요소와 그 상위 요소 혹은 최상위 도큐먼트인 viewport와의 교차 영역에 대한 변화를 비동기적으로 감지할 수 있도록 도와줍니다.

즉, arget Element가 화면에 노출되었는지 여부를 간단하게 구독할 수 있는 API입니다.

var intersectionObserver = new IntersectionObserver(function(entries) {
  // If intersectionRatio is 0, the target is out of view
  // and we do not need to do anything.
  if (entries[0].intersectionRatio <= 0) return;

  loadItems(10);
  console.log('Loaded new items');
});
// start observing
intersectionObserver.observe(document.querySelector('.scrollerFooter'));

IntersectionObserverCallback 함수를 통해 두개의 매개변수를 받습니다.

new IntersectionObserver(callback[, options]);

  • entries

    →더 보이거나 덜 보이게 되면서 통과한 역치를 나타내는, IntersectionObserverEntry (en-US) 객체의 배열.

  • observer

    → 자신을 호출한 IntersectionObserver.

이 기능을 이용해 화면 맨 아래에 div 요소를 넣어 이 div 요소를 IntersectionObserver 가 감시하도록 구현했습니다.

<div class="list"></div>
<p id="sentinel"></p>
const io = new IntersectionObserver((entries, observer) => {
	entries.forEach(entry => {
	  if (!entry.isIntersecting) return; 
		//entry가 interscting 중이 아니라면 함수를 실행하지 않습니다.
	  if (page._scrollchk) return;
		//현재 page가 불러오는 중임을 나타내는 flag를 통해 불러오는 중이면 함수를 실행하지 않습니다.
    observer.observe(document.getElementById('sentinel'));
		//observer를 등록합니다.
    page._page += 1;
		//불러올 페이지를 추가합니다.
    page.list.search();
		//페이지를 불러오는 함수를 호출합니다.
	});
});

io.observe(document.getElementById('sentinel'));

📌아이템이 로딩되기 전 UI 구현

IntersectionObserver를 통해 실행된 search 함수에서 ajax를 통해 아이템을 가져옵니다.

이때, beforeSendcomplete를 통해서 Skeleton UILoading animation 삽입, 삭제를 구현했습니다.

$.ajax({
	url: url,
	data: param,
	method: "GET",
	dataType: "json",
	success: function (result) {
	  console.log(result);
	},
	error: function (err) {
	  console.log(err);
	},
	beforeSend: function () {
    _scrollchk = true; 
		//데이터가 로드 중임을 나타내는 flag입니다.
		document.getElementById('list').appendChild(skeleton.show());
		//skeleton을 그리는 함수를 이용해 DOM에 추가해줍니다.
    $(".loading").show();
		//loading animation을 가진 요소를 보여줍니다.
	},
	complete: function () {
    _scrollchk = false;
		//데이터가 로드 중임을 나타내는 flag입니다.
    $(".loading").hide();
    skeleton.hide();
		//loading animation 요소와 skeleton을 지우는 함수를 이용해 DOM에서 지워줍니다.
	}
});

📌무한 스크롤 멈추기 구현

모든 아이템이 다 불러오거나 검색 시 20개의 아이템보다 적게 검색되는 경우 더 이상 IntersectionObserver의 감시를 받지 않도록 만들어야 합니다.

IntersectionObserver.unobserve(target);

IntersectionObserver API에는 지정된 대상 요소 관찰을 중지하는 unobserve 메서드가 존재하는데 이를 이용해 관찰을 중지할 수 있습니다.

var observer = new IntersectionObserver(callback);
observer.observe(document.getElementById("elementToObserve"));

/* ... */

observer.unobserve(document.getElementById("elementToObserve"));

이렇게 관찰을 중지할 수도 있지만, 저는 간단하게 지금 관찰중인 요소를 이용해 관찰 대상을 DOM에서 숨기는 방식으로 구현했습니다.

if (_total === 0) {
	$('#sentinel').hide();
	//검색된 아이템이 없을 경우 관찰중인 요소를 숨긴다.
}
else {
	if (_total <= _page*20){
		$('#sentinel').hide();
		//검색된 아이템이 20개 이하일 경우 관찰중인 요소를 숨긴다.
	}
	else {
		 $('#sentinel').show();
		//관찰중인 요소를 보여준다.
	}
}

📌뒤로 가기 시 이전 스크롤 유지 구현

사용자가 아이템의 상세보기 페이지로 이동 한 후 다시 뒤로가기를 이용해 무한 스크롤 페이지로 돌아왔을 때, 페이지가 다시 맨 처음처럼 로드되어 있다면 다시 보고 있던 아이템까지 스크롤을 해야 하는 불편함이 발생합니다.

이를 해결하기 위해, 기존에 보고 있던 페이지로 스크롤을 옮기는 기능을 추가했습니다.

브라우저 뒤로가기 이벤트 감지를 이벤트 처리해줍니다.

window.addEventListener('pageshow', function (event) {
	if (event.persisted || window.performance && window.performance.navigation.type == 2) {
		 //
	}
});

상세보기 페이지로 넘어가기 전 페이지를 sessionStorage에 저장한 후 뒤로가기 이벤트가 발생하면 sessionStorage에서 값을 가져와 넘겨줍니다.

if (sessionStorage.getItem("page")) {
    var pageNum = Number.parseInt(sessionStorage.getItem("page"));
    _page = pageNum ;
    list.search();
}

이렇게 완성된 리스트 무한 스크롤입니다.

profile
💻 .net, Spring

1개의 댓글

comment-user-thumbnail
2022년 3월 29일

혹시 소스코드를 받을 수 있을까요?....ㅠㅠ 제가 딱 원하는 기능들을 정말 잘 구현해주셔서 포스팅 읽으면서 따라해보는데 막혀서요 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ

답글 달기