구글 맵 좌표 가져오기

J.A.Y·2024년 3월 30일
0

프로젝트

목록 보기
4/5

지오코더 참고문서

구글 자바스크립트 맵 API 활용기

이번 개인 프로젝트에서는 핵심이 날씨 정보를 불러오는 것입니다. 날씨 API 요청을 하기 위해선 위도와 경도값이 필요하기 때문에 구글 맵을 활용했습니다. 처음으로 활용하는 건데다가 TS로 변형시켜야 했기에 거의 3~4일을 거쳐 완성했던 것 같습니다.

아래는 제가 구글 맵을 TS로 적용하면서 겪은 에러와 해결 과정을 담고 있습니다.

1. ⚙️ENV 환경변수 Undefined Error

[오류]

  • Google Maps JavaScript API has been loaded directly without loading=async. This can result in suboptimal performance. For best-practice loading patterns please see https://goo.gle/js-api-loading

  • Google Maps JavaScript API warning: InvalidKey


[일반적인 원인] 이처럼 환경 변수가 `undefined`로 출력되는 이유를 찾아봤는데 대부분 아래 4개 중 지키지 않은 것 때문에 발생한다고 합니다.
  1. 프로젝트 폴더 루트에 알맞게 env 파일이 있는가?
  2. 형식이 key=value에 알맞는가? (공백, 다른 특수문자 X)
  3. key가 REACT_APP_으로 시작하는가?
  4. key를 불러올 때 process.env를 앞에 붙였는가?

그래서 4개의 경우를 모두 체크해봤는데, 이상하게도 제 코드에는 문제가 없었습니다.
파일 경로도 올바르게 작성해줬고, 키 또한 REACT_APP_MAP_API_KEY=발급 받은 API 라고 잘 적어줬습니다. 그리고 이를 불러와서 사용할 때는 process.env.REACT_APP_MAP_API_KEY로 문제없이 잘 적어줬습니다.

문제가 여전히 해결되지 않아 구글을 재검색했습니다.

그러던 중, env파일은 작성 후에는 VS CODE를 닫았다가 재실행해줘야 적용이 된다는 글을 보고 '혹시...?' 하는 마음에 . 닫고 다시 재실행해줬습니다.

그랬더니 놀랍게도 해결😓..... 원인은 '재실행을 해주지 않아서'였습니다..


2. 📁Redux로 공동으로 사용할 데이터 저장

그렇게 한참을 환경변수와 씨름을 하다 해결하고, 불러온 데이터들 중에서 위도와 경도값만 색출해 redux store에 저장해줬습니다. Input태그로 받아온 주소 또한 여러 곳에서 사용할 것 같아서 이것도 reudx에 저장되게끔 설정했습니다.

const inputText = document.createElement("input");
inputText.type = "text";
inputText.placeholder = "주소";
inputText.classList.add(styles.input)
inputText.value = inputValue;

inputText.addEventListener("change", (event) => {
  if(event.target instanceof HTMLInputElement) {
     // input 값이 변경될 때마다 inputValue 업데이트
     setInputValue(event.target.value);
     // Redux store의 address 값을 업데이트
     dispatch(setAddress(event.target.value));
   }
});

function geocode(request: google.maps.GeocoderRequest): void {
        clear();
        geocoder
          .geocode(request, (results, status) => {
            if (status === 'OK') {
              map.setCenter(results[0].geometry.location);
              marker.setPosition(results[0].geometry.location);
              marker.setMap(map);
              dispatch(setLocation(results[0].geometry.location.lat(), results[0].geometry.location.lng()));
            } else {
              alert("Geocode was not successful for the following reason: " + status);
            }
          });
      }

Redux의 state에도 타입 지정을...??😱❗
useSelector로 store에 있는 state들을 불러올 때, typescript를 사용하는 경우라면, state의 타입을 지정해줘야 오류가 나지 않습니다.
처음에 type을 지정하라는 오류가 났을 때, 'state의 타입이 뭐지?, object인가?' 싶어서 당황했는데, 검색해보니 저처럼 combine해서 사용하는 경우엔 일반적으로 type을 별도로 만들어서 지정해준다고 합니다.
그래서 아래와 같이 type을 생성한 후 지정해줬더니 오류가 더 이상 나지 않았습니다. (👍)

  • src > store > index.tsx (reducer 여러 개를 combine하는 곳)
export type RootState = ReturnType<typeof rootReducer>;
  • src > services > MAP > MapAPI.tsx
import { RootState } from '../../Store/Data/Reducers';

const address = useSelector((state: RootState) => state.address.address);
const { latitude, longitude } = useSelector((state: RootState) => state.location);

이번에 TS로 개발하면서 JS때는 상상도 할 수 없는, 사소한 이벤트 하나 하나, 타입에러가 정말 많이 발생하는구나... 라는 생각을 했습니다. 이 얘기를 동기분께도 했더니 본인도 타입에러가 수시로 나서 any를 많이 쓰게 된다고 하셨던 웃픈 일이... 모던 리액트 책 읽을 때 'any는 정말 최후의 수단으로 써야 하고, 가급적이면 타입을 지정해주거나 정말 모르겠을 때는 unknown을 적어줘야한다'길래, 속으로 '당연한 소리 아냐? 왜 굳이 any를 쓰지?'라고 생각했었는데, 실제로 TS를 쓰면서 any처럼 달콤한 유혹이 따로 없었습니다 왜 책 저자가 'any를 경계해야 한다.'는 부분을 강조했었는지 개발하면서 많이 느끼고 있습니다.


3. 🪢useEffect로 공통 데이터 업데이트 사항 추적

최근 리액트 스터디를 시작하면서 useEffect 훅은 단순히 언마운트, 마운트의 대체제로 사용하는 줄 알았는데, useEffect훅이 애초에 마운트 대체품으로 등장한 것이 아니며, 이렇게 의존 배열에 값을 넣어주면 해당 값 외의 다른 상태가 변경되더라도 컴포넌트가 렌더링되지 않아 렌더링 성능을 높여준다는 것을 알게 되었습니다. 그래서 이번 사이드 프로젝트에서 이러한 점을 이용해 웹 이용자가 주소를 입력할 때마다 맵이 초기화됨과 동시에 새로운 위도 및 경도값을 가져올 때만 컴포넌트가 재렌더링되고, 다른 상태값이 변경되었을 때는 컴포넌트가 재렌더링되지 않도록 해주었습니다.

useEffect(() => {
    const loadScript = async (url: string) => {
      //script 로직 
      });
    };

    const initMap = () => {
   		//맵을 어떻게 조작할 것인지에 관한 로직
    }

	// 구글 맵 API 로딩 함수
	const loadAndInitMap = async () => {
      try {
        await loadScript(`https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_MAP_API_KEY}&callback=initMap`);
        initMap();
      } catch (error) {
        console.error('Error loading Google Maps API:', error);
      }
    };

    loadAndInitMap();
    
    // 언마운트 시 실행되는 로직
    return () => {
      const script = document.querySelector('script[src^="https://maps.googleapis.com/maps/api/js"]');
      if (script) {
        script.remove();
      }
    };

  }, [address]);
profile
Done is better than perfect🏃‍♀️

0개의 댓글