박주니 개발 정리

페이지네이션 함수 본문

react

페이지네이션 함수

박주니 2022. 9. 1. 17:58
728x90
반응형

1. DB에서 데이터 총 갯수를 가지고 와서 state로 설정합니다. 

 

            let count = response.data.all_count;
            // response.data.all_count는 해당 데이터에 전체 갯수를 가지온 값
            setTotalPage(count);

 

2.  맨마지막 버튼을 클릭시 맨마지막 페이지에 이동할 수 있는 state를 설정합니다. 

            // count 는 데이터 전체 갯수 
            // pageCtrlLength는 
            /*
            * 예를 들어서 하단에 1, 2, 3, 4, 5 ... 10 
            * 그다음에는 11, 12, 13 ... 20 으로 되는 경우에는 
            * 하단에 10개씩 숫자 넘어가야하고 
            * 한페이지에 20개씩 데이터를 출력해야한다면 
            * 1, 2, 3 ... 10까지 한페이지에 20개씩 즉 데이터 갯수가 200가
            * 넘어갔을 경우 이전과 다음 버튼이 생기게 된다. 
            */
            let pageNum = Number(count) / pageCtrlLength;
            // 소수점 한자리까지만 나오게 하기 위함
            let ceilPageNum = Math.ceil(pageNum * 10) / 10;
            setLastPage(ceilPageNum);
  • 참고 이미지 

pageCtrlLength 참고 이미지

  • 추가 설명 
    •  page에 state 설정한 부분에 총 페이지 갯수를 보냄으로써 페이지네이션 함수에서 구분해서 맨마지막에 페이지로 이동하게 합니다. 

3.  html 영역에서 페이지네이션 레이아웃을 설정합니다. 

              {/*
               *pagination
               *setNftCtrlNum은 리스트에 출력된 한페이지에 20개이고 10개가 나오니깐 200을 의미
               *totalPage>300 이전과 다음 button이 생성
               *totalPage<=300 데이터양에 맞게 페이지 갯수 나옴(이전과 다음 버튼은 생성되지 않음)
               */}
              {totalPage > setNftCtrlNum ? (
                <div className="pagination">
                  {
                    // 맨처음 버튼 클릭시 page는 무조건 1로 이동하기 때문에 setPage(1) 설정
                  }
                  <button
                    id="firstPage"
                    style={{ display: "none" }}
                    onClick={() => {
                      setPage(1);
                    }}
                  >
                    ◀◀
                  </button>
                  {
                    // 이전 버튼은 page에서 뒤로 가기 때문에 setPage(page-1) 설정
                  }
                  <button
                    id="prevPage"
                    style={{ display: "none" }}
                    onClick={() => {
                      setPage(page - 1);
                    }}
                  >
                    ◁
                  </button>
                  {
                    // 하단에 보여지는 페이지 숫자 
                    redering(page)
                  }
                  {
                    // 다음 버튼은 page에서 앞으로 가기 때문에 setPage(page+1) 설정
                  }
                  <button
                    id="nextPage"
                    onClick={() => {
                      setPage(page + 1);
                    }}
                  >
                    ▷
                  </button>
                  {
                    // 맨마지막 버튼 클릭시 page는 무조건 1로 이동하기 때문에 setPage(lastPage) 설정
                  }
                  <button
                    id="endPage"
                    onClick={() => {
                      setPage(lastPage);
                    }}
                  >
                    ▶▶
                  </button>
                </div>
              ) : (
                <div className="pagination">
                  {
                  // 10개이하일경우에는 바로 데이터에 총 페이지 갯수를 보여주게 설정   
                  redering(page)
                  }</div>
              )}
  • 추가 설명 
    • 이부분에서 주목해야할 부분은 setPage를 어떻게 설정했는 지와 rendering 함수는 어떻게 위치했는 지 주석 참고해주시길 바랍니다. 

4.  rendering 함수를 만듭니다. 이 함수는 page 변환이지 데이터 변환되는 함수는 별도로 만들어서 관리하셔야 합니다. 

function redering(page) {

}
  • 추가설명
    • page가 의미하는 것은 1, 2,3 ... 10 각각에 페이지를 의미하는 것이 아닌 예를 들어서 page가 1을 넣었을 경우 그 1의 의미는 하단에 페이지를 1, 2, 3, 4, 5, 6 .. 10 이 나오게 한다를 의미합니다. 2를 넣으면 11, 12, 13 .. 20이 됩니다. 

