4월29일(토) Pagination(with DB에서 받아온 kakaoAPI)

Mindfulness·2023년 4월 28일
1

결론적으로 얘기하면 pagination 실패다. 우선 카카오 map API에서는 Pagination이라는 큰 데이터를 보내주고 거기서 다양한 정보와 함수들이 있다. 그걸 활용해서 그 전에는 pagination구현을 했다. 그런데 이번에는 우리 DB에서 값을 보내주기에 장소 값만 있다. 그래도 최대한 해보려고 다양한 방법을 해 보았다.

일반적인 Pagination 방법.

  [chunk.js파일]
  function chunk(arr, size) {
  const chunked_arr = [];
  let copied = [...arr];
  const numOfChild = Math.ceil(copied.length / size);
  for (let i = 0; i < numOfChild; i++) {
    chunked_arr.push(copied.splice(0, size));
  }
  return chunked_arr;
}

export default chunk;

[Pagination2.js]
function Pagination({ pages, activePage, setPage }) {
  return (
    <div
      style={{ display: 'flex', marginTop: '12px', justifyContent: 'center' }}
    >
      {pages.map((page) => (
        <div
          key={page}
          style={{
            height: '30px',
            width: '30px',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            borderRadius: '50%',
            marginRight: '12px',
            cursor: 'pointer',
            backgroundColor: activePage === page ? '#FF4840' : '#F4F4F4',
            color: activePage === page ? 'white' : '#BDBDBD',
          }}
          onClick={() => setPage(page)}
        >
          {page}
        </div>
      ))}
    </div>
  );
}

export default Pagination;


[실제 해당 컴포넌트 사용한 파일]  
  //페이지 네이션 처음 시작이 1번창부터 켜지도록
  const [activePage, setActivePage] = useState(1);
  
    //페이지네이션을 위한 구역 data 는 쿼리에서 먼저 undefined되기에 ? 로 있을 때
  //map을 돌릴 데이터를 4개씩 끊어서 라는 뜯 입니다 (9개ㅈ씩 끊고 싶으면 9 적으면 됩니다. )
  const chunkedData = data ? chunk(data, 4) : [];
  const currentPageData = chunkedData[activePage - 1] ?? [];
  
  return(
  <ReviewDiv>
            {currentPageData.map((calLog) => (
              <CalLogDiv key={calLog.id}>
                <img
                  src="Group 2248.png"
                  alt="글쓰기 수정 버튼"
                  className="editImg"
                />
                <div>
                  <p>{calLog.title}</p>
                  <p>{calLog.content}</p>
                  <p>{calLog.selectedDate.substr(2).replace(/-/gi, '.')}</p>
                </div>
              </CalLogDiv>
            ))}
            <Pagination
              pages={chunkedData.map((_, i) => i + 1)}
              activePage={activePage}
              setPage={setActivePage}
            />
          </ReviewDiv>
  )

실제로 우리 팀원이 구현한 pagination이다. 팀원분에게 해당 방법들 로직들 설명듣고 이 방식을 통해서 구현하려하였지만, kakao map의 api들이 뒤섞이고 유기적으로 엮인 나의 코드에서는 사용이 힘들었다. 왜냐하면 해당 방법은 단순히 데이터들 묶어서 페이지 만들어주는 것이지만, 나의 경우는 페이지 바뀔때 마다 지도의 마커도 바뀌고 지도도 해당 위치로 이동하는 등의 기능들이 다 연결이 되어 있기 떄문이다. 그래서 하는 수 없이 전에 하였던 pagination로직 (그러나 kakao map에서 Pagination이라는 데이터를 받아서 활용)으로 이번 중간위치 주변 술집 페이지에서 구현해보려고 노력하였다(백에서 kakaoApi중 장소 데이터들만 보내줌.)

아 왜 카카오 Api에서 바로 값을 안 받는지 생각을 할 수 있는데, 그렇게 되면 우리 프로젝트에서 해당 장소들의 id를 따올 수 없고 사진 등 이미지를 사용할 수 없기에, 현재 내가 검색을 하면 kakaoApi의 data를 백으로 보내줘서 db에 저장을 하고 다시 그 저장한 값을 불러오는.. 그런 식으로 진행이 되고 있기에 바로 카카오 API의 Pagination 기능 쓸 수 없었다.

그래도 시도를 해 보았는데 전에 키워드검색 모달창에서 사용하였던 Pagination 코드는 아래와 같다.

// 검색결과 목록 하단에 페이지 번호 표시
        function displayPagination(pagination) {
            const paginationEl = document.getElementById('pagination');
            const fragment = document.createDocumentFragment();
            // 기존에 추가된 페이지 번호 삭제
            while (paginationEl?.hasChildNodes()) {
                paginationEl.lastChild &&
                paginationEl.removeChild(paginationEl.lastChild)
            }
    
            for (let i = 1; i <= pagination.last; i++) {
                const el = document.createElement('a')
                el.href = '#'
                el.innerHTML = i
    
                if (i === pagination.current) {
                el.className = 'on';
                } else {
                el.onclick = (function (i) {
                    return function () {
                    pagination.gotoPage(i)
                    }
                })(i)
                }
    
                fragment.appendChild(el)
            }
            // paginationEl이 null이 아닐 때 appendChild 메소드를 호출
            if (paginationEl !== null) { 
                paginationEl.appendChild(fragment);
            }
        }

