<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>haein_.log</title>
        <link>https://velog.io/</link>
        <description>'해인사팔만기록'은 프론트엔드 개발의 유익한 주제와 내용을 기록하여 공유하는 공간입니다.</description>
        <lastBuildDate>Wed, 09 Apr 2025 07:53:18 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>haein_.log</title>
            <url>https://velog.velcdn.com/images/haein_/profile/9aaef53d-d033-4216-b8ef-49f587a9b73a/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. haein_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/haein_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[❗️Google Maps에 `styles` JSON 스타일이 적용되지 않을 때 (결제 계정 문제)]]></title>
            <link>https://velog.io/@haein_/Google-Maps%EC%97%90-styles-JSON-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%B4-%EC%A0%81%EC%9A%A9%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C-%EA%B2%B0%EC%A0%9C-%EA%B3%84%EC%A0%95-%EB%AC%B8%EC%A0%9C</link>
            <guid>https://velog.io/@haein_/Google-Maps%EC%97%90-styles-JSON-%EC%8A%A4%ED%83%80%EC%9D%BC%EC%9D%B4-%EC%A0%81%EC%9A%A9%EB%90%98%EC%A7%80-%EC%95%8A%EC%9D%84-%EB%95%8C-%EA%B2%B0%EC%A0%9C-%EA%B3%84%EC%A0%95-%EB%AC%B8%EC%A0%9C</guid>
            <pubDate>Wed, 09 Apr 2025 07:53:18 GMT</pubDate>
            <description><![CDATA[<p>React + Google Maps API를 연동해서 지도를 꾸미던 중, 공식 문서에 나온 대로 <code>styles</code> JSON을 넣었는데도…<br><strong>지도가 아무 변화가 없다?!</strong></p>
<blockquote>
<p>&quot;분명히 styles는 제대로 넣었는데, 왜 기본 지도 그대로지?&quot;</p>
</blockquote>
<p>꽤 한참을 헤매다가 원인을 알아냈다.<br>바로, <strong>결제 계정 미등록</strong> 때문이다.</p>
<hr>
<h3 id="💡-해결의-실마리-stackoverflow에서-찾은-힌트">💡 해결의 실마리: StackOverflow에서 찾은 힌트</h3>
<p>구글링 중에 <a href="https://stackoverflow.com/questions/32846119/google-maps-not-applying-style">StackOverflow에서 한 유저의 답변</a>을 보고 결정적인 힌트를 얻었다:</p>
<blockquote>
<p>&quot;Styling is not working unless you have a valid API key.&quot;
&quot;Google Maps are working without the API key with a dark overlay saying &#39;For development purposes&#39;, so I didn&#39;t expect that some feature would be silently ignored.&quot; </p>
</blockquote>
<p>이 글을 보고 알게 됐다.
<strong>지도 자체는 API 키 없이도 어느 정도 작동하지만,</strong>
styles 같은 일부 고급 기능은 <strong>API 키 + 결제 계정이 모두 있어야 작동</strong>한다는 사실을!</p>
<hr>
<h3 id="🧩-문제-상황">🧩 문제 상황</h3>
<pre><code class="language-tsx">import mapStyles from &quot;./mapStyles.json&quot;;

&lt;GoogleMap
  // ...
  options={{ styles: mapStyles }}
/&gt;
</code></pre>
<p>스타일 JSON을 넣었는데도 전혀 적용되지 않고, 그냥 <strong>기본 구글맵 스타일</strong>만 나오는 상황. 콘솔 에러도 없고, 코드도 맞는데 뭐가 문제지?</p>
<hr>
<h3 id="✅-원인-google-maps는-결제-계정이-등록되어-있어야-스타일이-적용된다">✅ 원인: Google Maps는 결제 계정이 등록되어 있어야 스타일이 적용된다</h3>
<p><code>styles</code>를 직접 넣는 방식이든 <code>mapId</code>를 사용하는 방식이든,<br><strong>모두 Google Cloud Platform(GCP)에 결제 계정이 등록되어 있어야 작동한다.</strong></p>
<p>Google은 지도를 기본 형태로는 무료로 제공하지만,<br><strong>커스터마이징(스타일 적용)</strong>은 유료 기능에 포함되어 있어서 <strong>결제 계정이 필요</strong>하다.</p>
<hr>
<h3 id="🔧-해결-방법">🔧 해결 방법</h3>
<ol>
<li><a href="https://console.cloud.google.com/">Google Cloud Console</a></li>
<li>좌측 메뉴 &gt; <strong>Billing(결제)</strong>로 이동</li>
<li>현재 사용 중인 프로젝트에 결제 계정 연결</li>
<li>페이지 새로고침 or 앱 재실행</li>
</ol>
<p>적용되던 <code>styles</code> JSON이 드디어 반영된다 🎉</p>
<p>찾아보니 개인 프로젝트의 경우는 무료 체험 한도를 넘지 않기 때문에  </p>
<hr>
<h3 id="⚠️-주의할-점">⚠️ 주의할 점</h3>
<ul>
<li>GCP에서 <strong>Maps JavaScript API가 활성화</strong>되어 있는지 확인</li>
<li><strong>API 키에 리퍼러 제한이 있는 경우</strong>, 현재 개발 중인 URL이 허용되어야 함 (<code>localhost</code>, <code>127.0.0.1:3000</code> 등)</li>
<li>GCP에서 요구하는 <code>styles</code> <a href="https://mapstyle.withgoogle.com/">JSON 구조</a>를 지켜야한다. </li>
<li>결제 계정을 등록해도, <a href="https://mapsplatform.google.com/pricing/">무료 체험 환경</a>을 벗어나지 않는 한 요금이 청구되지 않지만, 막상 결제 정보를 입력하는 건 여전히 부담스럽게 느껴진다..💸</li>
</ul>
<hr>
<h3 id="🧪-적용-예시-정상-작동하는-경우">🧪 적용 예시 (정상 작동하는 경우)</h3>
<pre><code class="language-tsx">import mapStyles from &quot;./mapStyles.json&quot;;

&lt;GoogleMap
  // ...
  options={{ styles: mapStyles }} // 결제 계정 등록 시 정상 반영!
/&gt;</code></pre>
<p>아래 스크린샷은 mapStyles.json을 적용한 뒤 실제로 지도의 스타일이 정상 반영된 모습이다.
&quot;For development purposes only&quot; 문구도 말끔히 사라졌다. </p>
<p><img src="https://velog.velcdn.com/images/haein_/post/3721b4e9-d2d6-4701-b004-7c863302c168/image.png" alt="스타일 적용된 모습"></p>
<hr>
<h3 id="📌-마무리">📌 마무리</h3>
<p>구글맵 스타일링이 적용 안 될 때는 일단 <strong>결제 계정이 등록되어 있는지</strong> 먼저 체크하자.<br>API 키 설정, 콘솔 에러가 없더라도 스타일이 적용되지 않는다면 <strong>100% 결제 계정 때문일 가능성 높음.</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🗺️ React + Google Maps + GeoJSON으로 대한민국 시도별 색상 시각화하기]]></title>
            <link>https://velog.io/@haein_/React-Google-Maps-GeoJSON%EC%9C%BC%EB%A1%9C-%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD-%EC%8B%9C%EB%8F%84%EB%B3%84-%EC%83%89%EC%83%81-%EC%8B%9C%EA%B0%81%ED%99%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@haein_/React-Google-Maps-GeoJSON%EC%9C%BC%EB%A1%9C-%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD-%EC%8B%9C%EB%8F%84%EB%B3%84-%EC%83%89%EC%83%81-%EC%8B%9C%EA%B0%81%ED%99%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 08 Apr 2025 09:49:07 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요!  