페이지네이션 함수 원리 참고

  • 먼저 크게 페이지네이션 함수는 페이지네이션 숫자 관리와 버튼 생성 관리 조건이 구분됩니다. 즉 그것만 담당하는 라이브러리로 만드는 것입니다. 한번에 페이지를 눌러서 그 조건안에 route 이동하는 부분까지 다 넣게 되면 코드가 지저분해집니다. 

5. 총 데이터 갯수에서 나올 수 있는 페이지 갯수와 예를 들어 1줄이 10개에 숫자가 나오고 한 페이지에 20개가 나온다고 가정했을 때 200 보다 큰지 작은지에 따라 조건을 다르게 작성합니다. 

  // 페이지네이션 숫자 변경 함수
  // page가 이동할 때마다 변함
  function redering(page) {
    /**
     * 10의 의미는 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
     * let setNftPageNum = 20;
     * let setNftCtrlNum = Number(setNftPageNum*10);
     * totalPage는 전체 갯수
     */
    // pageNum = 현재 한 페이지에 보여줄 수 있는 것이 20개일 때 최대 몇페이지까지 보여줄 수 있는 지 확인
    let lineCtrlNum = 10;
    let pageNum = Number(totalPage) / Number(setNftPageNum);

    // pageNum을 올림 ex) 11.4 일경우 12 즉 12페이지까지 존재하고 그 안에 3개가 있다는 의미
    // 실질적인 페이지 총 갯수
    let ceilPageNum = Math.ceil(pageNum);

    /**
     * ceilPageNum 즉 totalPageNum이 한줄에 있어야할 10보다 클 때
     */
    if (ceilPageNum > lineCtrlNum) {

    } 
    // 현재 페이지갯수가 10개 미만일 경우 
    else {

    }
  }

6. 총 데이터 갯수가 10의 배수가 아닐 경우 중간에 남은 페이지가 나오고 다음 페이지가 나오지 않게 하기 위해 먼저 페이지가 10을 넘었을 경우 1의 자리만 가지고 올 수 있는 변수를 만듭니다. 

let remainPage = String(ceilPageNum).substr(-1);
  • 추가 설명 
    • 방법은 다양합니다. 계산을 할 때 남은 페이지 갯수를 소수점 아래로 해서 가져오는 방법도 있고 빼서 가져오는 경우도 있는데 저는 이렇게 접근한 이유가 데이터가 많아지면 십의자리 백의 자리 계속 넘어가게 될텐데 그 중에 필요한 부분은 몇번을 돌릴 수 있는 지 여부이기 때문에 일의 자리 숫자로 충분하기 때문입니다. 

7. 페이지네이션 숫자 관리를 구현합니다.

        let lineCtrlNum = 10;
        // 반복문으로 1-10까지 보이게함
        for (let i = 1; i <= lineChangeNum; i++) {
          result.push(
            <span
              key={i}
              onClick={() => {
                //page가 1이면 결국 클릭했을 때 i만 남고 해당되는 데이터만 나옴
                movePage((page - 1) * 10 + i);
              }}
            >
              {(page - 1) * 10 + i}
            </span>
          );
        }
  • 추가 설명
    • 페이지네이션에 메인 로직으로써 반복문 10까지 돌리는 이유는 하단에 페이지 갯수가 10개만 필요하기 때문이고 (page - 1)*10 + i 부분은 html에 보여줄 1, 2, 3 ... 10을 맡게 됩니다. page에 따라서 11부터 시작될 지 21부터 시작될 지 결정이 됩니다. onClick에 있는 movePage 함수는 예를 들어서 3페이지를 눌렀을 경우 그에 따른 리스트를 보여줘야하는 데 주로 route 담당 함수 호출이라고 보시면 됩니다. 
    • movePage 함수 역할 
      • function movePage(page){}를 진행하게 되면 page 값 받은 것을 axios를 통해 서버에 전달하게 돼고 그 page를 토대로 백엔드를 laravel로 셋팅했다면 pagiate로 설정되어있어서 page에 값만 넘겨서 보내주면 해당 데이터에 맞게 셋팅이 될 것입니다. 
 $users = User::where([['grade', '>', Config::get('global_variable.USER_GRADE_ADMIN')],
            ['status', '<>' ,Config::get('global_variable.USER_STATUS_BASIC')]])->orderBy('id', 'DESC')->paginate(30)->items();
  • 백엔드를 라라벨로 사용했을 때 페이지네이션 예시 
  • node로 할 때에는 slice 활용해서 전체데이터를 리스트에 보내준 것을 쪼개서 진행하시면 될 것입니다. 

