[React] Pagination 구현 (라이브러리 X)

Jungwoo LEE·2023년 9월 19일
0

프로젝트에서 다음과 같은 페이지네이션 기능을 구현해야 했다.

npm에서 제공하는 react-js-pagination 라이브러리를 사용하면 간단하게 페이지네이션을 구현할 수 있다.
하지만 라이브러리를 사용하지 않고 페이지네이션 기능을 직접 구현해보았다.

먼저 한 페이지에 요소를 몇 개씩 렌더할지를 결정하는 itemNumber 변수를 선언하였다. 현재 내가 구현해야 하는 페이지에는 페이지당 컴포넌트 6개를 렌더하므로 itemNumber를 6으로 선언해주었다.

const itemNumber = 6;

다음으로, 하단에 있는 앞으로 가기, 뒤로 가기 버튼을 구현해야 했다.
이를 위해 앞으로 가기, 뒤로 가기 버튼을 눌렀을 때 어떤 컴포넌트를 렌더시킬지를 결정하는 이벤트 핸들러를 구현해야 했다.

먼저, 핸들러 구현에 필요한 state들을 선언해주었다.

// 한 페이지에 띄울 아이템 배열 시작 인덱스(0으로 고정)
const [startIdx, setStartIdx] = useState(0);

// 한 페이지에 띄울 아이템 배열 끝 인덱스(으로 고정)
const [endIdx, setEndIdx] = useState(itemNumber - 1);

// 첫 번째 페이지인지 아닌지를 저장하는 state
const [isStart, setIsStart] = useState(true);

// 마지막 페이지인지 아닌지를 저장하는 state
const [isEnd, setIsEnd] = useState(false);

다음으로, 해당 페이지에서 렌더할 아이템을 담은 배열인 currentArray 배열을 선언하였다.

let currentArray = totalArray.slice(startIdx, endIdx + 1);

전체 배열에서 stardIdx부터 endIdx까지 잘라서 currentArray 변수에 담아주었다.

다음으로, 왼쪽 버튼 이벤트 핸들러를 구현하였다.

const handleClickLeftButton  = () => {
  	
  	// 마지막 페이지일때 endIdx 연산하기
 	if (isEnd === true && totalArray.length % itemNumber !== 0) {
      	setEndIdx(endIdx - (totalArray.length % itemNumber));
    } else {
      	setEndIdx(endIdx - itemNumber);
    }
  	
  	// 마지막 페이지가 반드시 아니므로 isEnd를 false로 set
  	setIsEnd(false);
  	
    // 앞으로 간 페이지가 시작 페이지이면 isStart를 true로 set
  	if (startIdx - itemNumber === 0) {
      	setIsStart(true);
    }
  	
  	// startIdx 연산하기
  	setStartIdx(startIdx - itemNumber);
};

처음에 앞으로 갔을때의 endIdx 값을 수정한다. 여기서 중요한 점은, 마지막 페이지라면 렌더된 아이템 개수가 반드시 itemNumber가 아닐 수도 있다는 것이다. 이를 위해 isEnd를 검사하는 조건문을 추가했다.

만약 마지막 페이지라면, 마지막 페이지에 렌더된 아이템만큼 endIdx에서 빼주어야 한다. 마지막 페이지에 렌더된 아이템 개수를 구하기 위해 dummyData.length를 itemLength로 나눠주었다. 나눈 나머지가 결국 마지막 페이지에 렌더되는 아이템 개수이다. 이 값을 endIdx에서 빼준 다음, 다음 페이지 endIdx로 set한다. 마지막 페이지가 아니라면, endIdx에서 itemNumber만큼 빼준 값을 다음 페이지 endIdx로 set하면 된다.

여기서 또 한 가지 고려해야 할 점이 있는데, 마지막 페이지에 렌더된 아이템 개수가 itemNumber인 경우이다. 이 경우 totalArray.length % itemNumber 값이 0이 되므로, endIdx 값이 갱신되지 않는다!!! (어려워......) 그래서 totalArray.length % itemNumber가 0이 아니라는 조건까지 AND 연산자로 걸어주었고, 만약 0이라면 그냥 endIdx에서 itemNumber를 빼주면 된다.