카카오 Developer사이트의 공식문서를 보며 콘솔을 찍어가며 하나하나 비교하여서 아래와 같이 수정하였다.

        function displayPagination(pagination) {
            const paginationEl = document.getElementById('pagination');
            const fragment = document.createDocumentFragment();
            // 기존에 추가된 페이지 번호 삭제
            while (paginationEl?.hasChildNodes()) {
                paginationEl.lastChild &&
                paginationEl.removeChild(paginationEl.lastChild)
            }
    
            for (let i = 1; i <= 3; i++) {
                const el = document.createElement('a')
                el.href = '#'
                el.innerHTML = i
    
                if (i === 1) {
                el.className = 'on';
                } else {
                el.onclick = (function (i) {
                    return function () {
                        //gotoPage Function알아내기.
                    // pagination.gotoPage(i)
                    }
                })(i)
                }
    
                fragment.appendChild(el)
            }
            // paginationEl이 null이 아닐 때 appendChild 메소드를 호출
            if (paginationEl !== null) { 
                paginationEl.appendChild(fragment);
            }
        }

우선 전체적으로는 같은데 kakao api를 쓴 부분은 내가 직접 바꾸었다. 예를 들어 pagination.last 같은 경우는 3으로 바꿨다. 왜냐하면 어짜피 3page 밖에 DB에서 받을 수 있는 값이 없었기 때문이다. 이런식으로 다 수정을 해주어서 다른 오류가 나지 않고 잘 되었지만 딱 한가지 바꿀 수 없는 게 있었다. gotoPage라는 함수다... 사실 이 부분 하나때문에 아래와 같이 Pagination 잘 되었지만 페이지 이동이 안 되었다.

그래서 gotoPage라는 함수를 따로 만들어보려고 이리저리 구글링하고 카카오 디벨로퍼스에 알아보고 chatGPT로 만들어도 보고 하였지만, 되지않았다.

현재 state의 값을 기준으로 함수에 입력한 값인 페이지 값만 변경하여 재요청 하는 기능입니다.
state라 함은 검색할 키워드, perPage, 지도 중심 좌표나 bound 등등
이 전 요청에서 사용한 값 그대로를 상태로 들고 있는 객체입니다.
페이지 a태그를 눌러서 요청을 보낼 때마다 goToPage메소드에서 placesSearchCB가 재호출된다.

/**
 * @public
 * @param {number} nubmer
 */
this.gotoPage = function (number) {

    number = Number(number);

    if (0 < number &&
        number <= totalPage && number !== current) {
        toPage(number);
    }
};

라고 카카오 디벨로퍼스에서 답변을 해주었고 위와 같이 함수를 만들어볼 수 도 있다고 하였다. 그렇지만 쉽게되지 않았다. 우선 지금 수준에서는 잘 이해가 안 되어 그런 거 같은데, 여기에 저장해두고 나중에 조금 더 성장했을 때, 저러한 함수도 직접 만들어서 쓸 수 있도록 더 노력해야겠다. 우선 지금에서는 백엔드분에게 카카오 api중 pagination정보도 같이 줄 수 없는지 요청하였고, 내일 중으로 답변을 주신다 하였다. 만약에 그 정보를 받을 수 있다면 곧바로 pagination은 구현 가능하겠고, 아니라면 다른 차선책을 활용해보아야겠다.

=> 단단히 꼬였다. 사실 카카오 API를 그대로 사용하면 너무나 수월히 해결될 문제이다. 그렇지만 백엔드에서 더욱 많은 걸 해보고 싶다고 하셨고, 또한 이미지 파일을 사용하기 위해 우리의 DB에 저장을 해야하기에 조금 특이한 방식으로 장소DATA받아오고 있다. 내가 카카오 API 검색을 하며 그 값을 동시에 서버로 보내주어 DB로 저장하고 다시 그 장소를 받아온다. 다른 부분들은 이에 맞추어 다 변경하였고 성공하였지만, Pagination은 현재 실패다. 페이지까지 만드는 부분은 하였지만, gotoPage라는 함수를 내가 직접 만들어야 했다. 백에다가 요청을 하였지만 pagination이라는 data를 같이 보내줄 수 없다고 하였고, 내가 gotoPage와 같은 동작을 하는 함수르 만들어 구현을 해주었으면 한다고 하였다.

