개발일기
[내일배움캠프] 최종 프로젝트 - 스팟 공유: 구글 지도 API 키 발급, API 키 보안에 대하여, 작성 페이지에 구글 지도 띄우기(React, TypeScript) 본문
[내일배움캠프] 최종 프로젝트 - 스팟 공유: 구글 지도 API 키 발급, API 키 보안에 대하여, 작성 페이지에 구글 지도 띄우기(React, TypeScript)
코찡 2023. 8. 28. 17:271. 구글 지도 API 키 발급받기
* 다음 링크로 이동 -> https://mapsplatform.google.com/
Google Maps Platform - Location and Mapping Solutions
Create real world and real time experiences for your customers with dynamic maps, routes & places APIs from Google Maps Platform’s location solutions.
mapsplatform.google.com








* API 및 사용자 인증 정보 등의 콘솔 관리는 다음의 링크에서 가능 -> https://console.cloud.google.com/apis/
Google 클라우드 플랫폼
로그인 Google 클라우드 플랫폼으로 이동
accounts.google.com
2. 스팟 공유: 작성 페이지에 구글 지도 띄우기
* Maps JavaScript API 가이드 링크 -> https://developers.google.com/maps/documentation/javascript?hl=ko
Google Maps Platform 문서 | Maps JavaScript API | Google for Developers
Google Maps Platform 문서
developers.google.com
1) Google Maps JavaScript API용 TypeScript 유형 설치
아래 명령어를 통해 TypeScript에서 우리가 사용할 'google' 객체의 타입을 알려줘야 한다. 이 패키지를 설치하지 않으면 코드에서 'google' 객체를 사용할 때 TypeScript 에러 발생!
패키지 설치시 TypeScript는 Google Maps API의 type을 알게 되고, API 사용에 필요한 자동 완성, 타입 검사 등의 기능 사용 가능!
yarn add @types/googlemaps
2) Google Maps API script 코드 추가 - API 키 보안에 대하여
(1) 1차적 보안
API 키를 1차적으로 숨기고 싶다면 React의 .env.local과 같은 파일에서 환경 변수를 설정해 사용할 수 있지만 .env.local 파일에 저장된 환경 변수는 javascript 파일에서만 접근할 수 있고 index.html에서는 직접 접근할 수 없다.
따라서 나는 .env.local에 환경 변수를 설정한 뒤, src 폴더 하위 index.tsx 파일에서 동적으로 script를 생성하여 사용하도록 하겠다.
- 환경 변수 없이, index.html에서 API 코드 까고 script 생성한 기존 방법
// index.html -> .env 환경 변수 없이 API 키를 그냥 포함해서 사용하는 방법
<!DOCTYPE html>
<html lang="en">
<head>
<!-- 구글 맵 -->
<script async defer src="https://maps.googleapis.com/maps/api/js?key=<API 키 입력>®ion=kr"></script>
</head>
</html>
- .env.local의 환경 변수를 사용하여 index.tsx 파일에서 동적 script 생성하는 1차 보안된 방법
// root 경로에 .env.local 파일
REACT_APP_GOOGLE_MAPS_API_KEY=your_api_key_here
// src 폴더 하위 index.tsx
import ReactDOM from 'react-dom/client';
import App from './App';
import './index.css';
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(<App />);
// 다음의 동적 script 코드 추가
// .env.local의 환경 변수에 접근하는 방법 -> ${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}
// region=kr 설정 추가 안하면, 동해를 일본해라고 표기한다고 함 갯ㅅ끼들이
const script = document.createElement('script');
script.src = `https://maps.googleapis.com/maps/api/js?key=${process.env.REACT_APP_GOOGLE_MAPS_API_KEY}®ion=kr`;
script.async = true;
script.defer = true;
document.head.appendChild(script);
(2) 2차적 보안
그러나, 위의 방법처럼 .env.local을 사용하더라도 최종 컴파일된 javascript 코드에서는 API 키가 여전히 노출됨. Google Maps JavaScript API는 공개 API 키가 필요한 클라이언트 측 라이브러리이기 때문에 이를 피할 방법이 없다.
구글 맵 API 키를 HTML에 직접 포함시키면 모든 사람에게 노출되지만, 이는 실제로 Google Maps Platform 문서에서 추천하는 방법.
따라서 API 키를 보호하는 가장 좋은 방법은 아래와 같다.
- Google Cloud Console에서 API 키를 특정 리퍼러, 특정 도메인, IP 주소 또는 앱으로 제한한다.
- 서로 다른 서비스나 앱에 동일한 API 키를 사용하지 않는다.
- API 키 사용을 모니터링하기 위해 정기적으로 사용량 및 청구 보고서를 확인한다.
[ 9/2 (토) 추가 작성한 글]
드디어 vercel로 1차 임시 배포를 해서 'Google Map API 키 보안'을 설정해주도록 하겠다.
위에서 말한 'API 및 사용자 인증 정보 등의 콘솔 관리' 링크(https://console.cloud.google.com/apis/ )로 들어간다.
(아래 사진 1) API 보안을 설정해 줄 해당 프로젝트로 들어가서 > 사용자 인증 정보 > 보안 설정해줄 API 키 클릭
(아래 사진 2) 먼저 '애플리케이션 제한사항 설정' 해줄건데, 나는 배포한 웹사이트에서만 사용할 수 있게 설정할거임. ADD 클릭해서 배포한 웹사이트 주소 넣어주면 됨. (주소 마지막에 /* 을 넣어주면 해당 주소 하위로 사용되는 모든 주소에서 접근할 수 있음)
(아래 사진 3) 이제 'API 제한사항'에서, 키 제한을 선택해주고, 내가 사용한 API 키들만 선택해줌. 나는 구글 맵 불러오기(Maps Javascript API), 위도/경도로 마커 찍기(Places API), 위도/경도로 주소 변환하기(Geocoding API)를 사용해서 선택해주었음.
그럼 이제 내가 정한 웹사이트 내에서 내가 사용한 API만 호출할 수 있게 보안 설정 끝
3) 스팟 공유: 작성 페이지에 SpotMap 컴포넌트 추가
// pages 폴더 하위 SpotShareWrite.tsx
import SpotCalendar from '../components/common/calendar/SpotCalendar';
import SpotMap from '../components/spotShare/map/SpotMap';
function SpotShareWrite() {
return (
<div>
<SpotCalendar />
<SpotMap />
</div>
);
}
export default SpotShareWrite;
4) SpotMap 컴포넌트 -> 여기다가 구글 맵 띄울거야!
[참고]
index.tsx 파일에서 script를 동적으로 추가하면, 페이지가 로딩된 후에 script가 추가된다. 그러나, React 컴포넌트는 이 script가 완전히 로드되기 전에 렌더링을 시작하므로, google 객체가 정의되지 않은 상태에서 SpotMap 컴포넌트가 렌더링됨.
따라서, google 객체가 로드되었는지 확인하고, google 객체가 로드되어 checkIfGoogleIsLoaded 함수가 true를 반환하면, 그때서야 initializeMap 함수가 실행되게 하여 맵을 초기화
+) mapRef.current가 null이 아닌 경우(mapRef가 DOM 요소를 참조한 경우)에만 맵을 초기화!
// components 폴더 하위 spotShare 폴더 하위 map 폴더 하위 SpotMap.tsx
import { useEffect, useRef } from 'react';
const SpotMap: React.FC = () => {
// 구글 map을 렌더링할 참조 div 설정
const mapRef = useRef(null);
useEffect(() => {
// Google Maps API가 로드되었는 지 확인
const checkIfGoogleIsLoaded = () => {
return typeof window.google === 'object' && typeof window.google.maps === 'object';
};
// 지도 초기화 함수 (참조 div가 있다면, 서울, zoom 레벨 12)
const initializeMap = () => {
if (mapRef.current) {
const seoul = { lat: 37.5642135, lng: 127.0016985 };
const map = new google.maps.Map(mapRef.current, {
zoom: 12,
center: seoul,
});
}
};
// Google Maps API가 로드 되었다면,
if (checkIfGoogleIsLoaded()) {
initializeMap();
}
// Google Maps API가 아직 로드되지 않았다면,
else {
const googleMapScript = document.querySelector('script[src*="googleapis"]');
googleMapScript?.addEventListener('load', initializeMap);
}
}, []);
return <div ref={mapRef} style={{ width: '100%', height: '50vh' }} />;
};
export default SpotMap;