[TIL] 페이지네이션 / state 끌어올리기

신재욱·2023년 3월 31일
0
post-thumbnail

📒 오늘 공부한 내용

🔍수업목차

[14-1] 페이지네이션
[14-2] state 끌어올리기

✅ 페이지네이션


  • 페이지 처리를 하는 방법에는 크게 일반적인 방식무한스크롤 방식, 2가지 방법이 있다.

  • 페이지 번호를 클릭해서 이동하는 방식의 페이지 처리 방법이다.

  • 게시판 형태의 페이지에서 가장 일반적으로 사용되는 방식이다.

📂 page 인자를 사용해서 게시글 목록 불러오기

  • 가장 먼저 fetchBoards API를 활용해서 게시글 목록을 불러온다.

  • 이 때, playground Docs를 참고하여 page 인자도 함께 불러와준다.

// gql query
const FETCH_BOARDS = gql`
  query fetchBoards($page: Int) {
    fetchBoards(page: $page) {
      _id
      writer
      title
    }
  }
`;

// fetchBoards
const { data } = useQuery(FETCH_BOARDS, { variables: { page: 1 } });

return (
	<div>
    <h1>페이지네이션 연습 !!!</h1>
    {data?.fetchBoards?.map((el) => (
      <div key={el._id}>
        {el.title} {el.writer}
      </div>
    ))}
		<span> 1 </span>
		<span> 2 </span>
		<span> 3 </span>
  </div>
)

📂 페이지 클릭 시 게시글 목록 데이터 다시 불러오기 (refetch)

  • 페이지네이션의 페이지 숫자를 클릭할 때마다. 목록에 뿌려진 데이터가 해당 페이지에 해당하는 데이터로 변경

  • graphQL의 useQuery에서 제공하는 refetch라는 함수를 활용하면 페이지 클릭 시 해당 페이지에 해당하는 데이터를 다시(re) 불러올(fetch) 수 있다.

  • refetch를 사용하기 위해서는 useQuery에서 data와 함께 refetch라는 함수를 불러와야 한다.

// 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>
    ))}
		<span onClick={onClickPage} id="1"> 1 </span>
		<span onClick={onClickPage} id="2"> 2 </span>
		<span onClick={onClickPage} id="3"> 3 </span>
  </div>
)

📂 map을 이용해 페이지네이션 뿌리기

  • 방금 한 것과 같이 모든 페이지네이션의 숫자를 직접 입력하는 작업은 비효율적이다.

  • 배열과 map을 사용해 페이지네이션 만들기

// 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>
)

📂 페이지네이션 next / prev 구현

  • startPage의 값을 변경해주면 현재 페이지 이후의 10페이지, 이전의 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>
  );
}

📂 lastPage 설정

  • 페이지네이션이 마지막 페이지를 훌쩍 지나갔는데도 계속해서 다음 페이지를 불러온다.

  • 어떻게 하면 마지막 페이지를 설정해줄 수 있을까?

  • 가장 먼저 DB에 등록된 게시글의 총 개수를 불러와서 마지막 페이지의 값을 구한다.

// fetchBoardsCount API 요청하기
const FETCH_BOARDS_COUNT = gql`
  query fetchBoardsCount {
    fetchBoardsCount
  }
`;

// lastPage 구하기
const lastPage = Math.ceil(dataBoardsCount?.fetchBoardsCount / 10);
  • 1 페이지 미만, 그리고 lastPage가 화면에 출력된 이후로는 이전 페이지, 다음 페이지 버튼이 동작하지 않도록 만들어준다.
const onClickPrevPage = () => {
	// startPage가 1이면 하단 스크립트를 실행하지 않고 종료한다.
  if (startPage === 1) return; 
  setStartPage((prev) => prev - 10);
};
const onClickNextPage = () => {
	// startPage + 10가 lastPage보다 클 경우 하단 스크립트를 실행하지 않고 종료한다.
  if (startPage + 10 > lastPage) return;
  setStartPage((prev) => prev + 10);
};
  • 그리고 페이지네이션 map에 조건부 렌더링을 걸어서 lastPage보다 큰 숫자는 출력 되지 않도록 만들어준다.
    {new Array(10).fill(1).map(
      (_, index) =>
        index + startPage <= lastPage && (
          <span
            onClick={onClickPage}
            id={String(index + startPage)}
            key={index + startPage}
    
            {` ${index + startPage} `}
          </span>
        )
    )}

📂 이전 페이지/다음 페이지 이동 시 refetch

  • 11~20 페이지라면 11페이지21~30 페이지라면 21페이지로 게시글 목록이 함께 이동 되어야 한다.
const onClickPrevPage = () => {
	// startPage가 1이면 하단 스크립트를 실행하지 않고 종료한다.
  if (startPage === 1) return;
  setStartPage((prev) => prev - 10);
  refetch({ page: startPage - 10 });
};
const onClickNextPage = () => {
	// startPage + 10가 lastPage보다 클 경우 하단 스크립트를 실행하지 않고 종료한다.
  if (startPage + 10 > lastPage) return;
  setStartPage((prev) => prev + 10);
  refetch({ page: startPage + 10 });
};

✅ state 끌어올리기


  • React의 데이터 흐름은 상위 컴포넌트에서 하위 컴포넌트로 전달하는 하향식, 단방향 데이터 흐름을 따르고 있다.

  • 단방향 데이터 흐름의 장점 : 기능 변경 사항에 대한 코드 수정이 적어진다. 또한 복잡하지 않아 코드의 흐름을 알기 쉽다.

  • 단방향 데이터 흐름의 단점 : 자식 컴포넌트1의 state를 자식 컴포넌트2에서 보여주는 것이 불가능 또한, 자식 컴포넌트2의 state를 부모 컴포넌트에서 보여주는 것도 불가능하다.

  • 해결 방법 : 자식 컴포넌트의 state와 setState를 부모 컴포넌트로 끌어올려 선언해주면 된다. 그리고 props로 내려줄 경우, 자식 컴포넌트1, 2에서 모두 state를 사용할 수 있게 된다.

📌 요약


  • 페이지 번호를 클릭해서 이동하는 방식의 페이지 처리 방법이다.

  • 단방향 데이터 흐름 해결 방법으로 state와 setState를 부모 컴포넌트로 끌어올려 선언해주면 된다.

profile
2년차 프론트엔드 개발자

0개의 댓글