맵 API 사용하기


  • 지도 API에는 다양한 종류가 있음

  • 우리나라에서 가장 많이 사용하는 것은 구글, 네이버, 카카오에서 제공하는 지도 API

  • 각 API 간에는 제공하는 기능의 종류, 비용 문제 등의 차이가 있기에,
    차후 서비스 개발 시 이러한 차이점을 고려하여 어떤 API를 사용할 지 선택가능






예시 - 카카오 맵 API


  • 카카오에서 제공하는 각 API를 사용하기 위해서는 [카카오개발자 페이지] 에 접속해서 각 코드를 가져와야 함






  • 로그인 후, 애플리케이션 추가를 통해 각각의 입력사항을 작성하고 저장
    *어플리케이션을 생성하는 것은 새로운 프로젝트를 생성하는 것과 비슷한 의미








  • 이후 카카오 지도 API 관련 페이지에 접속한 다음, Web 용 API 페이지로 이동







  • 생성한 어플리케이션에서 javascript 키를 복사






<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey='JavaScript 앱 키 입력'"></script>
  • 이후 Script 태그 안에 src 경로의 appkey= 부분에 복사한 javascript 키를 넣어서 kakao의 map관련 제공 기능을 다운받음






  • 이후 위 코드를 입력하여 화면상에 kakao map 을 표출

    [BUT!]
    Next.js 는 SSR(서버사이드 렌더링)을 지원하기 때문에 화면이 렌더링 되기전까지 해당 코드에서 사용중인
    document 객체는 화면이 렌더링 되기 전까지는 undefined 상태이기에 오류가 발생!!







SSR(서버사이드 렌더링)으로 인한 이슈 해결

  • 프론트 서버에서 페이지가 렌더링되어 보는 시점에 document(DOM) 이라는 것은 존재하지 않기 때문에 document is not defined 라는 에러가 발생함






declare const window: typeof globalThis & {
  // window라는 것의 타입은 globalThis(전역 설정 This) 이며, &(그리고) 그 타입에는 kakao 도 있다고 알려주는 것 (타입스크립트 에러 해결법)
  kakao: any;
};

export default function kakaoMapPage(): JSX.Element {
  useEffect(() => {
    const container = document.getElementById("map"); // 지도를 담을 영역의 DOM 레퍼런스
    const options = {
      // 지도를 생성할 때 필요한 기본 옵션
      center: new window.kakao.maps.LatLng(33.450701, 126.570667), // 지도의 중심좌표.
      level: 3, // 지도의 레벨(확대, 축소 정도)
    };

    const map = new window.kakao.maps.Map(container, options); // 지도 생성 및 객체 리턴
    console.log(map);
  }, []);
  • useEffect 함수 안에 kakao map 코드들을 넣어서 이 문제를 해결 가능
    *useEffect는 화면이 렌더링 된 이후 실행되기 때문에 렌더링 후 document(DOM) 객체가 생성되어 있는 상태에서 kakao map 코드가 실행되어 undefined 오류가 발생하지 않음!!


+a) 그냥 kakao 만 작성할 경우 에러 발생!!

center: new kakao.maps.LatLng(33.450701, 126.570667)
  • 이런 식으로 kakao에서 그대로 값을 뽑으려 하면 에러가 발생하며 kakao를 찾지 못함

    [원인]
    외부에서 API를 받으면 window 객체에 새롭게 저장되는데, 일반적으로 객체에서 값을 뽑아올 때 Obj.A.B~ 로 뽑아오는 것처럼 kakao 또한 전역 스코프인 window 객체에서 해당 값을 뽑아와야 하기 때문





declare const window: typeof globalThis & {
  kakao: any;
};
  • 이후 declare 를 이용해 window의 타입을 globalThis(전역 객체)로 지정해 주고 & 연산자를 이용해 {kakao : any} 라는 타입형식을 추가해 줌으로써 Typescript 에러를 해결



    -kakao는 외부에서 받아오는 API로 따로 window 객체안에 저장이 되지만, 외부에서 받아왔기 때문에 Typescript는 해당 타입을 추론하지 못함

    -따라서 globalThis 라는 Typescript에서 제공하는 타입을 window 객체의 타입으로 지정하고 그 안에 kakao의 타입을 명시해서 넣어 줌







+a) 왜 globalThis로 타입을 지정해주는가 ?

  • globalThis 로 타입을 지정해 주는 이유는 타입스크립트는 그 한계점으로 인해 외부에서 받아오는 API 가 어떤 환경에서 사용되는지 알 수 없기에, API의 전역 객체를 알지못함


  • globalThis 는 모든 환경(브라우저,node.js)에서 동일한 타입으로 작동하기 때문에 어떤 환경에서 사용되던지 타입을 제대로 지정해 줄 수 있음









  • 이후 플랫폼 페이지에서 웹사이트 도메인을 등록


    [이 과정을 끝내고 나면 화면에 지도 출력이 가능해짐]










페이지 이동 후 지도 출력해보기 (SPA와 CSR의 이해)


import { useRouter } from "next/router";

export default function KakaoMapRoutingPage() {
  const router = useRouter();
  const onClickMoveToMap = () => {
    router.push("/29-03-kakao-map-routed");
  };

  return (
    <div>
      <button onClick={onClickMoveToMap}>맵으로 이동하기 !</button>
    </div>
  );
}
  • 위와 같은 코드에서 버튼 클릭으로 지정된 페이지로 routing 하면





  • kakao 를 찾을 수 없다는 에러가 발생함






