개발일기

카카오 지도 api에서 pagination을 통한 45개 데이터 가져오기 본문

SideProject(My-Selectshop-Finder)

카카오 지도 api에서 pagination을 통한 45개 데이터 가져오기

황대성 2024. 12. 24. 03:38

개요

카카오 지도 api를 사용하면서 분명 45개의 데이터를 제공한다고 되어있는데 왜 15개만 제공되는 의문이였다. 나머지 데이터를 얻기 위해 많이 헤맨 결과 pagination을 통해 45개의 데이터를 얻을 수 있다라는 걸 발견했다. 그래서 프로젝트를 진행하면서 45개의 데이터를 활용하기 위해 pagination을 구현한 것과 다른 기능을 만들면서 경험한 한계(?)에 대해 적어보려고 한다. 페이지네이션을 구현 해보자.

 

pagination

검색 결과의 페이징을 담당하는 클래스.

하나의 검색 결과에 대해 페이지 이동을 쉽게 할 수 있도록 도와준다.
직접 선언은 불가능하며 검색 결과를 다루는 콜백함수의 인자로 인스턴스가 생성되어 넘어온다.

공식문서에 나와있는 내용이다.

var places = new kakao.maps.services.Places();

// callback의 세 번째 인자로 Pagination의 인스턴스가 넘어온다.
var callback = function(result, status, pagination) {
	if (status === kakao.maps.services.Status.OK) {
		// do something

		// 특정 엘리먼트를 클릭했을 경우 다음 페이지 검색을 시도하는 예제
		var nextBtn = document.getElementById('nextBtn');

		nextBtn.click(function() {
			// 속성 값으로 다음 페이지가 있는지 확인하고
			if (pagination.hasNextPage) {
				// 있으면 다음 페이지를 검색한다.
				pagination.nextPage();
			}
		});
	}
};

places.keywordSearch('판교 치킨', callback);

Methods

 

nextPage() - 다음 페이지를 검색한다.

prevPage() - 이 전 페이지를 검색한다.

gotoPage(page) - 저장한 페이지를 검색한다.

gotoFirst() - 가장 처음 페이지를 검색한다.

gotoLast() - 가장 마지막 페이지를 검색한다.

totalCount - 현재 검색의 결과 목록의 총 갯수

hasNextPage - 현재 검색 결과 기준, 다음 페이지가 있는지 여부

hasPrevPage - 현재 검색 결과 기준, 이 전 페이지가 있는지 여부

current - 현재 페이지 번호

 

적용하기

import {
  boundsState,
  markersState,
  myLocationState,
  selectShopsState,
} from "@/globalState/recoilState";
import { MarkersType, PaginationType } from "@/types/placeType";
import React, { useState } from "react";
import { useRecoilState, useRecoilValue } from "recoil";

const useKakaoSearch = () => {
  const myLocation = useRecoilValue(myLocationState);
  const [selectshops, setSelectshops] = useRecoilState(selectShopsState);
  const [pagination, setPagination] = useState<PaginationType>();
  const [, setBounds] = useRecoilState<any>(boundsState);
  const [, setMarkers] = useRecoilState<MarkersType[]>(markersState);

  const searchPlaces = (currentPage: number = 1) => {
    const ps = new window.kakao.maps.services.Places();
    const keyword = "의류판매";
    const options: kakao.maps.services.PlacesSearchOptions = {
      location: new window.kakao.maps.LatLng(
        myLocation.center.lat,
        myLocation.center.lng
      ),
      sort: window.kakao.maps.services.SortBy.ACCURACY,
      page: currentPage,
    };
    ps.keywordSearch(
      keyword,
      (data, status, pagination) => placesSearchCB(data, status, pagination),
      options
    );
  };

// 장소의 정보를 얻을 수 있고, 그 정보를 state에 담아서 활용,
// pagination을 따로 만들기 위해 pagination 또한 state에 담아서 활용
  const placesSearchCB = (
    data: any[],
    status: string,
    pagination: PaginationType
  ) => {
    if (status === window.kakao.maps.services.Status.OK) {
      setSelectshops(data);
      displayPlaces(data);
      setPagination(pagination);
    }
  };
 
 // 장소의 좌표를 표기하기 위한 함수
  const displayPlaces = (data: any[]) => {
    const bounds = new window.kakao.maps.LatLngBounds();
    let newMarkers: MarkersType[] = [];
    data.forEach((place) => {
      const position = { lat: place.y, lng: place.x };
      newMarkers.push({ position });
      bounds.extend(new window.kakao.maps.LatLng(position.lat, position.lng));
    });
    setMarkers(newMarkers);
    setBounds(bounds);
  };

  return { searchPlaces, pagination, selectshops, myLocation };
};

