백엔드에서 가지고 있는 데이터는 많고, 그 데이터를 한 화면에 전부 보여줄 수 없는 경우에 사용됩니다. Pagination은 프론트엔드, 백엔드 양쪽에서 모두 구현해야하며, 프론트엔드에서 현재의 위치(Offset)와 추가로 보여줄 컨텐츠의 수(Limit)를 백엔드에 전달합니다. 백엔드에서는 그에 해당하는 데이터를 끊어서 보내주는 방식으로 구현하게 됩니다.
`http://xx.xx.xx.xx:8000/ootds?offset=0&limit=10`
다음의 주소를 해석하면, offset은 데이터가 시작하는 위치를 알려주고, limit은 한 페이지에 보여줄 데이터의 수를 나타냅니다. ootds뒤에 ? 기호는 url에서 한 번만 쓸 수 있으며 Quary String의 시작임을 알립니다.
제가 접근한 방식은 2가지인데, 둘 다 코드를 통해 살펴보겠습니다.
첫 번째는, 스크롤 할 때마다 scroll event를 발생시키는 단점이 있지만, 직접 DOM에 접근하여 스크롤 높이 값, 화면의 상단, 하단의 위치를 설정해서 설정한 target지점에 도달했을 때 fetch하여 데이터의 일부분을 가져오는 방법입니다.
먼저, 클래스 밖에 다음 값들을 선언해 줬습니다.
const API = http://xx.xx.xx.xx:8000;
const LIMIT = 10;
state에는 offSet값을 0으로 선언해줬습니다.
class Main extends Component {
constructor() {
super();
this.state = {
offSet: 0,
isLoading: false,
}
}
첫 화면은 데이터를 0부터 10까지 불러오도록 설정했습니다. 그리고 offSet은 실행 될 때마다, 10씩 증가합니다.
componentDidMount() {
window.addEventListener("scroll", this.infiniteScroll);
fetch(`${API}/ootds?offset=${this.state.offSet}&limit=${LIMIT}`)
.then((res) => res.json())
.then((res) =>
this.setState({
cards: res.ootd_list,
offSet: this.state.offSet + LIMIT,
})
}
// 해당 화면에서의 높이 값과, 화면의 상단 값, 전체의 높이 값을 통해
infiniteScroll = () => {
const scrollHeight = Math.max(
document.documentElement.scrollHeight,
document.body.scrollHeight
);
const scrollTop = Math.max(
document.documentElement.scrollTop,
document.body.scrollTop
);
const clientHeight = document.documentElement.clientHeight;
if(scrollTop + clientHeight === scrollHeight) {
fetch(`${API}/ootds?offset=${this.state.offSet}&limit=${LIMIT}`)
.then((res) => res.json())
.then((res) => {
this.setState({ isLoading: true });
this.setState({
cards: this.state.cards.push(res),
offSet: this.state.offSet + LIMIT,
});
this.setState({ isLoading: true });
setTimeout(() => {
this.setState({ isLoading: false });
}, 350);
}
);
}
};
스크롤 이벤트를 이용하여 무한스크롤을 구현하려고 할 경우 발생하는 문제
개발자도구의 콘솔 창에 다음을 입력한 뒤, scroll 해보시면 알 수 있습니다!!
window.addEventListener('scroll', function() {
return console.log('scroll');
});
따라서 선택한 방법은 라이브러리를 이용하는 것이었습니다!
무한스크롤 라이브러리
Quary String을 이용한 Pagination 하는 것은 같았지만, Event 발생을 덜 시킬 수 있어서 백엔드와의 통신에 과부하를 줄일 수 있었습니다.
loadFunc = () => {
fetch(`${API}/ootds?offset=${this.state.offSet}&limit=${LIMIT}`)
.then((res) => res.json())
.then((res) => {
this.setState({
cards: [...this.state.cards, ...res.ootd_list],
offSet: this.state.offSet + LIMIT,
});
})
}
render() {
return (
<InfiniteScroll
pageStart={0}
loadMore={this.loadFunc}
hasMore={true || false}
loader={<div className="loader" key={0} />}
useWindow={false}
>
(여기에 화면에 띄울 data를 넣습니다)
<Main />
</InfiniteScroll>
)
}