크고 작은 문제들/어려움

[React] JavaScript로 작성된 카카오 지도를 TypeScript로 포팅하기

노새두마리 2023. 8. 3. 21:48

상황

카카오 지도 API를 활용함에 있어 우선 JavaScript 코드로 작성하여 구현하였습니다.

다음에서 제공하는 주소 검색 서비스와 카카오 지도 서비스 모두 동일하게 daum이라는 네임스페이스를 사용하여 서비스를 이용하고 있었고, 타입을 선언하기 곤란했습니다.

'24. 02. 05. - 당장 타입이 필요하신 분은 이쪽으로 https://github.com/JaeSeoKim/kakao.maps.d.ts 


변경 - any의 사용

  • kakao, daum 분리

지도 API를 활용할 때, kakao를 네임스페이스로 하여 타입을 선언한 글을 발견하여 any를 사용해서라도 타입을 정의하고 TypeScript 파일로 변환하고자 했습니다.

빠르게 기능을 완성해야 하는 현실과 타협하여 외부 라이브러리에 대하여 any 타입을 적용하고 TypeScript가 제공하는 이점을 누리지 못한 사례입니다.

우선 지도 서비스는 kakao, 주소 검색 서비스는 daum으로 분리하였습니다. 또한, 아래의 글을 참고하여 kakao에 대한 declare문을 추가하였습니다.

declare global {
  interface Window {
    kakao: any;
  }
}

 

const { kakao } = window;

export const Map = ({ address }: { address: string }) => {
  const divRef = useRef<HTMLDivElement | null>(null);
  const mapRef = useRef<any>(null);
  const markerRef = useRef<any>(null);
  const geocoderRef = useRef<any>(null);
  // Map 컴포넌트 최초 렌더링 시에만 실행
  useEffect(() => {
    if (kakao?.maps?.services) {
      const geocoder = new kakao.maps.services.Geocoder();
      geocoderRef.current = geocoder;
      const mapContainer = divRef.current, // 지도 div
        mapOption = {
          center: new kakao.maps.LatLng(37.556944, 126.923917), // 지도 중심좌표 - 홍대
          level: 5, // 지도의 확대 레벨
        };
      // map, marker 생성 및 ref 저장
      const map = new kakao.maps.Map(mapContainer, mapOption);
      mapRef.current = map;
      const marker = new kakao.maps.Marker({
        position: new kakao.maps.LatLng(37.556944, 126.923917),
        map: map,
      });
      markerRef.current = marker;
    }
  }, []);
  // 최초 렌더링 시 + address가 변경될 때마다 실행
  useEffect(() => {
    if (address) {
      // props로 전달된 address 검색 후 결과가 정상적으로 수신되면 해당 위치로 마커 이동
      geocoderRef.current?.addressSearch(
        address,
        (results: any, status: any) => {
          if (status === kakao.maps.services.Status.OK) {
            const result = results[0];
            const coords = new kakao.maps.LatLng(result.y, result.x);
            if (divRef.current) {
              divRef.current.style.display = 'block';
            }
            mapRef.current.relayout();
            mapRef.current.setCenter(coords);
            markerRef.current.setPosition(coords);
          }
        }
      );
    }
  }, [address]);

  return (
    <>
      <S.TitleButtonFlex>
        <S.Heading3>공연장 위치</S.Heading3>
      </S.TitleButtonFlex>
      <S.Paragraph>{address || '홍대'}</S.Paragraph>
      <S.MapDiv id="map" ref={divRef} />
    </>
  );
};

결과

any로 얼룩져 있기는 하지만 일단 TypeScript 파일로 성공적으로 변환하였습니다.


(추가) kakao.maps.d.ts 적용

https://github.com/JaeSeoKim/kakao.maps.d.ts 

이 분이 공유하신 타입을 사용할 겁니다.

타입을 가져와서 사용하기 위해서 해야 할 일은 두 가지입니다.

dev dependency로 타입을 설치합니다.

npm install kakao.maps.d.ts --save-dev

yarn add kakao.maps.d.ts --dev

tsconfig.json의 타입 경로에 kakao.maps.d.ts를 추가합니다.

// JSON
{
  ...,
  "compilerOptions": {
    ...,
    "types": [
      ...,
      "kakao.maps.d.ts"
    ]
  }
}

제 경우에 types를 추가했을 때 기존에 멀쩡하게 불러와지던 타입 정보가 불러와지지 않는 현상이 있었습니다. types와 typeRoots를 동시에 사용하면 발생하는 현상으로 보였고, 아래와 같이 typeRoots에 작성하는 방법으로 해결하였습니다.

{
  ...,
  "compilerOptions": {
    ...,
    "typeRoots": ["./node_modules/@types", "./@types", "./node_modules/kakao.maps.d.ts"],
  }
}

적용 완료


타입 정의 참고 자료:

 

Next.js, React에서 카카오맵 API 완벽하게 사용하기

Next.js, React에서 카카오맵 API 완벽하게 사용하기

mycodings.fly.dev

 

 

GitHub - JaeSeoKim/kakao.maps.d.ts: TypeScript Definitions for kakao.maps.* (Kakao(구 Daum) 지도 Web API)

TypeScript Definitions for kakao.maps.* (Kakao(구 Daum) 지도 Web API) - GitHub - JaeSeoKim/kakao.maps.d.ts: TypeScript Definitions for kakao.maps.* (Kakao(구 Daum) 지도 Web API)

github.com