gotoPage는 우선 searchbackCB라는 카카오 검색 함수를 같이 실행시켜 주면서 페이지를 이동하는 거다. 이 부분에서 본따 refatch 하여 페이지 마다 데이터 요청하고 값을 불러주어 실행하려고 시도했다.

       // 검색결과 목록 하단에 페이지 번호 표시
       function displayPagination(pagination) {
           const paginationEl = document.getElementById('pagination');
           const fragment = document.createDocumentFragment();
           // 기존에 추가된 페이지 번호 삭제
           while (paginationEl?.hasChildNodes()) {
               paginationEl.lastChild &&
               paginationEl.removeChild(paginationEl.lastChild)
           }
   
           const totalPage = Math.ceil(kakaoAllApi?.pageable_count / 3); // 전체 페이지 수
           const currentPage = pagination.currentPage; // 현재 페이지

           for (let i = 1; i <= totalPage; i++) {
               const el = document.createElement('a')
               el.href = '#'
               el.innerHTML = i
               console.log("@@el@@",el)
               console.log("@@el.innerHTML@@",el.innerHTML)
               console.log("@@pagination@@",pagination)
               console.log("@@currentPage@@",currentPage)
               if (i === currentPage) {
               el.className = 'on';
               } else {
               el.onclick = (function (i) {
                   return function () {
                       refetch({
                           url: `/kakaoApi?y=${midPointProp?.lat}&x=%20${midPointProp?.lng}&query=술집&radius=1500&page=${i}&size=15&sort=distance`
                       });
                       pagination.currentPage = i;
                       displayPagination(pagination);
                   }
               })(i)
               }
   
               fragment.appendChild(el)
           }
           // paginationEl이 null이 아닐 때 appendChild 메소드를 호출
           if (paginationEl !== null) { 
               paginationEl.appendChild(fragment);
           }
       }

이런식으로 수정했다. 근데 안 된다. 계속 같은 페이지를 불러온다. 그래서 콘솔로 하나하나 찍어가며 로직을 정리했는데 el.innerHTML은 숫자를 잘 나타내주고 그래서 currentPage는 1,2,3 순으로 잘 나타났다. 근데도 안 된다는 거는 refatch하는 Url의 문제도 아니고 그 위에 제일 처음 useQuery로 서버와 통신할때의 그 페이지 적힌 부분이 계속 동작하는 것 같았다. 그런데 함수가 다르기에 위에꺼의 Url도 아래와 같이 바꿀 수 가 없었다. 위에도 정말 다양한 방법들을 시도해보았다 그중

const { data, isLoading, isError, refetch } = useQuery({
  queryKey: ['GET_KAKAOAPI'],
  queryFn: async () => {
    let i = 1;
    const response = await apis.get(
      // 서버 URL
      `/kakaoApi?y=${midPointProp?.lat}&x=%20${midPointProp?.lng}&query=술집&radius=1500&page=3&size=15&sort=distance`,
      //테스트용 서버 URL
      // '/kakaoApi?y=37.534485&x=%20126.994369&query=술집&radius=1500&page=1&size=15&sort=distance',
      // 중간지점 lat,lng값
      midPointProp
    );
    while (i < totalPage) {
      const nextPage = await apis.get(
        // 서버 URL
        `/kakaoApi?y=${midPointProp?.lat}&x=%20${midPointProp?.lng}&query=술집&radius=1500&page=${i + 1}&size=15&sort=distance`,
        // 중간지점 lat,lng값
        midPointProp
      );
      response.data.documents = response.data.documents.concat(nextPage.data.documents);
      i++;
    }
    return response;
  },
  //  한 번 실행되고 100분 후 실행되도록. 100분 (단위: 밀리초)
  staleTime: 6000000
});

이런식으로도 해 보았다. 새로운 페이지의 결과를 현재 페이지 결과와 합치기 위해 concat 함수를 사용해보기도 했다.

        // kakao Pagination API중 gotoPage 함수(시도 실패) 
        // function gotoPage(page) {
        //     const pageSize = 15; // 페이지당 표시할 장소 수
        //     const pageCenter = (page - 1) * pageSize + pageSize / 2; // 페이지 중심 장소 인덱스
        //     const center = new kakao.maps.LatLng(kakaoApi[pageCenter]?.y, kakaoApi[pageCenter]?.x); // 페이지 중심 좌표
        //     map.setCenter(center); // 지도의 중심 좌표를 페이지 중심 좌표로 설정
        //     refetch({
        //         url: `/kakaoApi?y=${midPointProp?.lat}&x=%20${midPointProp?.lng}&query=술집&radius=1500&page=${page}&size=15&sort=distance`
        //     });
        // }

이거도 실패
그렇지만 결론적으로 페이지까지 아래에는 나오지만 그것을 클릭했을 때 같은 값이 계속 나온다. 정말 단순히 useQuery Url만 조금 손보면 될 거 같은데, 다 절묘히 안 되었다... 지금 계속 이걸 붙잡고 있을 수 는 없기에 우선 스크롤로 대체하고 다른 것들을 진행해야겠다.

profile
Junior Frontend Developer

0개의 댓글