무한 스크롤?
⇒ 컨텐츠를 페이징하는 기법 중 하나로, 아래로 스크롤하다 컨텐츠의 마지막 요소를 볼 즈음 다음 컨텐츠가 있으면 불러오는 방식
⇒ 프론트엔드에서 많이 쓰는 기법인 이미지 지연 로딩에 쓸 수 있다
⇒ but. 상황에 따라 무한 스크롤보다 직접 불러오는 인터렉션(ex. 더보기 버튼)을 통해 로딩하는게 나을 수 있다
[구현 방법1] window의 scroll 이벤트를 통해 스크롤링이 일어날 때마다 화면 전체의 height와 스크롤 위치를 비교하는 방식
[구현 방법2] intersection observer
[API]
https://kdt-frontend.programmers.co.kr
/cat-photos?_limit={limit}&_start={start}
{limit} : 한번에 가져올 갯수
{start} : 가져올 시작 위치
- 응답
- id, imagePath, cats, photo_in_cats(id, name, colors, birthday, profileImage)
구현방법1 코드
const fetchPhotos = async () => { const {limit, start} = this.state this.setState({ ...this.state, isLoading: true }) const photos = await request(`/cat-photos?_limit=${limit}&_start=${start}`) this.setState({ ...this.state, start: start+limit, photos: this.state.photos.concat(photos), isLoading: false }) }
- isLoading으로 패치 전후로 로딩여부를 세팅하고, 로딩이 끝나면 사진을 불러오도록 처리한다
this.render = () => { if (!isInit) { $list.innerHTML = ` <ul class="photoList_photos"></ul> ` isInit = true } const $photos = $list.querySelector('.photoList_photos') this.state.photos.forEach(photo => { const { id, imagePath } = photo if($photos.querySelector(`li[data-id="${id}"]`) === null) { const $li = document.createElement('li') $li.setAttribute('data-id',id) $li.setAttribute('style',"list-style: none; min-height: 500px;") $li.innerHTML = `<img width="200" src="${imagePath}"/>` $photos.appendChild($li) } }) }
렌더링할 때마다 innerHtml을 지정하면 항상 사진이 처음부터 보여진다
그래서 처음에만 innerHtml을 하고 그 이후에는 새로 추가된 사진만 li에 담아 하나씩 appendChild한다
window.addEventListener('scroll', () => { const { isLoading, totalPhotoCount, photos } = this.state const isScrollEnded = window.scrollY + window.innerHeight >= document.body.scrollHeight if (!isLoading && isScrollEnded && totalPhotoCount > photos.length){ console.log(window.scrollY, window.innerHeight, document.body.scrollHeight) onScrollEnded() } })
구현방법2 코드
const $li = document.createElement('li') $li.setAttribute('data-id',id) $li.style = "list-style: none; min-height: 500px" $li.innerHTML = `<img width="100%" height="100%" src="${imagePath}"/>` $photos.appendChild($li) } }) let $lastLi = $photos.querySelector('li:last-child') if ($lastLi && !isLoading && totalPhotoCount > photos.length) { photoObserver.observe($lastLi) }
$li.style
로 js에서 dom 객체의 style을 지정할 수 있다
$li.style = "list-style: none; min-height: 500px"
⇒ 사진이 완전히 로딩 되기 전엔 사진들의 높이가 거의 없어서 한화면에 모든 사진이 다 나오게 된다
최소 li 높이를 지정함으로써 로딩 중에도 화면에 안보이는걸 보장한다.
- 불러온 것들 중에 마지막 사진을 담은 li를 관찰대상으로 한다.
'li:last-child'
: 구조 의사 클래스
const photoObserver = new IntersectionObserver((entries, observer)=>{ if (entries[0].isIntersecting) { console.log("마지막 보여짐, 사진 더 로딩합니당") observer.unobserve(entries[0].target) onScrollEnded() } })
- 보여진다면 로딩, 로딩 이후에는 지금 li는 마지막 li가 아니므로 관찰 삭제하면서 나온다.