본문 바로가기
항해99/프로젝트

[TIL-30] 실전 프로젝트 - 카카오 지도 API 기능 구현

by junvely 2023. 5. 28.

Today목표 : 05/27일 실전 프로젝트 : 카카오 지도 API 기능 구현 

오늘은 카카오 Map API를 이용해 지도 기능을 구현했다. 어제 까지는 Nave map api를 사용해서 지도를 띄워보았는데, 몇 가지 이유로 Kakao Map API로 변경하기로 결정 했다. 

1. Naver에 비해 Kakao가 api 사용법이 굉장히 친절하고 공식 문서가 잘 되어 있었다.

2. Naver에 비해 Kakao 지도의 UI가 더 선명한 느낌이 들었다.

3. 우리 프로젝트에서는 주소를 우선 검색 하고, 주소 리스트를 띄운 후, 주소에 대한 지도를 띄워 주고자 했었다. Naver에서는 해당 기능을 api 공식 문서에서 찾기가 힘들었다. 이에 비해 Kakao에서는 이 기능을 지원하는 것으로 확인하였고, Kakao의 공식 문서는 굉장히 다양한 기능과 예제에 대해 쉽게 정리가 되어있었다. 또 코드에 주석처리로 설명을 잘 해주고 있어 처음 사용하는 나같은 사람도 한 눈에 로직을 파악하기가 용이했다.

이와 같은 이유로 Kakao Map API를 사용하기로 하여 다음과 같이 구현해 보았다.


알게된점,

 

❗트러블 슈팅 : Kakao 공식 문서의 자바스크립트 버전 예제를 React로 변경하기 - 페이지네이션 문제

문제상황

자바스크립트에서는 다음과 같이 직접 DOM을 생성하거나 조작한다. 때문에 함수 내에서 페이지네이션 리스트 DOM을 생성하면서 매개변수로 받아온 카카오 api에서 자체적으로 제공하는 gotoPage() 함수를 사용해 페이지네이션 요소들을 클릭 시 페이지를 전달하며 해당 페이지로 이동할 수 있다. (해당 페이지 정보로 변경되는 것 같다)