이번 포스팅에서는 Google Maps API를 사용해서 <strong>대한민국의 시도(서울, 부산 등)</strong>를 <strong>GeoJSON 데이터</strong> 기반으로 맵에 그리고, 각 시도별로 <strong>서로 다른 색상</strong>을 입혀보는 과정을 정리해보았습니다.</p>
<p>프론트엔드 기술 스택은 <strong>React + TypeScript</strong>이며, 지도 위에 데이터를 시각화하는 간단한 예제를 만들어봤습니다.</p>
<hr>
<h2 id="🎯-목표">🎯 목표</h2>
<ul>
<li>Google Maps를 React에서 사용하기</li>
<li>GeoJSON을 기반으로 지도에 행정구역 표시하기</li>
<li>각 시도마다 다른 색상 입히기</li>
<li>마우스 오버시 스타일 변화 적용하기</li>
</ul>
<hr>
<h2 id="📦-사용한-라이브러리">📦 사용한 라이브러리</h2>
<pre><code class="language-bash">npm install @react-google-maps/api</code></pre>
<p><code>@react-google-maps/api</code>는 Google Maps를 React에서 쉽게 사용할 수 있도록 도와주는 라이브러리입니다.</p>
<hr>
<h2 id="🧩-geojson-데이터-준비">🧩 GeoJSON 데이터 준비</h2>
<p>GeoJSON은 다음과 같이 생긴 형태로, 각 시도별 경계 정보를 담고 있어요.</p>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;Feature&quot;,
  &quot;geometry&quot;: {
    &quot;type&quot;: &quot;Polygon&quot;,
    &quot;coordinates&quot;: [...]
  },
  &quot;properties&quot;: {
    &quot;CTP_KOR_NM&quot;: &quot;대구광역시&quot;,
    &quot;CTP_ENG_NM&quot;: &quot;Daegu&quot;
  }
}</code></pre>
<p><code>properties.CTP_KOR_NM</code> 속성을 활용해서 색상을 지정합니다.</p>
<hr>
<h2 id="🧪-구현-코드">🧪 구현 코드</h2>
<pre><code class="language-tsx">// components/KoreaMap.tsx
import { GoogleMap, LoadScript } from &quot;@react-google-maps/api&quot;;
import { useRef } from &quot;react&quot;;