export default useKakaoSearch;

 

 

인자로 pagition만 받으면 45개의 데이터를 활용할 수 있는건 아니다. 공식 문서와 같이 pagination의 메서드를 사용해서 다음 데이터를 얻어야 한다. 이유는 카카오 지도 api에서 45개의 데이터를 한번에 제공하지 않고 15개, 15개, 15개씩 나눠서 제공한다. 그렇기 때문에 다음 15개의 데이터를 보기 위해서 pagination을 구현 해야 한다.

pagination 구현하기

import React, { RefObject, useEffect } from "react";
import styled from "styled-components";
import Chevron from "@/assets/Chevron.svg";
import Chevrons from "@/assets/Chevrons.svg";
import { styleColor } from "@/styles/styleColor";

interface PropsType {
  pagination: any;
  currentPage: number; // 1이 기본값
  setCurrentPage: (page: number) => void;
  scrollRef : RefObject<HTMLDivElement>
}

const PaginationContainer = ({
  pagination,
  currentPage,
  setCurrentPage,
  scrollRef
}: PropsType) => {

// 페이지 이동시 가장 위로 이동
  useEffect(()=>{
    if (scrollRef.current) {
      scrollRef.current.scrollTo({ top: 0 });
    }
  },[pagination])

  const nextPageButtonHandler = () => {
    if (pagination && pagination.hasNextPage) {
      setCurrentPage(currentPage + 1);
      pagination.nextpage;
    }
  };

  const prevPageButtonHandler = () => {
    if (pagination && pagination.hasPrevPage) {
      setCurrentPage(currentPage - 1);
      pagination.prevPage;
    }
  };

  return (
    <S.PaginationContainer>
      <S.PageButtonWrap>
        <button onClick={() => setCurrentPage(pagination.first)}>
          <Chevrons
            transform={"rotate(180)"}
            fill={`${styleColor.GRAY[400]}`}
          />
        </button>
        <button onClick={prevPageButtonHandler}>
          <Chevron transform={"rotate(180)"} fill={`${styleColor.GRAY[400]}`} />
        </button>
      </S.PageButtonWrap>
      <S.PageNumberButtons>
        {Array.from({ length: pagination?.last }).map((_, index) => {
          return (
            <S.PageNumberButton
              key={index}
              $index={index + 1}
              $currentPage={currentPage}
              onClick={() => setCurrentPage(index + 1)}
            >
              {index + 1}
            </S.PageNumberButton>
          );
        })}
      </S.PageNumberButtons>
      <S.PageButtonWrap>
        <button onClick={nextPageButtonHandler}>
          <Chevron fill={`${styleColor.GRAY[400]}`} />
        </button>
        <button onClick={() => setCurrentPage(pagination.last)}>
          <Chevrons fill={`${styleColor.GRAY[400]}`} />
        </button>
      </S.PageButtonWrap>
    </S.PaginationContainer>
  );
};

export default PaginationContainer;

const S = {
  //...css code
};

 

결과

 

마무리

다른 기능을 하면서 카카오지도 api에서 제공하는 것을 그대로 사용해서 구현하기에는 조금 이질감이 있어서 데이터를 받아오는 방법과 pagination을 구현한 방법을 조금씩 변경해서 사용을 했는데 그것은 다음 글에서 설명하도록 하겠다. 위와 같이 데이터 45개를 pagination을 통해 받아 보았고 역시 공식문서를 보는 건 너무 어려운 일이였다.