인스타그램 infinite scroll 파헤치기 :: 마이구미
이 글은 infinite scroll(무한 스크롤) 에 대해서 다룬다.
어떻게 코드를 구현해야하는지? 어떤 라이브러리를 써야하는지? 에 대한 코드를 제공하는 글은 아니다.
어떤 접근과 아이디어를 통해 무한 스크롤을 구현할 수 있는지에 대한 그저 하나의 방법을 다룬다.
이를 위한 모델은 인스타그램으로써, Web 버전 인스타그램을 대상으로 진행된다.
간단하게 "인스타그램은 어떻게 무한 스크롤을 구현하는가?" 로 볼 수 있다.
예제 코드 - https://github.com/hotehrud/instagram-infinite-scroll
실제로 무한 스크롤을 위해서는 많은 케이스에 따른 기능과 대안을 제공해야한다.
이러한 초점보다는 어떤 아이디어로 큰 틀을 구현했는지를 보길 바란다.
예제 코드는 tag 를 통해 단계별로 존재한다.
- git checkout 01-scroll
- git checkout 02-load-more
- git checkout 03-sliced-items
- git checkout 04-scroll-dwon
- git checkout 05-scroll-up
우선 페이지네이션과 무한 스크롤의 차이는 무엇인가? (일반적인 시나리오라고 가정한다)
그 중 하나는 리스트를 출력하는 페이지 단위로 분류할 수 있다.
페이지네이션은 페이지에 따른 리스트가 출력된다.
- 1 페이지에는 이에 해당하는 리스트들을 출력한다.
- 2, 3, 4 페이지도 마찬가지이다.
무한 스크롤은 한 페이지에서 리스트를 추가하면서 출력한다.
페이지네이션은 한 페이지에 해당하는 리스트를 출력함으로써, 페이지내에서는 제한적인 갯수의 리스트를 출력한다.
반면, 무한 스크롤은 페이지내에서 리스트를 무한대로 출력할 수 있다.
즉, 보여줄 수 있는 리스트 갯수가 많을수록 엘리먼트는 계속해서 증가하게 된다.
DOM 노드 갯수가 계속 증가하게 되고, 대부분의 리스트는 이미지를 포함하기에 차지하는 메모리도 빠르게 증가하게 된다.
(Web 버전 페이스북을 통해 확인할 수 있다.)
이를 대안하기 위해 보여지는 리스트 갯수를 제한하는 방법을 사용할 수 있다.
이는 실제 인스타그램의 DOM 상황이고, 리스트를 100개 이상 가지고 있더라도 DOM 갯수를 제한하여 보여주고 있다.
사실 DOM 갯수를 제한하는 것은 어렵지 않다.
현재 17번째 리스트를 보여주고 있다면, 이를 기준으로 +- 10개와 같은 방법으로 리스트를 줄일 수 있다.
- 7번 리스트가 화면에 존재한다면, "1번 ~ 17번째 리스트만 보여줘".
- 8번 리스트가 화면에 존재한다면, "1번 ~ 18번째 리스트만 보여줘".
- 17번 리스트가 화면에 존재한다면, "7번 ~ 27번째 리스트만 보여줘".
- 18번 리스트가 화면에 존재한다면, "8번 ~ 28번째 리스트만 보여줘".
하지만 한가지 문제점이 발생한다.
스크롤을 아래로 내리는 과정에서, 리스트는 계속해서 추가될 것이고, 보여지는 리스트는 20개로 제한된다.
생각해보면, 스크롤의 높이는 20개의 높이를 넘어갈 수가 없게 된다.
20번째 리스트가 기준이라면, {(1 ~ 20 번째의 스크롤 높이) + (20번 ~ 현재 마지막 리스트 번호까지의 높이)} 가 실제 차지하는 높이이다.
하지만 10 ~ 30 번째 리스트를 보여주고 있기 때문에, 1 ~ 9번째까지의 리스트에 대한 높이는 현재 적용되어 있지 않게 된다.
결과적으로, 스크롤은 정상적으로 동작할 수 없다.
이를 해결하기 위해 인스타그램은 리스트를 가지고 있는 부모 컨테이너에 padding 을 활용했다.
1 ~ 9번째까지의 리스트에 대한 높이를 padding-top 에 할당함으로써, 스크롤 높이는 유지할 수 있게 된다.
const ROW_COUNT = 20; // 보여지는 리스트 갯수
const ROW_HEIGHT = 300 // 하나의 리스트가 차지하는 높이
const currentStart = scrollTop / ROW_HEIGHT; // 현재 시작 기준 번호
const hiddenLastEnd = currentStart - ROW_COUNT / 2; // 제한된 갯수로 인해 상단에 보여지지 않고 있는 영역
const paddingTop = hiddenLastEnd * ROW_HEIGHT;
반대로 스크롤을 위로 올릴 경우를 위해서도 paddingTop 을 다시 감소시켜줘야한다.
그리고 paddingBottom 값을 증가하여 스크롤 높이를 유지해줘야한다.
const hiddenLastEnd = 마지막으로 가져온 리스트 번호 - 현재 보여지고 있는 마지막 리스트 번호
const paddingBottom = hiddenLastEnd * ROW_HEIGHT;
현재 실제로 차지하고 있는 높이 = paddingTop + paddingBottom + 보여지고 있는 모든 리스트 높이
결과적으로 paddingTop 과 paddingBottom 을 상황에 맞게 값을 변화시키면서 스크롤 높이를 유지하게 된다.
결론적으로 말하고자 하는건 다음과 같다.
무한대로 추가되는 리스트로 인한 문제는 제한된 갯수로 적용하였다.
이로 인해 발생하는 스크롤 높이가 유지되지 않는 것은 padding 을 활용했다는 것이다.
설명한 내용은 예제 코드에 단계별로 확인해볼 수 있다.
이 글로 통해 "무한 스크롤을 이렇게 구현할 수 있구나" 보다는 "이런 방법이 있구나" 로 보길 바란다.