8.  10의 배수 여부 또는 10미만일 경우에도 달라지기 때문에 먼저 마지막 페이지로 이동했을 때 10의 배수가 아닐 경우에는 lineChangeNum이 변할 수 있게 조건문을 만듭니다. 

        let lineChangeNum = 10;
        if(Number(remainPage) > 0 && page === Math.ceil(lastPage)){
          lineChangeNum = remainPage;
        }
  • 추가 설명
    • 맨마지막 버튼을 눌렀거나 혹은 다음 버튼을 눌렀을 때 더이상 다음 페이지를 넘길 수 없을 때 반복문 돌리는 것도 10으로 다 돌리는 것이 아닌 남은 갯수만큼만 돌려야 하므로 
    • 1. Number(remainPage) > 0이 의미하는 것은 10의 배수 여부 체크를 의미하는 것이고 0보다 크다는 의미는 예를 들어서 24페이지로 끝나고 더이상 다음 페이지가 없다는 것을 의미합니다. 
    • 2. page === Math.ceil(lastPage) 의미하는 것은 lastPage는 총 페이지입니다. 이때 추가적인 조건을 더 생성해야하는데 page === lastPage일 경우 setPage(Math.ceil(lastPage)) 즉 page를 정수로 셋팅해서 그 페이지로 넘어갈 수 있게 만듭니다. 
    • 3. 두가지 공식이 만족한다는 것은 이미 맨 마지막페이지에 있는 것이고 반복문에 돌릴 갯수도 조정하게 됩니다. 

참고 자료

페이지네이션 기본 로직

        let lineChangeNum = 10;
        if(Number(remainPage) > 0 && page === Math.ceil(lastPage)){
          lineChangeNum = remainPage;
        }

        // 반복문으로 1-10까지 보이게함
        for (let i = 1; i <= lineChangeNum; i++) {
          result.push(
            <span
              key={i}
              onClick={() => {
                //page가 1이면 결국 클릭했을 때 i만 남고 해당되는 데이터만 나옴
                movePage((page - 1) * 10 + i);
              }}
            >
              {(page - 1) * 10 + i}
            </span>
          );
        }

9. 버튼 생성 관리를 구현합니다. 

        if (page === 1) {
          /**
           * 맨처음과 이전이 보이지 않는 이유는 그전에 숫자가 없기 때문
           */
          $("#firstPage").css("display", "none");
          $("#prevPage").css("display", "none");
          $("#endPage").css("display", "block");
          $("#nextPage").css("display", "block");
        } 
        else if (ceilPageNum > (page - 1) * 10 + 10) {
          $("#firstPage").css("display", "block");
          $("#prevPage").css("display", "block");
          $("#endPage").css("display", "block");
          $("#nextPage").css("display", "block");
        } 
        else {
          $("#firstPage").css("display", "block");
          $("#prevPage").css("display", "block");
          $("#endPage").css("display", "none");
          $("#nextPage").css("display", "none");
        }
  • 참고

  • << firstPage || < prevPage || >nextPage || >>endPage
    • 추가 설명
      • 조건 1 )  page === 1
        • page가 1일 경우 1, 2, 3, 4 ... 10이기 때문에 그 이전은 음수임으로 맨처음과 이전버튼이 생성될 수 없습니다. 그래서 맨처음과 이전을 dispay none으로 진행했습니다.
      • 조건 2) ceilPageNum > (page-1)*10 +10 
        • 단순하게 ceilPageNum 은 총 데이터의 페이지갯수이고 그것이 10개 넘어가면 기본적으로 맨처음, 이전, 다음, 맨마지막이 생기는데 처음 조건에서 page == 1조건을 건너왔기 때문에 page ==2 부터 진행된 것임으로 이미 다음 버튼을 누른 상태로 간주해야합니다. 
      • 조건 3) 
        • 조건 1과 2가 부합하다는 의미는 가장 마지막 페이지에 있다는 의미임으로 그 의미는 다음과 맨마지막 버튼이 생성될 수 없는 상태입니다. 조건 1과 반대로 맨마지막과 다음을 display none으로 진행했습니다. 

10. 10개미만일 경우 조건을 추가합니다. 

      for (let i = 1; i <= ceilPageNum; i++) {
        result.push(
          <span
            key={i}
            onClick={() => {
              movePage(i);
            }}
          >
            {i}
          </span>
        );
      }
      return result;
  • 추가 설명
    • ceilPageNum은 바로 데이터갯수에 맞는 페이지 갯수를 가지고 와서 반복문을 돌리면 됩니다. 

