[Next] Script 컴포넌트, kakao맵 오류

강동욱·2024년 3월 26일
0

들어가기 앞서

들어가기 앞서 nextjs에 Script 컴포넌트를 알고 가면 좋다.

문제 발생

처음 카카오맵을 nextjs에서 실행할 때는 다음과 같이 실행해야 된다.

<body>
	<Script
		type="text/javascript"
		strategy="beforeInteractive"
		src={`//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_MAP_KEY}&autoload=false&libraries=services`}
	/>
		<Navbar />
		{/* Layout UI */}
		<main className={`${noto.className}`}>{children}</main>
		<Modal />
</body>

카카오 문서에 따르면 v3 스크립트가 로딩이 끝나야지 kakao 객체에 접근할 수 있고 만약 로딩이 안끝났는데 kakao객체에 접근하면 에러가 발생되므로 nextjs 실행하기 전, 그리고 hydration이 일어나기전에 스크립트가 다운로드 되게 하는 strategy의 옵션 중 beforeInterective로 설정했다.

하지만 정적 라우팅에서는 문제가 없지만 동적 라우팅을 사용했을 때 Map 컴포넌트를 사용하려고 하면 다음과 같은 에러가 발생했다.

next 13은 새로운 버전이라 아직 정보가 많이 없어서 검색하다가 nextjs에서 다음과 같은 깃이슈를 발견하게 되었다. 결론부터 말하자면 이 에러의 원인 beforeInteractive의 스크립트를 사용하면 동적 라우팅을 이용할 때 더블인코딩이 발생하게 되고 그로 인해 에러가 발생한 것이라고 한다. 그래서 beforeInterective를 사용하지 말라고 한다. 그럼 beforeInterective를 사용하지 않고 로드하면 어떻게 될까?

아까도 말했듯이 v3 스크립트가 완전히 로딩이 된 상태가 아닌 상황에서 자바스크립트 코드를 즉 kakao 객체를 실행시키려고 하니 당연히 에러가 발생한다.

해결방법

에러가 발생하는 원인을 요약하면 다음과 같다.

  • beforeInteractive를 실행시키면 에러가 발생한다.
  • v3 스크립트가 완전히 로딩되지 않은 상태에서 kakao객체에 접근하면 에러가 발생한다.

그렇다면 에러 원인을 역으로 생각하면 beforeInteractive를 사용하지 않으면서 v3 스크립트가 완전히 로딩되고난 후에 카카오맵 객체를 실행 시키면 된다라는 결론을 추론해 볼 수 있다.

export default function TravelMap() {
  const [mapIsOpen, setMapIsOpen] = useState(false);

  return (
    <>
      <Script
        type="text/javascript"
        onReady={() => {
          kakao.maps.load(() => {
            setMapIsOpen(true);
          });
        }}
        src={`//dapi.kakao.com/v2/maps/sdk.js?appkey=${process.env.NEXT_PUBLIC_MAP_KEY}&autoload=false&libraries=services`}
      />
      {mapIsOpen &&
        <>
          <TravelMapUse />
          <TravelMarkerContainer />
        </>
      )}
    </>
  );
}

TravelMap 컴포넌트가 마운트되고 Script 컴포넌트가 로딩이 완료되면 onReady에 있는 콜백함수가 실행이 된다. 그리고 이때 kakao.maps.load를 실행시켜 v3 스크립트 로딩이 끝나는 시점에 setMapIsOpen을 실행시켜 동적으로 상태를 변경해주고 이때 travelMapUse, TravelMarkerContainer 즉 kakao 객체를 사용하는 컴포넌트를 마운트시켜주면 에러 없이 잘 동작하는걸 확인할 수 있다.

번외

onReady 대신 onLoad를 써도 별 문제가 없지만 둘의 차이점은 onLoad는 콜백함수를 한번 실행하지만 onReady함수는 컴포넌트가 마운트될 때마다 콜백 함수를 실행시킨다. Script Component를 통해 생성된 script 태그는 최초에 한번만 생성된다. 그렇다면 위에 코드에 onLoad를 적용하면 TravelMap 컴포넌트가 최초에 마운트 되고 스크립트 로딩이 완료되면 콜백함수를 실행시키고 이후에 다시 마운트되더라도 이미 스크립트는 로딩 되었기 때문에 콜백 함수를 실행시키지 않는다. 그렇기 때문에 다시 TravelMap이 마운트되면 mapIsOpen은 false이므로 지도가 화면에 보이지 않는다. 이를 해결하려면 mapIsOpen을 전역적으로 상태 관리를 해야하는데 한 컴포넌트 안에서만 쓰이는데 전역적으로 상태 관리 하는 것은 말이 안되기 때문에 컴포넌트가 마운트 될때 마다 콜백 함수를 실행해주는 onReady 속성을 사용해 오류를 해결하였다.

profile
차근차근 개발자

0개의 댓글