
페이지 번호를 클릭해서 이동하는 방식의 페이지 처리방법 (게시판 형태의 페이지에서 일반적으로 사용되는 방식)

+a) 페이지네이션 기능 만들기
1. 지정 번호 클릭 시 해당 번호의 페이지로 이동
// gql query
const FETCH_BOARDS = gql`
query fetchBoards($page: Int) {
fetchBoards(page: $page) {
_id
writer
title
}
}
`;
// fetchBoards
// data와 함께 refetch 가져오기
const { data, refetch } = useQuery(FETCH_BOARDS, { variables: { page: 1 } });
const onClickPage = (event) => {
// 위에서 가져온 refetch 사용하기
refetch({ page: Number(event.target.id) });
};
return (
<div>
<h1>페이지네이션 연습 !!!</h1>
{data?.fetchBoards?.map((el) => (
<div key={el._id}>
{el.title} {el.writer}
</div>
))}
{[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map((el) => (
<span onClick={onClickPage} id={String(el)} key={el}>
{` ${el} `}
</span>
))}
</div>
)
아래쪽에 map 함수를 이용해서 각각의 번호를 생성하고 해당 번호마다 onClick={onClickPage} 라는 속성을 줌
onClick 속성이 부여된 해당 요소(태그) 를 선택할 때마다 지정한 id값( el )이 onClickPage 함수에 넘어가서 그 값을 page 값으로 하여 refetch 되고 , 결과적으로 해당 값(번호)의 페이지를 다시 fetch 하여 리렌더링하게 됨
[BUT!] => 1~10 페이지까지만 로드되며, 11페이지 이상은 로드가 안 됨!
2. 이전 / 다음 버튼을 누르면 10페이지 씩 +, - 되어 이동하는 기능
export default function PaginationNextPage() {
// 시작 페이지
const [startPage, setStartPage] = useState(1);
const { data, refetch } = useQuery(FETCH_BOARDS, { variables: { page: 1 } });
const onClickPage = (event) => {
refetch({ page: Number(event.target.id) });
console.log(event.target.id);
};
// 이전 페이지 클릭 시 실행할 함수
const onClickPrevPage = () => {
setStartPage((prev) => prev - 10);
};
// 다음 페이지 클릭 시 실행할 함수
const onClickNextPage = () => {
setStartPage((prev) => prev + 10);
};
return (
<div>
<h1>페이지네이션 연습 !!!</h1>
{data?.fetchBoards?.map((el) => (
<div key={el._id}>
{el.title} {el.writer}
</div>
))}
<span onClick={onClickPrevPage}>이전페이지 |</span>
{new Array(10).fill(1).map((_, index) => (
<span
onClick={onClickPage}
id={String(index + startPage)}
key={index + startPage}
>
{` ${index + startPage} `}
</span>
))}
<span onClick={onClickNextPage}> 다음페이지</span>
</div>
);
}
startPage 변수에 기본값을 1로 할당
이전페이지, 다음페이지 버튼을 만들고 각각에 onClick 속성을 주어서 클릭 시 startPage 의 값이 + 10 or -10 이 되도록 지정
아래 쪽 return 문 안의 map 함수에서 실질적으로 렌더링 되는 부분을 {index + startPage} 이런 식으로 지정
*결과적으로 index값은 이전과 똑같이 0~9까지 반복되지만 이전페이지,다음페이지 버튼을 누를때마다 바뀌는 startPage의 값에 더해지면서 map으로 뿌려지는 부분이 변경됨
[BUT!] => 이 경우 버튼을 클릭하면 이전페이지가 1 미만이어도 -1, -2, -3 이런 식으로 계속 렌더링되거나 마지막페이지를 지나도 계속 페이지 수가 늘어나게 됨
(페이지 수의 한계에 대한 적절한 제어가 필요!)
3. 클릭 시 생성되는 페이지 수의 한계점 지정
const onClickPrevPage = () => {
// startPage가 1이면 하단 스크립트를 실행하지 않고 종료한다.
if (startPage === 1) return;
setStartPage((prev) => prev - 10);
};
<button disabled={startPage === 1} onClick={ClickPrev}>{`<`}</button>
이전페이지 버튼의 경우, 지정해 둔 변수 startPage 의 값이 1일 경우 (첫 10개의 페이지 1~10을 렌더링하고 있는 경우) return 하여 함수의 실행자체를 종료
*페이지가 -1, -2,.. 이런 식으로 넘어가는 것을 방지
추가적으로 버튼에 disabled 속성을 주어서 startPage의 값이 1일 경우(첫 페이지일 경우) 버튼 자체를 비활성화 시킬 수 있음
// fetchBoardsCount API 요청하기
const FETCH_BOARDS_COUNT = gql`
query fetchBoardsCount {
fetchBoardsCount
}
`;
const {data: dataBoardsCount} = useQuery(FETCH_BOARDS_COUNT)
// lastPage 구하기
const lastPage = Math.ceil(dataBoardsCount?.fetchBoardsCount / 10);
lastPage 에 현재 총 페이지 수를 10으로 나눈 것을 올림한 값을 마지막 페이지 값으로 할당해줌const onClickNextPage = () => {
// startPage + 10가 lastPage보다 클 경우 하단 스크립트를 실행하지 않고 종료한다.
if (startPage + 10 > lastPage) return;
setStartPage((prev) => prev + 10);
};
onClickNextPage 함수에 현재 startPage + 10 한 값이 마지막 페이지보다 클 경우 return 으로 함수를 종료{new Array(10).fill(1).map(
(_, index) =>
index + startPage <= lastPage && (
<span
onClick={onClickPage}
id={String(index + startPage)}
key={index + startPage}
>
{` ${index + startPage} `}
</span>
)
)}
4. 이전페이지, 다음페이지 버튼 클릭 시 refetch 하기
// [1번째 방법]
const onClickPrevPage = () => {
// startPage가 1이면 하단 스크립트를 실행하지 않고 종료한다.
if (startPage === 1) return;
setStartPage((prev) => prev - 10);
refetch({ page: startPage - 10 });
};
// [2번째 방법]
const { data, refetch } = useQuery(FETCH_BOARDS, {
variables: { page: startPage },
});
렌더링 화면은 구성되었으나 기능이 아직 구현되지 않았기에 이전 / 다음 페이지를 클릭하면 refetch로 해당 화면을 다시 렌더링해줘야 함
*위와 같이 startPage - 10의 값을 그대로 refetch의 page 값으로 할당해 주면 함수가 실행될 때마다 10페이지씩 감소된 페이지를 리렌더링
또는 useQuery 함수 내 두번째 인자로 variables 변수에 page 값을 startPage로 지정해 줄 수도 있음
*여기에서 variables는 변수를 필요로 하는 모든 실행함수(ex. refetchQueries) 의 변수 값으로 전역 지정 됨

단방향에서 데이터는 위에서 아래로 흐르기 때문에, 위 같은 경우와 같이 자식1의 state 값을 자식2 에게 보내주는 것이 불가능함
또한 자식의 state를 부모에게 보내주는 것도 불가능함

자식 컴포넌트의 state와 setState를 부모 컴포넌트로 끌어올려 선언 해주면 됨 (state 끌어올리기)
그리고 props 로 내려줄 경우, 자식 컴포넌트1, 2에서 모두 state를 사용할 수 있게 됨
state 끌어올리기 예제
// 자식 1 - Child1.tsx
export default function Child1(props) {
const onClickCountUp = () => {
props.setCount((prev) => prev + 1);
};
return (
<div>
<div>자식 1 카운트: {props.count}</div>
<button onClick={onClickCountUp}>카운트 올리기</button>
</div>
);
}
우선적으로 부모 컴포넌트에 useState 로 count, setCount 만든 후 이를 자식 1 컴포넌트에 props 로 상속
자식 1 컴포넌트에서 상속받은 props.setCount로 버튼을 클릭할 때마다 count 값이 변경되도록 설정
// 자식 컴포넌트 - Child2.tsx
export default function Child2(props) {
return (
<div>
<div>자식 2 카운트: {props.count}</div>
<button>카운트 올리기</button>
</div>
);
}
자식 2에도 마찬가지로 부모 컴포넌트에서 props로 상속 받은 count 값을 설정
[결과]

자식 1의 카운트 버튼을 클릭하면 자식2의 state 값도 변경되어 변경되는 카운트 값이 적용 됨