11. 마지막으로 해당 result.push로 작업했던 것을 return result로 내보내시면 됩니다. 

 

최종 코드 

  // 페이지네이션 숫자 변경 함수
  // page가 이동할 때마다 변함
  function redering(page) {
    /**
     * 10의 의미는 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
     * let setNftPageNum = 20;
     * let setNftCtrlNum = Number(setNftPageNum*10);
     * totalPage는 전체 갯수
     */
    // pageNum = 현재 한 페이지에 보여줄 수 있는 것이 20개일 때 최대 몇페이지까지 보여줄 수 있는 지 확인
    let lineCtrlNum = 10;
    let pageNum = Number(totalPage) / Number(setNftPageNum);

    // pageNum을 올림 ex) 11.4 일경우 12 즉 12페이지까지 존재하고 그 안에 3개가 있다는 의미
    // 실질적인 페이지 총 갯수
    let ceilPageNum = Math.ceil(pageNum);

    /**
     * ceilPageNum 즉 totalPageNum이 한줄에 있어야할 10보다 클 때
     */
    if (ceilPageNum > lineCtrlNum) {
      // 뒤에서 한자리 즉 일의 자리 숫자리를 남은 페이지 갯수로 계산
      let remainPage = String(ceilPageNum).substr(-1);
      /**
       * page는 정수단위
       * last page는
       */
      // 맨마지막을 눌렀을경우
      if (page === lastPage) {
        // setPage로 셋팅하면 page가 다시 rendering되어서 보여줌
        setPage(Math.ceil(lastPage));
      } 
      else {
        let lineChangeNum = 10;
        if(Number(remainPage) > 0 && page === Math.ceil(lastPage)){
          lineChangeNum = remainPage;
        }

        // 반복문으로 1-10까지 보이게함
        for (let i = 1; i <= lineChangeNum; i++) {
          result.push(
            <span
              key={i}
              onClick={() => {
                //page가 1이면 결국 클릭했을 때 i만 남고 해당되는 데이터만 나옴
                movePage((page - 1) * 10 + i);
              }}
            >
              {(page - 1) * 10 + i}
            </span>
          );
        }

        if (page === 1) {
          /**
           * 맨처음과 이전이 보이지 않는 이유는 그전에 숫자가 없기 때문
           */
          $("#firstPage").css("display", "none");
          $("#prevPage").css("display", "none");
          $("#endPage").css("display", "block");
          $("#nextPage").css("display", "block");
        } 
        else if (ceilPageNum > (page - 1) * 10 + 10) {
          $("#firstPage").css("display", "block");
          $("#prevPage").css("display", "block");
          $("#endPage").css("display", "block");
          $("#nextPage").css("display", "block");
        } 
        else {
          $("#firstPage").css("display", "block");
          $("#prevPage").css("display", "block");
          $("#endPage").css("display", "none");
          $("#nextPage").css("display", "none");
        }

        return result;
      }
    } 
    // 현재 페이지갯수가 10개 미만일 경우 
    else {
      for (let i = 1; i <= ceilPageNum; i++) {
        result.push(
          <span
            key={i}
            onClick={() => {
              movePage(i);
            }}
          >
            {i}
          </span>
        );
      }
      return result;
    }
  }

 

페이지네이션 정리하면서 느낀점 

  • 사실 코드는 정답이 없지만 다만 확실한 역할 분담을 하지 않으면 코드가 지저분해지고 유지보수하는데 시간이 오래걸린다는 것을 감안했을 때 각자의 역할을 어떻게 구분해서 최대한 코드가 길어지지 않는 것이 효율적인 코드라는 것을 알게 되었습니다. 원래 처음 페이지네이션 코드 짰을 때는 여러 경우의 수와 예외처리, route 설정등 다 넣다보니 코드가 1000줄이 넘어가는 것을 보게 되었습니다. 
  • 페이지네이션의 목적
  • 1.데이터 갯수에 따른 페이지에 보여지는 것
  • 2. 페이지를 클릭시 리스트 출력 함수 연결 

2번은 사실상 함수를 따로 구분하게 되면 1번에 기능에만 집중하면 되는 것이고 경우의 수는 최대한 변수 숫자변경만 하고 조건문과 반복문은 최대한 줄이면 될것입니다. 현재 올린 코드도 최적화된 코드가 아니기에 좀 더 최적화 하거나 효율적인 방법이 있으시면 알려주시면 감사하겠습니다. 

728x90
반응형
Comments