왼쪽 버튼을 누르게 되면 반드시 마지막 페이지가 아니게 된다. 따라서 isEnd를 false로 set하였다.

다음으로, 다음 페이지가 시작 페이지인지 검사하기 위해 startIdx에서 itemNumber 값을 뺀 값이 0인지 확인하였고, 맞다면 시작 페이지이므로 isStart를 true로 set하였다.

마지막으로 기존 startIdx 값에서 itemNumber 값을 뺀 값을 다음 페이지 startIdx로 set하면 된다.

다음으로, 오른쪽 버튼 이벤트 핸들러를 구현하였다.

const handleClickRightButton = () => {
  	
  	// 첫 페이지가 반드시 아니므로 isStart를 false로 set
  	setIsStart(false);
  	
  	// startIdx 연산하기
  	setStartIdx(startIdx + itemNumber);
  
  	// endIdx 연산하기
  	if (endIdx + 1 + itemNumber > dummyData.length) {
      	setEndIdx(endIdx + (dummyData.length % itemNumber));
      	setIsEnd(true);
    } else if (endIdx + 1 + itemNumber === dummyData.length) {
      	setEndIdx(endIdx + itemNumber);
      	setIsEnd(true);
    } else {
      	setEndIdx(endIdx + itemNumber);
    }
};

먼저, 오른쪽 버튼을 누르게 되면 반드시 첫 페이지가 아니게 된다. 따라서 isStart를 false로 set하였다.

다음으로, 기존 startIdx 값에서 itemNumber 값을 더한 값을 다음 페이지 startIdx로 set하였다.

마지막으로, 다음 페이지가 마지막 페이지인지 아닌지 알아야 한다. 마지막 페이지에는 렌더되는 아이템 개수가 itemNumber일 수도 있고, 아닐 수도 있다.
먼저 endIdx + 1 + itemNumber > dummyData.length 인 경우는 마지막 페이지인데 렌더되는 아이템 수가 itemNumber이 아닌 경우이다. 이 경우 endIdx를 endIdx + (dummyData.length % itemNumber) 로 set해주면 된다.
endIdx + 1 + itemNumber === dummyData.length 인 경우는 마지막 페이지인데 렌더되는 아이템 수가 itemNumber인 경우이다. 이 경우 기존 endIdx에 itemNumber를 더한 값으로 endIdx를 set하면 된다.
위의 두 조건의 경우 다 마지막 페이지이므로 isEnd를 true로 set한다.
만약 위 조건이 둘 다 아니라면, 마지막 페이지가 아닌 것으로, 기존 endIdx에 itemNumber를 더한 값으로 endIdx를 set해주었다.

jsx 코드는 다음과 같다.

<div className="archiveboxcontainer">
     {currentArray.map((it)=> (
  		<ArchiveBox {...it}/>
  	))}
</div>
{totalArray.length <= itemNumber ?
	<div className='arrowbuttoncontainer'></div> : 
	isStart === true ? 
	<div className='arrowbuttoncontainer'>
  		<Button img={rightArrow} onClick={handleClickRightButton} type={'more'}/>
	</div> :
	isEnd === true ?
	<div className='arrowbuttoncontainer'>
  		<Button img={leftArrow} onClick={handleClickLeftButton}  type={'more'}/>
	</div> :
  	<div className='arrowbuttoncontainer'>
      	<Button img={leftArrow} onClick={handleClickLeftButton}  type={'more'}/>
      	<Button img={rightArrow} onClick={handleClickRightButton} type={'more'}/>
	</div> 
}

전체 아이템 개수가 itemNumber 이하이면 버튼을 렌더시키지 않았다.
현재 시작 페이지라면 오른쪽 버튼만 렌더시켰고, 마지막 페이지라면 왼쪽 버튼만 렌더시켰다.
위 조건이 모두 아니라면 왼쪽 버튼과 오른쪽 버튼을 모두 렌더시켰다.

그 결과, 원하던 기능대로 정상 동작하였다 👍🏻👍🏻👍🏻

profile
Moved to -> https://jungwoo3490.github.io/

0개의 댓글