// 검색결과 목록 하단에 페이지번호를 표시는 함수입니다
function displayPagination(pagination) {
    var paginationEl = document.getElementById('pagination'),
        fragment = document.createDocumentFragment(),
        i; 

    // 기존에 추가된 페이지번호를 삭제합니다
    while (paginationEl.hasChildNodes()) {
        paginationEl.removeChild (paginationEl.lastChild);
    }

    for (i=1; i<=pagination.last; i++) {
        var 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.appendChild(fragment);

문제는 리액트에서는 이렇게 함수 내에서 DOM을 직접 조작하지 않고, 상태값으로 DOM을 조작하는 방식을 지향한다는 것이다. 때문에 리액트로 변환하면서 페이지네이션 배열 상태를 만들어 JSX에서 배열의 요소들로 DOM을 생성했다. 

여기서 발생한 문제가, 이 페이지 요소들을 클릭 시 해당하는 페이지의 데이터를 받아 장소 리스트를 변경해 주어야 하는데, 이 페이지 정보를 제공하는 pagination.gotoPage() 함수가 독립적으로 사용이 불가능하고, 함수의 매개변수로 받아서 사용하는 것만 가능하다는 것이다... 즉 함수 외부에 있는 DOM 요소에서 클릭됐을 때 해당 페이지의 정보를 받아오는 것이 불가능한 것으로 보였다. 

이 Pagination 매개변수를 받으면 상태값에 저장하여 사용하고자 했으나 이것도 불가능했다.. gotoPage()함수를 사용하려 하니 undefined 등 사용이 불가능 했다. 때문에 다른 방법이 필요했다.

 

시도한 점

1. 장소 리스트들을 제공받는 함수의 option을 찾아보며 분명 특정 page의 정보들만 요청하여 받아올 수 있을 것이라 생각하여 해당 option이 있는지 확인했다.

2. option에 page 속성으로 장소 리스트의 특정 page의 정보들만 요청하여 받아올 수 있는 것을 확인했다. 이를 이용하여 페이지네이션을 띄우는 로직과, 장소 리스트를 받아오는 로직을 분리하여 사용하면 gotoPage()함수가 없이도 상태값을 업데이트하여 장소 리스트를 변경할 수 있을 것이라 예상했다.

 

해결한 점

1. 페이지네이션을 띄우는 로직과, 장소 리스트를 받아오는 로직을 분리하였다.

2. 초기 검색 시 해당하는 페이지네이션 정보를 받아 페이지네이션을 띄우고, 첫 페이지의 장소 리스트들을 places 상태로 업데이트하여 첫 페이지 리스트를 보여줬다.

2. 페이지네이션이 클릭 되면, 해당 페이지 번호를 currentPage상태값에 저장하고, 페이지 클릭 시 마다 currentPage의 페이지 번호에 해당하는 데이터를 재요청하여 places 상태를 업데이트 해주도록 했다. 이렇게 하니 gotoPage()함수가 없이도 페이지가 클릭될 때 마다 해당 페이지의 정보로 places 상태가 업데이트 되어 해당 페이지의 정보들로 업데이트 할 수 있었다. 

const [searchInput, setSearchInput] = useState('');
  const [places, setPlaces] = useState([]);
  const [pages, setPages] = useState([]);
  const [currenPage, setCurrentPage] = useState('');

  const displayPagination = pagination => {
    const pagesArray = Array.from({ length: pagination.last }, (v, i) => i + 1);
    setPages([...pagesArray]);
    setCurrentPage(1);
  };
  
  const searchedResultStatusHandler = (data, status, pagination) => {
    if (status === kakao.maps.services.Status.OK) {
      setPlaces([...data]);
      console.log('초기 검색값 :', data);
      displayPagination(pagination);
    } else if (status === kakao.maps.services.Status.ZERO_RESULT) {
      alert('검색 결과가 존재하지 않습니다.');
    } else if (status === kakao.maps.services.Status.ERROR) {
      alert('검색 결과 중 오류가 발생했습니다.');
    }
  };

  const updatePlaces = (data, status) => {
    if (status === kakao.maps.services.Status.OK) {
      setPlaces([...data]);
    } else if (status === kakao.maps.services.Status.ZERO_RESULT) {
      alert('검색 결과가 존재하지 않습니다.');
    } else if (status === kakao.maps.services.Status.ERROR) {
      alert('검색 결과 중 오류가 발생했습니다.');
    }
  };

  const searchPlaces = page => {
    if (!searchInput.replace(/^\s+|\s+$/g, '')) {
      alert('키워드를 입력해주세요!');
      return false;
    }
    const options = {
      page: page || 1,
      size: 7,
    };

    if (page) { // 페이지 번호가 있으면 해당 페이지 번호로 정보 요청
      ps.keywordSearch(searchInput, updatePlaces, options);
    } else { // 초기값 첫 페이지 정보 요청
      ps.keywordSearch(searchInput, searchedResultStatusHandler, options);
    }
    return true;
  };

  const handlePageNumClick = page => { // 페이지 번호를 받아 해당 페이지 정보 요청
    setCurrentPage(page);
    searchPlaces(page);
  };

 

 

목표 달성 여부,

1. 카카오 로그인 기능 API 테스트 완료 ✅

2. Kakao Map API 사용해 주소 검색 기능 및 지도 띄우기 완료

 

느낀 점,

Kakao Map API를 사용하면서 기존 자바스크립트 코드를 리액트로 변환하는 것이 생각보다 어려웠다. 앞으로는 계속해서 이렇게 자바스크립트 방식을 리액트 방식으로 변환시킬 일이 많을텐데 미리 경험해보니 연습도 되고 좋았다. 이미 있는 API를 사용해 리액트 방식으로 변경하는 데에 성공한 것 같아 뿌듯했다.

페이지네이션이 우리 서버에서 정보를 받아오는 API가 있는 것이라 Kakao에서 자체 제공하는 기능을 사용해야 하다보니 기존 페이지네이션 보다 어려웠던 것 같다. 그래도 오늘 지도 구현과 더불어 페이지네이션까지 조금은 공부하게 된 것 같다. 

아쉬웠던 점은 아무래도 1. 변수명을 좀 더 직관적, 기능중심으로 작성하거나 함수를 재활용 할 수 있는 부분들을 더 리팩토링 할 수 있을 것 같다는 생각이 들었다. 오늘은 기능 구현에만 집중했지만 다음에는 리팩토링하면서 가독성이라던지 재사용성을 고려해 리팩토링 해봐야겠다는 생각이 들었다.

2. 오늘 구현한 부분은 검색어가 onChange 될 때마다 해당하는 주소값을 받아 Map을 띄우게 했는데, 이렇게 되면 검색어를 한 글자씩 입력 시 마다 데이터 요청이나 렌더링이 되는 것 같다. 또 잦은 요청 만큼 리소스가 많이 들 것으로 예상된다. 굳이 이렇게까지 잦은 데이터 요청, 렌더링으로 화면이 변환되는 것이 사용자의 입장에서 사용자 경험성을 크게  좋다고 느끼는 것이 아니라면, 클릭 시에만 반영되도록 하는 것도 좋을 것 같다는 생각이 들었다.