const containerStyle = {
  width: &quot;100%&quot;,
  height: &quot;100vh&quot;,
};

const center = {
  lat: 36.5,
  lng: 127.5,
};

const regionColors: Record&lt;string, string&gt; = {
  서울특별시: &quot;#e41a1c&quot;,
  부산광역시: &quot;#377eb8&quot;,
  대구광역시: &quot;#4daf4a&quot;,
  인천광역시: &quot;#984ea3&quot;,
  광주광역시: &quot;#ff7f00&quot;,
  대전광역시: &quot;#ffff33&quot;,
  울산광역시: &quot;#a65628&quot;,
  세종특별자치시: &quot;#f781bf&quot;,
  경기도: &quot;#999999&quot;,
  강원특별자치도: &quot;#66c2a5&quot;,
  충청북도: &quot;#fc8d62&quot;,
  충청남도: &quot;#8da0cb&quot;,
  전라북도: &quot;#e78ac3&quot;,
  전라남도: &quot;#a6d854&quot;,
  경상북도: &quot;#ffd92f&quot;,
  경상남도: &quot;#e5c494&quot;,
  제주특별자치도: &quot;#b3b3b3&quot;,
};

export default function KoreaMap({ geoJson }: { geoJson: any }) {
  const mapRef = useRef&lt;google.maps.Map | null&gt;(null);

  const onLoad = (map: google.maps.Map) =&gt; {
    mapRef.current = map;

    map.data.addGeoJson(geoJson);

    map.data.setStyle((feature) =&gt; {
      const name = feature.getProperty(&quot;CTP_KOR_NM&quot;);
      const fillColor =
        typeof name === &quot;string&quot; &amp;&amp; name in regionColors
          ? regionColors[name]
          : &quot;#cccccc&quot;;

      return {
        fillColor,
        strokeColor: &quot;#555&quot;,
        strokeWeight: 1,
        fillOpacity: 0.6,
      };
    });

    map.data.addListener(&quot;mouseover&quot;, (event: any) =&gt; {
      map.data.overrideStyle(event.feature, { fillColor: &quot;#FEB24C&quot; });
    });

    map.data.addListener(&quot;mouseout&quot;, () =&gt; {
      map.data.revertStyle();
    });
  };

  return (
    &lt;LoadScript googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY!}&gt;
      &lt;GoogleMap
        mapContainerStyle={containerStyle}
        center={center}
        zoom={7}
        onLoad={onLoad}
      /&gt;
    &lt;/LoadScript&gt;
  );
}</code></pre>
<blockquote>
<p>참고: <code>.env</code> 파일에 Google Maps API Key를 넣어주고 <code>REACT_APP_GOOGLE_MAPS_API_KEY=...</code> 형식으로 설정해야 합니다.</p>
</blockquote>
<hr>
<h2 id="🤯-타입스크립트-이슈-해결">🤯 타입스크립트 이슈 해결</h2>
<p>처음엔 이런 에러가 발생했어요:</p>
<pre><code>TS2538: Type &#39;unknown&#39; cannot be used as an index type.</code></pre><p>이는 <code>feature.getProperty(&quot;CTP_KOR_NM&quot;)</code>의 반환 타입이 <code>unknown</code>이기 때문이었습니다.<br>이를 해결하기 위해 <code>typeof name === &quot;string&quot;</code>으로 먼저 체크한 다음,<br><code>name in regionColors</code>로 키 존재 여부를 확인해 <strong>타입 안정성</strong>을 확보했습니다.</p>
<pre><code class="language-ts">if (typeof name === &quot;string&quot; &amp;&amp; name in regionColors) {
  const fillColor = regionColors[name]; // 안전!
}</code></pre>
<hr>
<h2 id="🌈-결과-화면">🌈 결과 화면</h2>
<ul>
<li>대한민국 시도가 지도에 표시됨</li>
<li>각 시도마다 지정한 색상으로 구분됨</li>
<li>마우스를 올리면 fillColor가 변경되어 하이라이트 효과</li>
</ul>
<p><img src="https://velog.velcdn.com/images/haein_/post/e86a6bc3-7da7-40be-afb9-ddbfb06b1bee/image.png" alt=""></p>
<hr>
<h2 id="💡-배운-점">💡 배운 점</h2>
<ul>
<li>Google Maps API에서 <code>map.data.addGeoJson()</code>으로 GeoJSON을 직접 추가할 수 있다.</li>
<li><code>map.data.setStyle()</code>은 Feature별로 스타일을 커스터마이징할 수 있는 유용한 기능!</li>
<li><code>value is Type</code> 형태의 타입 가드 함수로 TypeScript에서도 안전한 GeoJSON 핸들링이 가능</li>
<li><code>in</code> 연산자는 객체의 키 존재 여부를 안전하게 체크하는 데 꼭 필요함</li>
</ul>
<hr>
<h2 id="🧩-다음-목표">🧩 다음 목표</h2>
<ul>
<li>시도 클릭 시 해당 지역 정보 표시</li>
<li>시각화에 animation 효과 추가</li>
<li>공공데이터 API와 연동하여 실시간 데이터 반영하기</li>
</ul>
<hr>
<h2 id="🙌-마무리">🙌 마무리</h2>
<p>지도 시각화는 생각보다 재미있고, 활용도가 높은 작업이었습니다.<br>특히 GeoJSON을 어떻게 활용하느냐에 따라, <strong>행정구역 시각화부터 데이터 기반 분석까지</strong> 다양한 프로젝트로 확장할 수 있겠다는 가능성을 느꼈어요.</p>
<p>궁금한 점이나 도움이 필요하신 분은 댓글로 남겨주세요!  
읽어주셔서 감사합니다 ☺️</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🗺️ Google 지도에 "For development purposes only"가 뜨는 이유는?]]></title>
            <link>https://velog.io/@haein_/Google-%EC%A7%80%EB%8F%84%EC%97%90-For-development-purposes-only%EA%B0%80-%EB%9C%A8%EB%8A%94-%EC%9D%B4%EC%9C%A0%EB%8A%94</link>
            <guid>https://velog.io/@haein_/Google-%EC%A7%80%EB%8F%84%EC%97%90-For-development-purposes-only%EA%B0%80-%EB%9C%A8%EB%8A%94-%EC%9D%B4%EC%9C%A0%EB%8A%94</guid>
            <pubDate>Tue, 08 Apr 2025 08:12:03 GMT</pubDate>
            <description><![CDATA[<p>Google Maps API를 연동해 프로젝트에 지도를 띄우던 중, 아래와 같은 <strong>알림창(alert)</strong>이 뜨는 걸 보신 적 있나요?</p>
<blockquote>
<p><strong>&quot;Google 지도를 제대로 로드할 수 없습니다. 이 웹사이트의 소유자이신가요?&quot;</strong>
<img src="https://velog.velcdn.com/images/haein_/post/ea485117-ad3a-4d4c-817f-3c438f78345c/image.png" alt="Google 지도를 제대로 로드할 수 없습니다. 이 웹사이트의 소유자이신가요?"></p>
</blockquote>
<p>처음에는 &quot;내 코드가 잘못됐나?&quot; 싶었지만, 찾아보니 문제는 <strong>API 키가 유효하지 않거나, 결제 계정이 등록되지 않은 경우</strong> 발생하는 현상이더라고요.</p>
<hr>
<h2 id="💳-원인은-바로-결제-계정-미등록">💳 원인은 바로 결제 계정 미등록!</h2>
<p>저의 경우, <strong>무료 크레딧 기간이 이미 만료된 상태</strong>였고,<br><strong>결제 계정을 등록하지 않아서</strong> 아래와 같이 지도에 워터마크가 뜨는 상황이었어요.</p>
<blockquote>
<p>지도 위에 <code>For development purposes only</code> 문구가 표시되고, 지도가 흐릿하게 출력됩니다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haein_/post/34805386-04d4-42f3-a028-ab45aaa8b15e/image.png" alt="For development purposes only "></p>
<hr>
<h2 id="📌-google-cloud-요금-정책-정리">📌 Google Cloud 요금 정책 정리</h2>
<p>처음에는 &quot;결제 등록 = 요금 바로 청구&quot; 라고 생각해서 망설였는데,<br><strong>Google Cloud의 요금 정책은 생각보다 친절하고 안전하게 설계되어 있더라고요.</strong></p>
<h3 id="✅-요금-관련-핵심-요약">✅ 요금 관련 핵심 요약</h3>
<ul>
<li>결제 계정을 등록하면 $300의 <strong>무료 크레딧</strong>이 제공됩니다.</li>
<li>크레딧이 소진되거나 유효기간이 끝나도, 사용자가 <strong>직접 유료 계정으로 전환하지 않는 한 요금은 청구되지 않습니다.</strong></li>
<li>결제 계정을 등록해도, <strong>무료 체험 환경을 벗어나지 않는 한 안전</strong>합니다.<ul>
<li><a href="https://cloud.google.com/free?hl=ko">구글 무료 체험 환경 정보</a></li>
<li><a href="https://mapsplatform.google.com/pricing/">구글맵 무료 사용량 정보</a></li>
</ul>
</li>
</ul>
<hr>
<h2 id="😅-그런데도-나는-아직-결제-등록을-못했어요">😅 그런데도 나는 아직 결제 등록을 못했어요</h2>
<p>아무리 안전하다고 해도, <strong>결제 정보를 넣는 건 살짝 부담스럽기도 하죠.</strong><br>그래서 저도 아직까지는 결제 등록을 하지 않았고,<br>그 결과 아래처럼 지도가 완전히 출력되지 않고 있습니다.</p>
<p>📷 <em>아래는 실제 내 프로젝트에서의 지도 상태</em>
<img src="https://velog.velcdn.com/images/haein_/post/34805386-04d4-42f3-a028-ab45aaa8b15e/image.png" alt="For development purposes only 이미지"></p>
<hr>
<h2 id="🔐-그래도-걱정된다면">🔐 그래도 걱정된다면?</h2>
<p>요금이 걱정된다면, 최소한 <strong>API 키의 보안 설정(리퍼러 제한)</strong>은 꼭 해두는 걸 추천드려요.<br>이렇게 하면 내 API 키가 다른 사이트에 도용되는 걸 방지할 수 있습니다.</p>
<p>👉 <a href="https://velog.io/@haein_/%EA%B5%AC%EA%B8%80-Maps-API-%ED%82%A4-%EB%B3%B4%EC%95%88%EC%9D%84-%EC%9C%84%ED%95%9C-Referrer-%EC%A0%9C%ED%95%9C-%EC%84%A4%EC%A0%95%EB%B2%95">Google Maps API 키 보안을 위한 Referrer 제한 설정법</a></p>
<hr>
<h2 id="✨-마무리">✨ 마무리</h2>
<p>결제 계정 등록이 무섭게 느껴질 수 있지만,<br>Google Cloud는 <strong>“내가 원하지 않는 한 요금이 나가지 않게”</strong> 잘 설계돼 있었습니다.<br>저처럼 무료 크레딧 기간이 끝났더라도, 조금 더 정보를 알고 나면<br>조심스럽게 사용해볼 수 있을 거예요 🙌</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[React + TypeScript로 Google Maps 위에 GeoJSON 시도 경계 시각화하기]]></title>
            <link>https://velog.io/@haein_/React-TypeScript%EB%A1%9C-Google-Maps-%EC%9C%84%EC%97%90-GeoJSON-%EC%8B%9C%EB%8F%84-%EA%B2%BD%EA%B3%84-%EC%8B%9C%EA%B0%81%ED%99%94%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@haein_/React-TypeScript%EB%A1%9C-Google-Maps-%EC%9C%84%EC%97%90-GeoJSON-%EC%8B%9C%EB%8F%84-%EA%B2%BD%EA%B3%84-%EC%8B%9C%EA%B0%81%ED%99%94%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 08 Apr 2025 07:14:12 GMT</pubDate>
            <description><![CDATA[<p>최근 프로젝트에서 <strong>대한민국 시도 경계 데이터를 Google Maps 위에 시각화</strong>할 일이 있었다.<br><code>React</code>와 <code>TypeScript</code>, 그리고 <code>@react-google-maps/api</code> 라이브러리를 활용해 이를 구현한 과정을 정리해본다.</p>
<hr>
<h2 id="✅-목표">✅ 목표</h2>
<ul>
<li>Google Maps에 대한민국 시도 경계 데이터를 GeoJSON으로 시각화</li>
<li>경계 hover 시 색상 변경</li>
<li>React + TypeScript 기반</li>
</ul>
<hr>
<h2 id="📦-사용-기술">📦 사용 기술</h2>
<ul>
<li>React</li>
<li>TypeScript</li>
<li><a href="https://www.npmjs.com/package/@react-google-maps/api">@react-google-maps/api</a></li>
<li>GeoJSON (대한민국 시도 경계 데이터)</li>
</ul>
<hr>
<h2 id="1-패키지-설치">1. 패키지 설치</h2>
<pre><code class="language-bash">npm install @react-google-maps/api</code></pre>
<hr>
<h2 id="2-google-maps-api-키-발급">2. Google Maps API 키 발급</h2>
<ul>
<li><a href="https://console.cloud.google.com/">Google Cloud Platform</a></li>
<li>프로젝트 생성 → <code>Maps JavaScript API</code> 활성화</li>
<li>API 키 발급 → <code>.env</code>에 저장</li>
</ul>
<pre><code class="language-bash">REACT_APP_GOOGLE_MAPS_API_KEY=your_api_key_here</code></pre>
<blockquote>
<p>⚠️ 꼭 <strong>리퍼러 제한</strong> 설정하자! (<code>*.yourdomain.com/*</code>)
리퍼러 제한 설정 방법이 궁금하다면 아래 글을 참고해보자!
<a href="https://velog.io/@haein_/%EA%B5%AC%EA%B8%80-Maps-API-%ED%82%A4-%EB%B3%B4%EC%95%88%EC%9D%84-%EC%9C%84%ED%95%9C-Referrer-%EC%A0%9C%ED%95%9C-%EC%84%A4%EC%A0%95%EB%B2%95">🔗 구글 Maps API 키 보안을 위한 Referrer 제한 설정법
</a></p>
</blockquote>
<hr>
<h2 id="3-geojson-준비">3. GeoJSON 준비</h2>
<ul>
<li><a href="https://www.data.go.kr">공공데이터포털</a>에서 대한민국 시도 경계 GeoJSON 다운로드</li>
<li><code>type: FeatureCollection</code> 형태여야 함</li>
<li>나는 이미 다른 분이 잘 정리해둔 GeoJSON 파일을 다운로드해서 사용했다. (정말 감사합니다! 🙏)  </li>
<li>GeoJSON 파일 출처: <a href="https://yeomss.tistory.com/267?utm_source=chatgpt.com">https://yeomss.tistory.com/267?utm_source=chatgpt.com</a></li>
</ul>
<pre><code class="language-json">{
  &quot;type&quot;: &quot;FeatureCollection&quot;,
  &quot;features&quot;: [
    {
      &quot;type&quot;: &quot;Feature&quot;,
      &quot;properties&quot;: { &quot;name&quot;: &quot;서울특별시&quot; },
      &quot;geometry&quot;: {
        &quot;type&quot;: &quot;Polygon&quot;,
        &quot;coordinates&quot;: [...]
      }
    },
    ...
  ]
}</code></pre>
<hr>
<h2 id="4-지도-컴포넌트-만들기">4. 지도 컴포넌트 만들기</h2>
<pre><code class="language-tsx">// components/KoreaMap.tsx
import { GoogleMap, LoadScript } from &#39;@react-google-maps/api&#39;;
import { useRef } from &#39;react&#39;;

const containerStyle = {
  width: &#39;100%&#39;,
  height: &#39;100vh&#39;,
};

const center = {
  lat: 36.5,
  lng: 127.5,
};

export default function KoreaMap({ geoJson }: { geoJson: any }) {
  const mapRef = useRef&lt;google.maps.Map | null&gt;(null);

  const onLoad = (map: google.maps.Map) =&gt; {
    mapRef.current = map;

    // GeoJSON 데이터 추가
    map.data.addGeoJson(geoJson);

    // 기본 스타일 지정
    map.data.setStyle({
      fillColor: &#39;#FFEDA0&#39;,
      strokeColor: &#39;#555&#39;,
      strokeWeight: 1,
      fillOpacity: 0.6,
    });

    // Hover 스타일
    map.data.addListener(&#39;mouseover&#39;, (event: any) =&gt; {
      map.data.overrideStyle(event.feature, { fillColor: &#39;#FEB24C&#39; });
    });

    map.data.addListener(&#39;mouseout&#39;, (event: any) =&gt; {
      map.data.revertStyle();
    });
  };

  return (
    &lt;LoadScript googleMapsApiKey={process.env.REACT_APP_GOOGLE_MAPS_API_KEY!}&gt;
      &lt;GoogleMap
        mapContainerStyle={containerStyle}
        center={center}
        zoom={7}
        onLoad={onLoad}
      /&gt;
    &lt;/LoadScript&gt;
  );
}</code></pre>
<hr>
<h2 id="5-apptsx에서-사용하기">5. App.tsx에서 사용하기</h2>
<pre><code class="language-tsx">import KoreaMap from &#39;./components/KoreaMap&#39;;
import sidoGeoJson from &#39;./SIDO_MAP_2022.json&#39;; // GeoJSON import

function App() {
  return &lt;KoreaMap geoJson={sidoGeoJson} /&gt;;
}

export default App;</code></pre>
<hr>
<h2 id="💡-tip">💡 Tip</h2>
<ul>
<li>GeoJSON 구조가 <code>FeatureCollection</code>이 아니면 <code>mapshaper.org</code>에서 변환 추천</li>
<li>도형이 너무 복잡하면 렌더링이 느릴 수 있으니 단순화 작업 고려</li>
<li>모바일에선 zoom 조절 필요 (8~9 적당)</li>
</ul>
<hr>
<h2 id="✅-결과">✅ 결과</h2>
<p><img src="https://velog.velcdn.com/images/haein_/post/213d0b3c-cfeb-4ce2-abe9-584ae6ba0800/image.png" alt=""></p>
<hr>
<h2 id="마무리">마무리</h2>
<p>Google Maps에서 GeoJSON을 쉽게 불러올 수 있다는 점은 정말 편리하다.<br>GeoJSON만 잘 준비되면 지도 시각화는 생각보다 어렵지 않다.</p>
<p>다음엔 시군구 단위 또는 마우스 클릭 시 정보 보여주기 등 인터랙션을 더해볼 예정!  
질문은 댓글로 편하게 남겨주세요 🙌</p>
<hr>
<p>필요하면 이 포스트에 맞는 이미지나 코드 스니펫용 gist도 같이 넣을 수 있어.<br>포스팅용으로 꾸며주는 것도 도와줄 수 있으니까, 원하면 알려줘!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[🔐 구글 Maps API 키 보안을 위한 Referrer 제한 설정법]]></title>
            <link>https://velog.io/@haein_/%EA%B5%AC%EA%B8%80-Maps-API-%ED%82%A4-%EB%B3%B4%EC%95%88%EC%9D%84-%EC%9C%84%ED%95%9C-Referrer-%EC%A0%9C%ED%95%9C-%EC%84%A4%EC%A0%95%EB%B2%95</link>
            <guid>https://velog.io/@haein_/%EA%B5%AC%EA%B8%80-Maps-API-%ED%82%A4-%EB%B3%B4%EC%95%88%EC%9D%84-%EC%9C%84%ED%95%9C-Referrer-%EC%A0%9C%ED%95%9C-%EC%84%A4%EC%A0%95%EB%B2%95</guid>
            <pubDate>Tue, 08 Apr 2025 05:03:29 GMT</pubDate>
            <description><![CDATA[<p>Google Maps API를 사용하는 프로젝트를 하다 보면,<br><code>API Key</code> 하나로 지도도 띄우고, GeoJSON도 로딩하고, 다양한 기능을 활용할 수 있어요.</p>
<p>하지만!  
이 <code>API Key</code>는 <strong>기본적으로 아무 웹사이트에서나 사용할 수 있기 때문에</strong><br>아무런 제한 없이 방치하면 <strong>다른 사람이 내 API 키를 마음대로 가져다 쓸 수도 있습니다.</strong><br>그 결과는? 👉 <strong>과금 폭탄</strong> 😱</p>
<p>그래서 꼭 필요한 게 바로 <strong>리퍼러(Referrer) 제한 설정</strong>이에요.</p>
<hr>
<h3 id="🧠-리퍼러referrer-제한이란">🧠 리퍼러(Referrer) 제한이란?</h3>
<blockquote>
<p><strong>내 API 키를 사용할 수 있는 웹사이트(출처, 도메인)를 지정하는 보안 설정</strong></p>
</blockquote>
<p>즉, “이 키는 오직 <code>내사이트.com</code>에서만 쓸 수 있어!” 라고 구글에 알려주는 보안 설정이에요.</p>
<p>예를 들어 이렇게 설정하면:</p>
<table>
<thead>
<tr>
<th>도메인</th>
<th>사용 가능 여부</th>
</tr>
</thead>
<tbody><tr>
<td><code>http://localhost:3000</code></td>
<td>✅ 사용 가능 (개발용)</td>
</tr>
<tr>
<td><code>https://myapp.com</code></td>
<td>✅ 사용 가능 (배포용)</td>
</tr>
<tr>
<td><code>https://badguy.com</code></td>
<td>❌ 차단됨</td>
</tr>
</tbody></table>
<hr>
<h3 id="✅-왜-꼭-설정해야-할까">✅ 왜 꼭 설정해야 할까?</h3>
<ul>
<li>내 API 키가 외부에 유출되더라도, <strong>지정된 도메인이 아니면 사용 불가</strong></li>
<li>의도치 않은 API 사용으로 <strong>Google Cloud 비용이 폭증하는 걸 방지</strong></li>
<li><strong>회사나 팀 프로젝트에서도 기본 보안 습관으로 강력 추천</strong></li>
</ul>
<hr>
<h3 id="⚙️-리퍼러-제한-설정-방법">⚙️ 리퍼러 제한 설정 방법</h3>
<ol>
<li><a href="https://console.cloud.google.com">Google Cloud Console</a> 접속</li>
<li><code>API 및 서비스 &gt; 사용자 인증 정보</code> 메뉴 클릭</li>
<li>사용 중인 <strong>API 키 선택</strong></li>
<li>&quot;애플리케이션 제한사항&quot;에서 👉 <strong>웹사이트(HTTP 리퍼러)</strong> 선택</li>
<li><strong>허용할 도메인 입력</strong></li>
</ol>
<pre><code class="language-txt">// 로컬 개발 환경
http://localhost:3000/*

// 배포 환경
https://yourdomain.com/*

// 서브도메인까지 허용할 경우
https://*.yourdomain.com/*</code></pre>
<ol start="6">
<li>💾 저장 버튼 클릭!</li>
</ol>
<hr>
<h3 id="📸-설정-화면-예시">📸 설정 화면 예시</h3>
<blockquote>
<p><strong>API 키 상세 설정 화면</strong></p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/haein_/post/eeac2840-93b2-4d40-a6bc-2ef8a2aff8eb/image.png" alt="Google API Key 설정 예시"></p>
<blockquote>
<p>위처럼 &quot;웹사이트&quot; 항목이 선택되어 있어야 합니다.</p>
</blockquote>
<hr>
<h3 id="🧪-설정-후-확인">🧪 설정 후 확인</h3>
<ul>
<li><p>잘못된 도메인에서 키를 쓰면 콘솔에 아래 오류가 떠요:</p>
<pre><code class="language-bash">RefererNotAllowedMapError</code></pre>
</li>
<li><p>반대로, 설정한 도메인에서는 문제 없이 지도가 잘 뜨면 OK!</p>
</li>
</ul>
<hr>
<h3 id="✅-요약">✅ 요약</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>목적</td>
<td>내 Google Maps API 키를 <strong>정해진 웹사이트에서만 사용하도록 제한</strong></td>
</tr>
<tr>
<td>이점</td>
<td>보안 강화, 무단 사용 방지, 예상치 못한 요금 폭탄 방지</td>
</tr>
<tr>
<td>추천 대상</td>
<td>모든 웹 기반 Google Maps API 사용자</td>
</tr>
</tbody></table>
<hr>
<p>지금 구글 지도 프로젝트를 시작하려고 한다면,<br><strong>API 키 발급 후 리퍼러 제한부터 꼭 설정해두세요.</strong><br>작은 습관 하나가 큰 사고를 막아줍니다 🔒</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[프로젝트 소개 및 분석 환경 설정]]></title>
            <link>https://velog.io/@haein_/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%86%8C%EA%B0%9C-%EB%B0%8F-%EB%B6%84%EC%84%9D-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@haein_/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%86%8C%EA%B0%9C-%EB%B0%8F-%EB%B6%84%EC%84%9D-%ED%99%98%EA%B2%BD-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Tue, 28 May 2024 03:49:39 GMT</pubDate>
            <description><![CDATA[<h3 id="프로젝트-목적">프로젝트 목적</h3>
<p>&#39;OpenDota 웹사이트 성능 최적화&#39; 프로젝트의 목적은 크롬 개발자 도구를 사용하여 오픈소스 프로젝트의 성능을 분석하고 개선하는 것이다. 우수한 사용자 경험을 제공하는 웹사이트의 기준으로서, 크롬에서 제시하는 성능 평가 지표<sup><a href="#footnote_1">1</a></sup>에서 <strong>90점 이상을 달성</strong>하는 것을 목표로 설정했다.</p>
<h3 id="분석-대상">분석 대상</h3>
<p>분석 대상은 <a href="https://github.com/odota/web">OpenDota</a>이다. Dota 2의 경기 데이터를 다양한 통계, 분석 보고서, 리더보드 등으로 제공하여 사용자들이 자신의 게임 성과를 확인하고 향상시킬 수 있도록 돕는 오픈소스 프로젝트이다.</p>
<p>OpenDota는 모든 페이지의 평균 성능 점수가 30점대로 낮았다. 이는 사용자 경험과 성능 면에서 개선이 필요한 상황을 의미한다. CLS, FCP, TTI, Speed Index, LCP 등의 모든 성능 지표가 모두 개선이 필요한 상태였기 때문에, 다양한 최적화 기법을 실험해볼 수 있는 이상적인 환경이었다. 따라서 OpenDota를 선정하여 성능 최적화의 효과를 분석하고자 한다. </p>
<div style="display:flex; gap: 10px;">
  <img src="https://velog.velcdn.com/images/haein_/post/d9c7004d-ab87-4b6e-aedc-6be11c7862d6/image.png" alt="메인 페이지" width="50%" />
  <img src="https://velog.velcdn.com/images/haein_/post/d1799509-f92f-4e9a-a5ab-6ef683a74717/image.png" alt="Team 페이지" width="50%"/>
</div>
<div style="text-align:center; font-size:15px">
  <span style="font-weight: bold; color: gray">그림1</span>
  메인 페이지(좌), Team 페이지(우)
</div>


<h3 id="분석-툴">분석 툴</h3>
<p>성능 측정 시 다음 툴들을 사용한다. </p>
<ul>
<li>Lighthouse</li>
<li>성능 통계</li>
<li>성능 </li>
<li>cra-bundle-analyzer</li>
</ul>
<h3 id="실험환경">실험환경</h3>
<p>성능 체크 환경은 다음과 같이 고정한다. </p>
<ul>
<li>Throttling
  Network: fast 3G
  CPU: 4x slowdown </li>
<li>뷰포트
  1920x1080 </li>
</ul>
<hr>
<!-- 각주 -->
<p><a name="footnote_1">1</a>: <a href="https://developer.chrome.com/docs/lighthouse/performance/performance-scoring?utm_source=lighthouse&amp;utm_medium=devtools&amp;hl=ko#color-coding">https://developer.chrome.com/docs/lighthouse/performance/performance-scoring?utm_source=lighthouse&amp;utm_medium=devtools&amp;hl=ko#color-coding</a></p>
]]></description>
        </item>
    </channel>
</rss>