import { useRouter } from "next/router";

export default function KakaoMapRoutingPage() {
  // const router = useRouter();
  // const onClickMoveToMap = () => {
  //   router.push("/29-03-kakao-map-routed");
  // };

  return (
    <div>
      {/* <button onClick={onClickMoveToMap}>맵으로 이동하기 !</button> */}
      <a href="/29-03-kakao-map-routed">맵으로 이동하기 !!</a>
    </div>
  );
}
  • 이 때 router 를 이용하지 않고 <a> 태그를 이용해서 페이지를 이동하면 정상적으로 이동되며 설정한 코드에 맞게 kakao map을 로드함







MPA vs SPA


  • 위의 결과가 발생하는 이유는 routerSPA(Single Page Application) 으로 동작하고, <a> 태그는 MPA(Multi Page Application) 으로 동작하기 때문

    *router는 SPA 방식으로 동작하여 기존에 프론트 서버에서 모든 데이터를 다 다운받아 두었다가 필요한 페이지에 맞게 렌더링하기 때문에, useEffect 안의 kakao는 프론트서버에 존재하지 않고, 브라우저에서 정의되는 것임으로 에러가 발생했던 것!!





MPA (Multi Page Application)

  • 서로 다른 url을 가진 페이지들이 각각 독립적으로 존재

  • 렌더링하기 전에 각 리소스 파일 다운로드가 완료될 때까지 기다림 (동기)

  • 페이지가 이동될 때마다 프론트 서버에서 페이지를 그리고, 브라우저로 HTML/CSS/JS 파일을 보내주는 작업 진행
    *이는 주소창에 주소를 직접 입력해서 접속하는 방식과 동일

    페이지 이동 시마다 서버에서 데이터를 다운받아 오기 때문에 비효율적





SPA(Single Page Application)

  • SPA는 처음 접속할 때 프론트 서버에 있는 모든 페이지의 데이터를 한 번에 다 다운받음
  • 이후 페이지를 이동할 때, 페이지의 일부 컴포넌트만 교체하고 페이지를 다시 렌더링 (필요한 리소스만 요청, 전체 리렌더링 X)

  • 요청한 리소스 파일은 다운로드와 동시에 렌더링 (비동기)

*미리 모든 페이지를 다운받기 때문에 최초 로딩은 조금 느리지만, 이후 페이지 이동 시 기존에 다운받은 페이지를 렌더링하기 때문에 압도적으로 로드 속도가 빠름







  • <a> 태그를 사용해도 되지만, 이는 페이지 이동 시마다 새로 다운받아 오기 때문에 비효율적이며, Next.js를 사용하는 의미가 없어짐



import { useRouter } from "next/router";
import Link from "next/link";

export default function KakaoMapRoutingPage() {
  // const router = useRouter();
  // const onClickMoveToMap = () => {
  //   router.push("/29-03-kakao-map-routed");
  // };

  return (
    <div>
      {/* <button onClick={onClickMoveToMap}>맵으로 이동하기 !</button> */}
      <Link href="/29-03-kakao-map-routed">
        맵으로 이동하기 !!
      </Link>
    </div>
  );
}
  • Next에서 자체적으로 제공하는 <Link> 태그를 활용하면 문제를 해결하면서 효율적인 사용이 가능해짐
    *Link는 페이지 전환 시, 이동 될 페이지의 js 파일을 미리 다운받기 때문에 kakao를 제대로 찾을 수 있음 (에러발생 X)

  • <Link> 를 사용할 시, Next에서 제공하는 것이기에 SPA 방식으로 동작함

  • <Link> 는 시멘틱 요소를 가진 html 태그로 렌더링 되기 때문에 웹표준, 검색엔진 최적화 차원에서도 이점이 있음



    [Link는 클릭 시 무조건 이동되고, router는 이동 시 로직을 작성하고 제한을 걸 수 있기 때문에 상황에 맞게 각각 사용할 필요가 있음]







script 태그의 비동기 작동 이슈

  • 그냥 script 태그로 카카오 맵을 다운받을 경우, 다운과 함께 작성한 코드로 (window.kakao.~~) 지도 로드도 동시에 진행되기 때문에 PC의 다운속도가 느릴 경우 지도가 정상적으로 표시되지 않을 수 있음


useEffect(() => {
  // 여기서 직접 다운로드 받고, 다 받을때까지 기다렸다가 그려주기!!
  const script = document.createElement("script"); // html에 script라는 태그(Element)를 만든다.
  script.src =
    "//dapi.kakao.com/v2/maps/sdk.js?appkey='JavaScript API Key'&autoload=false";
  document.head.appendChild(script);

  script.onload = () => {
		window.kakao.maps.load(function () {
			const container = document.getElementById("map");
      const options = {
        center: new window.kakao.maps.LatLng(33.450701, 126.570667),
        level: 3,
      };
      const map = new window.kakao.maps.Map(container, options);
		}
	}
}
  • .onload 와 kakao에서 제공하는 kakao.maps.load 를 이용해서 문제 해결 가능

  • script.onload 로 script가 모두 다운되기까지를 기다린 다음, onload 함수 안에서 다시 kakao map 코드를 로드


  • 결과적으로 script가 모두 다운된 다음, kakao map이 로드되기 대문에 순서에 맞게 로직이 동작하여 지도가 정상적으로 보일 수 있게 됨

profile
막 발걸음을 뗀 신입

0개의 댓글