๐ŸŒ [Flutter] ์ง€๋„ ๋„์šฐ๊ธฐ๋ถ€ํ„ฐ ๋งˆ์ปค๊นŒ์ง€, Google Maps ์—ฐ๋™ํ•˜๊ธฐ

Tygerยท2025๋…„ 9์›” 14์ผ
1

Flutter

๋ชฉ๋ก ๋ณด๊ธฐ
67/67
post-thumbnail

๐ŸŒ ์ง€๋„ ๋„์šฐ๊ธฐ๋ถ€ํ„ฐ ๋งˆ์ปค๊นŒ์ง€, Google Maps ์—ฐ๋™ํ•˜๊ธฐ

Google Cloud Platform(GCP)
Google Maps Platform
Platform Pricing & API Costs
Styling Wizard: Google Maps
Google Maps Platform - Documentation

์ด๋ฒˆ๊ธ€์—์„œ๋Š” Google Maps๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Flutter์—์„œ ์ง€๋„๋ฅผ ๋„์šฐ๊ณ  ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

Flutter์—์„œ ์ง€๋„๋ฅผ ๋„์šฐ๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ๋ฐฉ๋ฒ•๋“ค์ด ์žˆ๋‹ค.
๊ตญ๋‚ด ์„œ๋น„์Šค๋งŒ์„ ์ง€์›ํ•œ๋‹ค๋ฉด Naver, Kakao ๋“ฑ์˜ ์ง€๋„๋ฅผ SDK๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ , ๊ธ€๋กœ๋ฒŒ ์„œ๋น„์Šค๋ฅผ ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” Google, OSM(Open Street Map) ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

์—ฌ๋Ÿฌ ๋ฐฉ๋ฒ• ์ค‘ Google ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๋‹ค๋ค„๋ณผ ์˜ˆ์ •์ด๊ณ , ๋‹ค๋ฅธ ๊ธ€์—์„œ OSM, Naver, Kakao ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ๊ณต์œ ํ•  ์˜ˆ์ •์ด๋‹ค.

Google Maps๋Š” Google์ด ์ œ๊ณตํ•˜๋Š” ์ „์„ธ๊ณ„ ์ง€๋„ ๋ฐ ๋„ค๋น„๊ฒŒ์ด์…˜ ์„œ๋น„์Šค๋กœ, ์ผ๋ฐ˜/์œ„์„ฑ/์ง€ํ˜•/์‹ค์‹œ๊ฐ„ ๊ตํ†ต ๋“ฑ์˜ ๋‹ค์–‘ํ•œ ๋ทฐ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ณ  ๊ธธ์ฐพ๊ธฐ, ์ŠคํŠธ๋ฆฌํŠธ ๋ทฐ, ์œ„์น˜ ๊ฒ€์ƒ‰, ๋งค์žฅ ๋ฐ ์—…์ฒด ๊ฒ€์ƒ‰, ์žฅ์†Œ ๋ฆฌ๋ทฐ/๋ณ„์  ๋“ฑ์˜ ์œ„์น˜์™€ ๊ด€๋ จ๋œ ๊ธฐ๋Šฅ๋“ค์„ API๋ฅผ ํ†ตํ•ด ๋ฌด๋ฃŒ ๋˜๋Š” ์œ ๋ฃŒ๋กœ ์ œ๊ณตํ•ด์ฃผ๊ณ  ์žˆ๋‹ค.

์œ„์˜ ๊ธฐ๋Šฅ๋“ค์„ ๋ชจ๋ฐ”์ผ์ธ์ง€ ์›น์ธ์ง€์— ๋”ฐ๋ผ ์ œ๊ณต ๋ฒ”์œ„๊ฐ€ ๋‹ฌ๋ผ์ง€๊ฒŒ ๋˜๊ณ  ๊ธฐ๋Šฅ๋ณ„ API ๋น„์šฉ๋„ ์ผ๋ถ€ ์ฐจ์ด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ๋ฐ˜๋“œ์‹œ ์„œ๋น„์Šค์— ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์€ ๊ธฐ๋Šฅ์˜ ๋น„์šฉ์„ ํ™•์ธํ•˜์‹œ์–ด ๊ฐœ๋ฐœ์„ ์ง„ํ–‰ํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

Google Maps๋Š” ๊ตฌ๊ธ€์˜ ๋‹ค์–‘ํ•œ Platform์„ ์ง€์›ํ•˜๋Š” GCP(Google Cloud Platform)์—์„œ ์ œ๊ณตํ•˜๋ฉฐ, Maps Platform์˜ ๊ฒฝ์šฐ ๋ชจ๋“  ๊ณ„์ •์— ์›” $200 ๋ฌด๋ฃŒ ํฌ๋ ˆ๋”ง์„ ์ง€์›ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๋ฒ”์œ„๋‚ด์—์„œ๋Š” ๋ฌด๋ฃŒ ์‚ฌ์šฉ๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

Flutter๋กœ ๋ชจ๋ฐ”์ผ ๊ฐœ๋ฐœ์„ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” Maps SDK๋กœ ๋‹จ์ˆœํžˆ ์ง€๋„๋ฅผ ๋…ธ์ถœํ•˜๊ณ  ๋””๋ฐ”์ด์Šค์˜ GPS๋ฅผ ํ†ตํ•ด ์‹ค์‹œ๊ฐ„ ์œ„์น˜๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ์ •๋„์˜ ๊ธฐ๋Šฅ์€ ๊ณผ๊ธˆ ์—†์ด ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ์ฃผ์†Œ๋ฅผ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” Geocoding API๋‚˜ ์ขŒํ‘œ๋ฅผ ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” Reverse Geocoding API, ๊ฒฝ๋กœ๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” Directions API, ์žฅ์†Œ ๋ฐ ๋งค์žฅ ์ •๋ณด๋ฅผ ๊ฒ€์ƒ‰ํ•˜๋Š” Places API๋Š” ๊ณผ๊ธˆ ๋Œ€์ƒ์ด๋‹ˆ GCP ๊ฐ€๊ฒฉ ์ •์ฑ…์—์„œ ์ •ํ™•ํ•œ ๋น„์šฉ์„ ํ™•์ธํ•˜์‹œ๋ฉด ๋œ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” Google Maps์˜ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ตœ๋Œ€ํ•œ ๋‹ค๋ค„๋ณผ ์˜ˆ์ •์ด๋ฉฐ, ๋น„์šฉ์— ๋Œ€ํ•œ ๋ถ€๋ถ„์€ ๊ณ ๋ คํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ˆ ์ฐธ๊ณ ํ•˜์‹œ๊ธธ ๋ฐ”๋ž€๋‹ค.

Google Maps Setup

Google Maps ์‚ฌ์šฉ์„ ์œ„ํ•œ API ํ™œ์„ฑํ™” ๋ฐ ์ฝ˜์†” ์„ธํŒ…์„ ์ง„ํ–‰ํ•ด ๋ณด์ž.

Google Cloud Platform(GCP)์— ์ ‘์†ํ•˜์—ฌ, ์›ํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์„ ํƒํ•ด ์ฃผ๋„๋ก ํ•˜์ž.

GCP์— ์ ‘์† ํ›„ ์ฝ˜์†”์„ ํด๋ฆญํ•˜์—ฌ ์ด๋™ํ•ด ์ฃผ์ž.

์ขŒ์ธก ์ƒ๋‹จ์— ํ”„๋กœ์ ํŠธ๋ฅผ ๋ˆŒ๋Ÿฌ์„œ ์›ํ•˜๋Š” ํ”„๋กœ์ ํŠธ๋ฅผ ์„ ํƒํ•ด ์ฃผ์‹œ๋ฉด ๋œ๋‹ค. ๋งŒ์ผ ํ”„๋กœ์ ํŠธ๊ฐ€ ์•„์ง ์ƒ์„ฑํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด "์ƒˆ ํ”„๋กœ์ ํŠธ"๋ฅผ ์„ ํƒํ•ด์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์‹œ๋ฉด ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ GCP๋ฅผ ํ•œ ๋ฒˆ๋„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด ๋ฌด๋ฃŒ๋กœ ์‹œ์ž‘ํ•˜๊ธฐ๋ฅผ ํ†ตํ•ด ๊ณ„์ •์„ ๋“ฑ๋กํ•ด ์ฃผ๊ณ  ์ง„ํ–‰ํ•ด์ฃผ์‹œ๋ฉด ๋œ๋‹ค.

GCP์˜ API๋Š” ์œ ๋ฃŒ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์–ด ์‚ฌ์šฉํ•œ ๋งŒํผ ๊ณผ๊ธˆ๋˜๋Š” ๋ฐฉ์‹์ด๊ธฐ ๋•Œ๋ฌธ์—, ๊ณ„์ • ์ตœ์ดˆ ๋“ฑ๋ก์‹œ์—๋Š” ์นด๋“œ ์ •๋ณด๋ฅผ ๋ฐ˜๋“œ์‹œ ์ž…๋ ฅํ•ด์•ผ ํ•œ๋‹ค. ํ…Œ์ŠคํŠธ ์šฉ๋„๋ผ๋ฉด ํ•œ๋„๋ฅผ 100์›์œผ๋กœ ๋‚ฎ๊ฒŒ ์„ค์ •ํ•ด ์ฃผ์‹œ๋ฉด ๋œ๋‹ค.

ํ”„๋กœ์ ํŠธ๋กœ ์ด๋™์„ ์™„๋ฃŒ ํ•˜์˜€๋‹ค๋ฉด, ์ƒ๋‹จ์˜ ๊ฒ€์ƒ‰๋ฐ”๋ฅผ ํ†ตํ•ด Google Maps Platform์œผ๋กœ ์ด๋™ํ•ด ์ฃผ์ž.

์ด์ œ Google Maps Platform์œผ๋กœ ์ด๋™ํ•ด๋ณด๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด Google Maps Platform ์‹œ์ž‘ํ•˜๊ธฐ๊ฐ€ ๋…ธ์ถœ ๋˜๋ฉด์„œ ๋ฐ”๋กœ API ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ๋‚˜์˜ค๊ฒŒ ๋˜๋Š”๋ฐ, "Google Maps Platform์œผ๋กœ ์ด๋™" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•ด ์ฃผ๊ณ  ์šฐ์„  API ํ‚ค ๋ณดํ˜ธ๋Š” ๋‚˜์ค‘์—๋ฅผ ์„ ํƒํ•ด์„œ ๋„˜์–ด๊ฐ€ ์ฃผ๋„๋ก ํ•˜์ž.

์œ„์— API ํ‚ค ์ž๋™ ์ƒ์„ฑ ์ฐฝ์ด ๋…ธ์ถœ๋˜์ง€ ์•Š์•„๋„ ์•„๋ฌด ๋ฌธ์ œ ์—†๋‹ค.

๋Œ€์‹œ๋ณด๋“œ ์ขŒ์ธก์— "ํ‚ค ๋ฐ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด"๋กœ ์ด๋™ํ•ด ๋ณด๋ฉด, ๋ฐฉ๊ธˆ ์ƒ์„ฑํ•œ ํ‚ค๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒ์„ฑ๋œ ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ์„œ ๋“ค์–ด๊ฐ€ ๋ณด๋ฉด ์ƒ์„ธํ•œ ์ •๋ณด๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๊ฐ€ ํ•˜๋‚˜ ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค.

ํ‚ค ์ œํ•œ์‚ฌํ•ญ ํƒญ์—์„œ ์ ‘์† ๊ฐ€๋Šฅํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œํ•œ์‚ฌํ•ญ์ด ์žˆ๋Š”๋ฐ, ํ•ด๋‹น API ํ‚ค๋กœ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œํ•œ ์—ญ์‹œ 1๊ฐœ๋งŒ ๋“ฑ๋ก์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

Flutter๋Š” Android, iOS๋ฅผ ๋ชจ๋‘ ์ปค๋ฒ„ํ•˜๋Š” ํฌ๋กœ์Šค ํ”Œ๋žซํผ์ด๊ธฐ ๋•Œ๋ฌธ์—, ํ•˜๋‚˜์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๋งŒ ๊ฐœ๋ฐœํ•˜์ง€ ์•Š๋‹ค๋ณด๋‹ˆ API ํ‚ค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๋ณดํ˜ธํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋œ๋‹ค.

๋ณธ์ธ์˜ ์„œ๋น„์Šค์—์„œ ๋ณ„๋„๋กœ API ํ‚ค๋ฅผ ๋ณดํ˜ธํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค๊ณ  ํŒ๋‹จํ•˜์‹œ๋ฉด, ๊ทธ๋ƒฅ ์ œํ•œ ์—†์ด ํ•˜๋‚˜์˜ ์ƒ์„ฑ๋œ API๋กœ Android, iOS๋ฅผ ๋ชจ๋‘ ์‚ฌ์šฉํ•˜์—ฌ๋„ ์ƒ๊ด€์€ ์—†๋‹ค. ๋‹ค๋งŒ API ํ‚ค์˜ ์ œํ•œ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— API ํ‚ค ์œ ์ถœ๋กœ ์ธํ•œ ํƒ€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ œํ•œ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.

Android, iOS ์ค‘ ๋‹จ์ผ ํ”Œ๋žซํผ๋งŒ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•  ๊ฒฝ์šฐ๋ผ๋ฉด ํ•˜๋‚˜์˜ ํ‚ค๋งŒ ์ œํ•œ์‚ฌํ•ญ์„ ์„ค์ •ํ•˜์—ฌ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋œ๋‹ค.

์ƒ๋‹จ์— ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ๋งŒ๋“ค๊ธฐ๋ฅผ ํด๋ฆญํ•˜์—ฌ API ํ‚ค๋ฅผ ์„ ํƒํ•ด ์ฃผ๋ฉด ์ฆ‰์‹œ ์ƒ์„ฑ์ด ์™„๋ฃŒ๋œ๋‹ค.

์ƒ์„ฑ๋œ API ํ‚ค๋ฅผ ํด๋ฆญํ•˜์—ฌ ์ˆ˜์ •์„ ํ•ด๋ณด๋„๋ก ํ•˜์ž.

์šฐ์„  Android ์ „์šฉ API๋ฅผ ์ƒ์„ฑํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค. ํ‚ค ๊ตฌ๋ถ„์„ ์œ„ํ•ด ์ด๋ฆ„์„ ์ž์œ ๋กญ๊ฒŒ ์ˆ˜์ •ํ•ด ์ฃผ์‹œ๋ฉด ๋˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œํ•œ์‚ฌํ•ญ์— Android ์•ฑ์„ ์„ ํƒํ•˜๋ฉด ์•„๋ž˜์— ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฒ„ํŠผ์ด ๋‚˜์˜จ๋‹ค.

ํ˜„์žฌ ํ•ด๋‹น API ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋Š” ์•ฑ์˜ ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ๋„ฃ์–ด์ฃผ๊ณ , SHA-1 ์ธ์ฆํ‚ค๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

์šฐ์„  ํŒจํ‚ค์ง€ ๋„ค์ž„์€ ํ”„๋กœ์ ํŠธ์˜ Android > app > build.gradle ํŒŒ์ผ์•ˆ์— android ํƒœ๊ทธ์˜ namespace์—์„œ ํ™•์ธํ•˜๋ฉด ๋œ๋‹ค.

SHA-1 ๋””๋ฒ„๊ทธ ์ธ์ฆํ‚ค ์ถ”์ถœ์€ Mac ๊ธฐ์ค€์œผ๋กœ ํ”„๋กœ์ ํŠธ ์ˆ˜์ค€์—์„œ ํ„ฐ๋ฏธ๋„์„ ์—ด์–ด ์•„๋ž˜ ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ•ด์ฃผ๋ฉด ์ถœ๋ ฅ๋˜๋Š” ์ธ์ฆ์„œ ์ง€๋ฌธ์˜ SHA1์„ ๋ณต์‚ฌํ•ด์„œ ๋„ฃ์–ด์ฃผ๋ฉด ๋œ๋‹ค.

keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android

์œˆ๋„์šฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์•„๋ž˜ ๊ธ€์—์„œ ์ถ”์ถœ ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

SHA ์ธ์ฆ์„œ ์ถ”์ถœํ•˜๊ธฐ

์ฐธ๊ณ ๋กœ SHA1 ๋””๋ฒ„๊ทธ ์ธ์ฆํ‚ค๋Š” ๊ฐ PC๋งˆ๋‹ค ๊ณ ์œ ํ•œ ์ธ์ฆ์„œ์ด๋ฏ€๋กœ, ์—ฌ๋Ÿฌ ํŒ€์›๊ณผ ํ˜‘์—…ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๊ฐ ํŒ€์›์˜ PC์˜ ์ธ์ฆํ‚ค๋ฅผ ๋ชจ๋‘ ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค. ์ถ”๊ฐ€๋ฅผ ํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด API ํ‚ค ๋ณดํ˜ธ์— ๋”ฐ๋ผ API ์š”์ฒญ์ด ๊ฑฐ๋ถ€๋˜๊ฒŒ ๋œ๋‹ค.

SHA-1 ํ‚ค์™€ ํŒจํ‚ค์ง€ ๋„ค์ž„์„ ์ถ”๊ฐ€ํ•˜๊ณ  ํ•˜๋‹จ์— ์ €์žฅ์„ ๋ˆŒ๋Ÿฌ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ์ €์žฅํ•ด์ฃผ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ •์ƒ์ ์œผ๋กœ ์ถ”๊ฐ€๊ฐ€ ๋œ๋‹ค.

์ถ”๊ฐ€๊ฐ€ ๋” ํ•„์š”ํ•˜๋‹ค๋ฉด Add๋ฅผ ๋ˆŒ๋Ÿฌ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ธ์ฆ์„œ๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

์ž ์ด์ œ ๋ฐฐํฌ๋ฅผ ์—ผ๋‘ํ•œ ์ƒํƒœ์ด๊ฑฐ๋‚˜ ์ด๋ฏธ ๋ฐฐํฌ๋œ ์„œ๋น„์Šค์˜ ๊ฒฝ์šฐ ์Šคํ† ์–ด ์ธ์ฆ์„œ์˜ SHA-1 ํ‚ค๋„ ๋“ฑ๋ก์„ ํ•ด์ฃผ์–ด์•ผ ์ •์ƒ์ ์œผ๋กœ ์‚ฌ์šฉ์ž๋“ค์ด ์„œ๋น„์Šค ์ด์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

Play Console๋กœ ์ด๋™ํ•˜์—ฌ ์•ฑ ๋ฌด๊ฒฐ์„ฑ > ์•ฑ ์„œ๋ช… ํƒญ์œผ๋กœ ์ด๋™ํ•˜๋ฉด ๋ฐฐํฌ๋œ ์„œ๋น„์Šค์˜ ์•ฑ ์„œ๋ช… SHA1 ํ‚ค ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๋‹น ํ‚ค๊ฐ€ ๋“ฑ๋ก๋˜์ง€ ์•Š์œผ๋ฉด ์Šคํ† ์–ด์—์„œ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์€ ์•ฑ์€ API ์ ‘๊ทผ์ด ๊ฑฐ์ ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐฐํฌ๋œ ์„œ๋น„์Šค๋ผ๋ฉด ๋ฐ˜๋“œ์‹œ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๊ณ  ์•„์ง ๋ฐฐํฌ ์ „์ด๋ผ๋ฉด ๋ฐฐํฌ์‹œ ๊ตฌ๊ธ€์—์„œ ์„œ๋ช…ํ•˜๋Š” ์•ฑ ์„œ๋ช… ํ‚ค๋ฅผ ๋†“์น˜์ง€ ๋ง๊ณ  ํ•ด๋‹น APIํ‚ค์— ๋“ฑ๋ก์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.

์ด๋ฒˆ์—๋Š” iOS API ์ „์šฉ ํ‚ค๋ฅผ ๋ฐœ๊ธ‰ํ•ด ๋ณด๋„๋ก ํ•˜์ž.

์œ„์—์„œ ์ˆ˜ํ–‰ํ•œ ์ ˆ์ฐจ๋Œ€๋กœ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ๋งŒ๋“ค๊ธฐ > API ํ‚ค๋ฅผ ์„ ํƒํ•˜์—ฌ ์ƒˆ๋กœ์šด ํ‚ค๋ฅผ ์ƒ์„ฑํ•œ ๋’ค์— ์ ์ ˆํ•œ ๋„ค์ž„์„ ๋งŒ๋“ค์–ด์ฃผ๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œํ•œ์‚ฌํ•ญ์— iOS ์•ฑ์„ ์„ ํƒํ•ด ์ฃผ๋„๋ก ํ•˜์ž.

iOS๋Š” ์•ฑ์˜ Bundle ID๋ฅผ ์ถ”๊ฐ€๋งŒ ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ์ด์ œ ํ•ด๋‹น ์„œ๋น„์Šค์˜ Bundle ID์— ๋“ฑ๋ก๋œ ์•ฑ๋งŒ API ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•ด ์ง„๋‹ค.

Bundle ID๋Š” ํ”„๋กœ์ ํŠธ์˜ iOS ํด๋”๋ฅผ ์šฐํด๋ฆญํ•˜์—ฌ XCode("Open In Xcode")๋ฅผ ์—ด์–ด์ค€ ๋’ค Signing & Capablities ํƒญ์˜ Bundle Identifier ๊ฐ’์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ด์ œ API ํ‚ค ์ƒ์„ฑ ๋ฐ ๋ณดํ˜ธํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ดค์ง€๋งŒ, ์•„์ง ์ค‘์š”ํ•œ ๋‹จ๊ณ„๊ฐ€ ๋‚จ์•„์žˆ๋‹ค.

์ƒ์„ฑํ•œ API ํ‚ค๋กœ ์–ด๋–ค GCP์˜ API๋“ค์„ ์‚ฌ์šฉํ• ์ง€๋ฅผ ์„ ํƒํ•˜์—ฌ ํ™œ์„ฑํ™” ํ•ด์ค˜์•ผ ํ•œ๋‹ค. ํ˜„์žฌ๋Š” API ์ œํ•œ์‚ฌํ•ญ์„ ์„ค์ •ํ•˜์ง€ ์•Š์•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ชจ๋“  ์ œํ’ˆ์˜ API๊ฐ€ ํ™œ์„ฑ๋˜์–ด ์žˆ๋Š” ์ƒํƒœ์ด๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ Google Maps Platform์„ ํ†ตํ•ด ์ž๋™์œผ๋กœ API๊ฐ€ ์ƒ์„ฑ๋˜๋ฉด ๊ตฌ๊ธ€์ด ์ถ”์ฒœํ•˜๋Š” Maps์™€ ๊ด€๋ จ๋œ API 31๊ฐœ๋งŒ ์ž๋™์œผ๋กœ ํ™œ์„ฑํ™” ๋˜์–ด ์žˆ๋Š”๊ฒŒ ๊ธฐ๋ณธ ์ƒํƒœ์ธ๋ฐ, ์‚ฌ์‹ค ์ด ์ •๋„ API ๊ธฐ๋Šฅ๋„ ๋Œ€๋ถ€๋ถ„ ๊ฑฐ์˜ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„๊ฑฐ๋‹ค.

ํ•„์š”ํ•œ API๋งŒ ํ™œ์„ฑํ™” ํ•˜๋ฉด์„œ ์–ด๋–ค ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๋•Œ์— ์–ด๋–ค API๊ฐ€ ํ•„์š”ํ•œ์ง€๋ฅผ ์•Œ์•„๋ณด๋ฉด์„œ ์ง„ํ–‰ํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

์ƒ์„ฑ๋œ ํ‚ค๋ฅผ ๋ˆŒ๋Ÿฌ ์ด๋ฒˆ์—๋Š” ํ•˜๋‹จ์— API ์ œํ•œ์‚ฌํ•ญ ์„น์…˜์œผ๋กœ ๋‚ด๋ ค๊ฐ€์„œ "ํ‚ค ์ œํ•œ"์„ ์„ ํƒํ•ด ์ฃผ๋„๋ก ํ•˜์ž.

Select APIs๋กœ ๋ฉ”๋‰ด๋ฅผ ์—ด์–ด์ค€ ๋’ค ํ•„ํ„ฐ์ฐฝ์— maps๋ผ๊ณ  ์น˜๋ฉด ์ง€๋„ ์‚ฌ์šฉ์„ ์œ„ํ•œ ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ SDK๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ ํ•ด๋‹นํ•˜๋Š” SDK๋ฅผ ์„ ํƒํ•ด ์ฃผ๋ฉด ๋˜๋Š”๋ฐ, ๊ฐ ํ‚ค์— ํ•ด๋‹นํ•˜๋Š” Maps SDK๋ฅผ ์„ ํƒํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
ํ•˜๋‚˜์˜ ํ‚ค๋กœ ์‚ฌ์šฉ์„ ํ•œ๋‹ค๋ฉด, Android, iOS๋ฅผ ๋ชจ๋‘ ์„ ํƒํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

์ €์žฅ์„ ๊ผญ ๋ˆŒ๋Ÿฌ์ฃผ์ž. ์ด์ œ Flutter์—์„œ Maps SDK๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋ณธ ์„ธํŒ…์€ ์™„๋ฃŒ๊ฐ€ ๋˜์—ˆ๋‹ค.

์ฐธ๊ณ ๋กœ API ํ‚ค ๊ด€๋ จํ•ด์„œ ํ—ท๊ฐˆ๋ ค ํ•˜์‹œ๋Š” ๋ถ„๋“ค์ด ์žˆ๋Š”๋ฐ, ํ•ด๋‹น ํ‚ค์˜ ๋ณดํ˜ธ์กฐ์น˜์™€ Google Platform์˜ API ์ œํ•œ์€ ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐœ๋…์ด๋‹ค.
ํ‚ค๋ฅผ ๋ณดํ˜ธ ํ•˜๋Š”๊ฑด ํƒ€ ์•ฑ ๋˜๋Š” ํƒ€ ์„œ๋น„์Šค์—์„œ์˜ ์ ‘๊ทผ์„ ์ œํ•œ ํ•˜๊ณ ์ž ํ•˜๋Š” ๋ณดํ˜ธ ๋ชฉ์ ์ด๊ณ , Google Platform์˜ API๋ฅผ ์ œํ•œ ํ•˜๋Š”๊ฑด ํ•ด๋‹น ํ‚ค๋กœ ์–ด๋–ค ์„œ๋น„์Šค๊ฐ€ ๊นŒ์ง€ ํ™œ์„ฑํ™” ํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์—, Maps API๋งŒ ์ „์šฉ์œผ๋กœ ํ‚ค๋ฅผ ์‚ฌ์šฉ ํ•˜์—ฌ๋„ ๋˜๊ณ  ๋‹ค๋ฅธ Google์˜ ์„œ๋น„์Šค๋ฅผ ํ•˜๋‚˜์˜ ํ‚ค๋กœ๋งŒ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ•ด์„œ ์‚ฌ์šฉํ•ด๋„ ๋œ๋‹ค. ์ด๊ฑด ๊ด€๋ฆฌ์˜ ์˜์—ญ์ด๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋น„์Šค, ํŒ€ ์„ฑํ–ฅ์— ๋งž๊ฒŒ ๊ด€๋ฆฌ ๊ธฐ์ค€์„ ์ ์šฉํ•ด ํšจ์œจ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

Flutter Map Integration

google_maps_flutter | Flutter Packages

GCP์—์„œ Google Maps ํ™œ์„ฑํ™”๋ฅผ ์™„๋ฃŒ ํ–ˆ์œผ๋‹ˆ, ์ด์ œ Flutter์—์„œ ๊ตฌ๊ธ€ ์ง€๋„๋ฅผ ๋„์šฐ๋„๋ก ํ•ด๋ณด์ž.

How to Set Up google_maps_flutter in Flutter

Google Maps SDK๋ฅผ Flutter์—์„œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜๋„๋ก ๋„์™€์ฃผ๋Š” ํŒจํ‚ค์ง€๋ฅผ ๋จผ์ € ์ถ”๊ฐ€ํ•ด์ฃผ์ž.

dependencies

- ์ด ๊ธ€์—์„œ๋Š” 2.10.1 ๋ฒ„์ „์„ ๊ธฐ์ค€์œผ๋กœ ์ž‘์„ฑ๋จ

dependencies:
	google_maps_flutter: <latest_version>

์ด์ œ Google Map์„ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ ๋’ค ์•ฑ์„ ์‹คํ–‰ํ•ด๋ณด์ž.

GoogleMap(
	initialCameraPosition: CameraPosition(
		zoom: 6,
		target: LatLng(37.5642135, 127.0016985),
	),
),

Android, iOS ๋ชจ๋‘ ์—๋Ÿฌ๊ฐ€ ๋‚˜๋ฉด์„œ ์ง€๋„๊ฐ€ ๋…ธ์ถœ๋˜์ง€ ์•Š์•˜์„ ๊ฒƒ์ด๋‹ค. ์ด์œ ๋Š” GCP์—์„œ ์ƒ์„ฑํ•œ API ํ‚ค๋ฅผ ๋“ฑ๋กํ•ด ์ฃผ์ง€ ์•Š์•„์„œ์ด๋‹ค.

iOS

Xcode๋ฅผ ์—ด๊ณ  AppDelegate.swift ํŒŒ์ผ๋กœ ์ด๋™ํ•˜์—ฌ GoogleMaps๋ฅผ ์ž„ํฌํŠธํ•œ ๋’ค ๋„ค์ดํ‹ฐ๋ธŒ์˜ GMSService์— API ํ‚ค๋ฅผ ์ œ๊ณตํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

GMSServices.provideAPIKey("your_api_key")

AppDelegate ์•ˆ์— ์ถ”๊ฐ€ํ•ด์ฃผ๋„๋ก ํ•˜์ž.

import Flutter
import UIKit
import GoogleMaps

@main
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
  
    GMSServices.provideAPIKey("AIzaSyDFYC6Nfif0GgcvPqalITn_twppssvSu3I")

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Android

Android ํด๋”์˜ app > src > main์˜ AndroidManifest.xml ํŒŒ์ผ์„ ์—ด์–ด์ค€ ๋’ค application ํƒœ๊ทธ ์•„๋ž˜์— geo.API_KEY ๊ด€๋ จ meta-data๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด ๋œ๋‹ค.

<meta-data android:name="com.google.android.geo.API_KEY"
           android:value="your_api_key"/>

ํ•ด๋‹น ํƒœ๊ทธ๋“ค ์‚ฌ์ด์— ์ •ํ™•ํžˆ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application
        android:label="map_playground"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
      
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:taskAffinity=""
            android:theme="@style/LaunchTheme"
			...
        </activity>
      
        <meta-data android:name="com.google.android.geo.API_KEY"
        android:value="AIzaSyDRQ763aWsmva0QMQ-___VNeQvjwcm__b8"/>
		...
    </application>
    <queries>
        <intent>
            <action android:name="android.intent.action.PROCESS_TEXT"/>
            <data android:mimeType="text/plain"/>
        </intent>
    </queries>
</manifest>

์ด์ œ ๋‹ค์‹œ ์‹คํ–‰ํ•ด๋ณด๋ฉด, Android, iOS ๋ชจ๋‘ ์ •์ƒ์ ์œผ๋กœ ๊ตฌ๊ธ€ ์ง€๋„๊ฐ€ ๋…ธ์ถœ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒ์„ฑํ•œ API ํ‚ค๋Š” APIํ‚ค์— ์ ‘์†ํ•ด ๋ณด๋ฉด "ํ‚ค ํ‘œ์‹œ" ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‹ค์‹œ ์•ฑ์„ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์ •ํ™•ํžˆ ์„œ์šธ์„ ๊ธฐ์ค€์œผ๋กœ Google ์ง€๋„๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์˜คํ”ˆ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค !

iOS Android

Overview of Core Features

๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ์„ ์œ„ํ•œ ์„ค์ •์€ ์™„๋ฃŒ ๋˜์—ˆ์œผ๋‹ˆ, google_maps_flutter ํŒจํ‚ค์ง€์˜ ์—ฌ๋Ÿฌ ๊ธฐ๋Šฅ๋“ค์— ๋Œ€ํ•ด์„œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜์ž.

๐Ÿ”ฅ google_maps_flutter ๋ฒ„์ „์— ๋”ฐ๋ผ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์˜ ์ฐจ์ด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ !

๊ฐ€์žฅ ๊ธฐ๋ณธ์ธ ์ง€๋„๋ฅผ ๋„์–ด์ฃผ๋Š” ๊ฒƒ ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด์ž.

GoogleMap ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด UI์— ์ง€๋„๋ฅผ ๋…ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉฐ, ํ•„์ˆ˜ ๊ฐ’์œผ๋กœ ์ดˆ๊ธฐ ์นด๋ฉ”๋ผ ํฌ์ง€์…˜์„ ์ง€์ •ํ•ด ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

CameraPosition ๊ฐ์ฒด์— ์œ„๋„/๊ฒฝ๋„๋ฅผ ์ƒ์„ฑํ•ด target์„ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.

GoogleMap(
	initialCameraPosition: CameraPosition(
	target: LatLng(37.5642135, 127.0016985),
	),
),

CameraPosition ๊ฐ์ฒด๋Š” target ์™ธ์—๋„ zoom, tilt(๊ธฐ์šธ๊ธฐ), bearing(๋ฐฉ์œ„๊ฐ) ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ์นด๋ฉ”๋ผ์˜ ์œ„์น˜ ๋ฐ ๋ทฐ๋ฅผ ์กฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

zoom: 6
tilt: 0
bearing: 0
zoom: 6
tilt: 0
bearing: 140
zoom: 6
tilt: 0
bearing: 90
zoom: 20
tilt: 0
bearing: 0
zoom: 20
tilt: 45
bearing: 0
zoom: 20
tilt: 90
bearing: 30

mapType ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€๋„ ์ข…๋ฅ˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค

GoogleMap(
	mapType: MapType.none,
    ...
),

none ํƒ€์ž…์„ ์ œ์™ธํ•˜๊ณ ๋Š” ๊ตฌ๊ธ€์—์„œ ์ œ๊ณตํ•˜๋Š” ์ง€๋„ ํƒ€์ž…์œผ๋กœ ๋งต์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , none ํƒ€์ž…์œผ๋กœ ์ง€์ •์‹œ ์ง€๋„๋ฅผ ๋…ธ์ถœ ์‹œํ‚ค์ง€ ์•Š์œผ๋ฉด์„œ, tileOverlays ํ•„๋“œ์— ์ปค์Šคํ…€ ๋˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ํƒ€์ผ์„ ์ œ๊ณตํ•˜์—ฌ ์›ํ•˜๋Š” ๋ทฐ๋‚˜ UI ๋“ฑ์„ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

normal - ๊ธฐ๋ณธ ์ง€๋„ (๋„๋กœ, ๊ฑด๋ฌผ, ์ง€ํ˜• ๋“ฑ)
satellite - ์œ„์„ฑ ์ด๋ฏธ์ง€ ์ง€๋„ (๊ฑด๋ฌผ ์œค๊ณฝ ์—†๋Š” ์‹ค์‚ฌ ์ด๋ฏธ์ง€)
terrain - ์ง€ํ˜• ์ง€๋„ (์‚ฐ, ์–ธ๋• ๋“ฑ ๊ณ ๋„ ์ •๋ณด ํ‘œ์‹œ)
hybrid - ์œ„์„ฑ ์ง€๋„ + ๋„๋กœ ๋ฐ ์ง€๋ช… ์ •๋ณด
none - ์•„๋ฌด๊ฒƒ๋„ ํ‘œ์‹œํ•˜์ง€ ์•Š์Œ

normal hybrid satellite terrain

์œ„์—์„œ ์‚ดํŽด๋ณธ tileOverlays๋Š” ๊ณต๊ฐ„์œ„์— ์—ฌ๋Ÿฌ ๋ ˆ์ด์–ด๋ฅผ ๊ฒน์ณ์„œ UI์— ์ ์šฉํ•˜๋Š” ๊ธฐ๋Šฅ์ธ๋ฐ, ๊ฒŒ์ž„ ์ง€๋„๋‚˜ ๊ณ ๋Œ€ ์ง€๋„์™€ ๊ฐ™์€ ๋‚˜๋งŒ์˜ ์ง€๋„๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜๋„ ์žˆ๊ณ  ๊ธฐ๋ณธ ์ง€๋„์œ„์— ๋‚ ์”จ๋‚˜ ํ–‰์ • ๊ตฌ์—ญ๋“ฑ์˜ ์ •๋ณด๋ฅผ ํ‘œํ˜„ํ•  ๋•Œ์—๋„ ๋ ˆ์ด์–ด๋ฅผ ๊ฒน์น˜๊ฒŒ ํ•ด์„œ ๋ณด์—ฌ์ค„ ์ˆ˜๋„ ์žˆ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค. ์ฆ‰ ๋ฐ‘์—์„œ ๋‹ค๋ค„๋ณผ style ํ•„๋“œ์ฒ˜๋Ÿผ UI ์ ์ธ ์š”์†Œ ์™ธ์—๋„ ์ถ”๊ฐ€์ ์ธ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๊ณ ์ž ํ•  ๋•Œ์—๋„ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

์ง€๋„์˜ ์Šคํƒ€์ผ์„ ๋งŒ๋“œ๋Š” ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ style ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›ํ•˜๋Š” UI ์Šคํƒ€์ผ์„ JSON ํƒ€์ž…์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

์•„๋ž˜ ์˜ˆ์‹œ๋Š” ๊ฐ„๋‹จํ•˜๊ฒŒ POI(Point of Interest)๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ์Šคํƒ€์ผ์ด๋‹ค.

GoogleMap(
	...
	style: '''
  [
    {
      "featureType": "poi",
      "elementType": "labels",
      "stylers": [
        { "visibility": "off" }
      ]
    }
  ]
  ''',
      ),

JSON์—๋Š” featureType, elementType, stylers๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ UI์— ์ปค์Šคํ…€ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜๊ฒŒ ํ•˜๋Š”๋ฐ, ๊ตฌ๊ธ€์—์„œ ์ œ๊ณตํ•˜๋Š” ์Šคํƒ€์ผ ๊ฐ€์ด๋“œ๋ฅผ ํ†ตํ•ด ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ  ๊ณต์‹ ํˆด์„ ํ†ตํ•ด ์ปค์Šคํ…€ ๋ฐ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Styling Wizard: Google Maps

๊ฐ„ํŽธํ•˜๊ฒŒ ๋„๋กœ ์ƒ‰์ƒ, poi, ๋ฌผ ์ƒ‰์ƒ, ๊ณต์› ์ˆจ๊ธฐ๊ธฐ, ์ง€ํ˜• ๊ฐ•์กฐ ๋“ฑ์˜ UI๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

style ๊ด€๋ จ๋œ ๋ถ€๋ถ„์€ ๋‹ค๋ฅธ ๊ธ€์—์„œ ๋” ์ž์„ธํžˆ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

๊ตฌ๊ธ€ ์ง€๋„์— ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” UI ์š”์†Œ๋“ค์— ๋Œ€ํ•ด์„œ๋„ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.

  • myLocationButtonEnabled: ํ˜„์žฌ ์œ„์น˜๋กœ ์ด๋™ํ•˜๋Š” ๋ฒ„ํŠผ UI ํ‘œ์‹œ ์—ฌ๋ถ€
  • zoomControlsEnabled: +/- ์คŒ ๋ฒ„ํŠผ UI ํ‘œ์‹œ
  • compassEnabled: ๋‚˜์นจ๋ฐ˜ UI ํ‘œ์‹œ ์—ฌ๋ถ€ (์ง€๋„ ํšŒ์ „์‹œ ๋ถ์ชฝ ์ •๋ ฌ ์•„์ด์ฝ˜)
  • mapToolbarEnabled(only Android): ๋งˆ์ปค ํƒญ ์‹œ Google ์ง€๋„ ์ƒํ˜ธ ์ž‘์šฉ ํˆด๋ฐ”
myLocationButton compass zoomControls mapToolbar

padding ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ง€๋„์˜ ๊ฐ€์žฅ์ž๋ฆฌ ์—ฌ๋ฐฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ธฐ๋ณธ UI ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์ „์ฒด ์˜์—ญ ๋ฒ”์œ„๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ์–ด UI ๊ฒน์นจ์„ ๋ฐฉ์ง€ํ•˜๊ณ  ์‹ถ์„ ๋–„์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ง€๋„์—์„œ ์ œ๊ณต๋˜๋Š” ์ œ์Šค์ณ ๊ธฐ๋Šฅ ์—ญ์‹œ ํ™œ์„ฑํ™”/๋น„ํ™œ์„ฑํ™” ์—ฌ๋ถ€๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • scrollGesturesEnabled: ์ง€๋„ ์Šคํฌ๋กค ๊ฐ€๋Šฅ ์—ฌ๋ถ€
  • zoomGesturesEnabled: ํ•€์น˜ ์คŒ ๊ฐ€๋Šฅ ์—ฌ๋ถ€
  • rotateGesturesEnabled: ํšŒ์ „ ์ œ์Šค์ณ ํ—ˆ์šฉ ์—ฌ๋ถ€
  • tiltGesturesEnabled: ๊ธฐ์šธ์ด๊ธฐ(3D) ํ—ˆ์šฉ ์—ฌ๋ถ€

fortyFiveDegreeImageryEnabled ํ•„๋“œ๋Š” 45๋„ ํ•ญ๊ณต๋ทฐ ํ‘œ์‹œ ์—ฌ๋ถ€์ธ๋ฐ, ์ด๊ฑด WEB ์ „์šฉ์ด๋ผ ๋ชจ๋ฐ”์ผ์—์„œ๋Š” ํ™œ์„ฑํ™”๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๋‹ค.

๊ธฐ๋ณธ์ ์œผ๋กœ 3D๋กœ ๋ณด์ด๋Š” ๊ฑด๋ฌผ์˜ ํ‘œ์‹œ ์—ฌ๋ถ€๋Š” buildingsEnabled ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๊ณ , ์‹ค์‹œ๊ฐ„ ๊ตํ†ต ์ƒํ™ฉ์„ ์ง€๋„์— ์˜ค๋ฒ„๋ ˆ์ด ํ•˜๊ณ  ์‹ถ์„ ๋•Œ๋Š” trafficEnabled๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

indoorViewEnabled๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๊ฑด ๋‚ด๋ถ€ ์ง€๋„ ํ™œ์„ฑํ™” ์—ฌ๋ถ€๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๋ชจ๋“  ๊ฑด๋ฌผ์— ์ง€์›๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๊ณ  ํ™œ์„ฑํ™”์‹œ ๋‚ด๋ถ€ ์ง€๋„๋ฅผ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋Š” ๊ฑด๋ฌผ๋กœ ์ด๋™ ํ•˜๊ฒŒ๋˜๋ฉด ์˜†์— ์ธต ์ˆ˜ ํ‘œ์‹œ๊ฐ€ ๋‚˜์˜ค๋ฉด์„œ ๋‚ด๋ถ€๋ฅผ ์ง€๋„์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

buildingsEnabled indoorViewEnabled trafficEnabled
true false true false true false

Android ์ „์šฉ์œผ๋กœ liteModeEnabled ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, Google Maps์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์ตœ์†Œํ™”๋กœ ํ•˜๋ฉด์„œ ์ง€๋„๋ฅผ ์ตœ๋Œ€ํ•œ ๊ฐ€๋ณ๊ฒŒ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ์„ ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

cameraTargetBounds์™€ minMaxZoomPreference๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ง€๋„์˜ ์Šคํฌ๋กค ํ—ˆ์šฉ ๋ฒ”์œ„์™€ ์ตœ์†Œ/์ตœ๋Œ€ ์คŒ ํ•€์น˜๋ฅผ ์กฐ์ ˆํ•  ์ˆ˜ ์žˆ๋‹ค.

CameraTargetBounds ๊ฐ์ฒด์˜ ๋‚จ์„œ์ชฝ(southwest), ๋ถ๋™์ชฝ(northeast) ์ขŒํ‘œ๋ฅผ ์ง€์ •ํ•˜๋ฉด ํ•ด๋‹น ๋ฐ•์Šค ์•ˆ์—์„œ๋งŒ ์Šคํฌ๋กค์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

ํŠน์ • ๊ตญ๊ฐ€๋งŒ์„ ์ œํ•œํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

cameraTargetBounds: CameraTargetBounds(
	LatLngBounds(
		southwest: LatLng(33.0, 124.0),
		northeast: LatLng(39.5, 131.0),
	),
),

MinMaxZoomPreference์— ์ตœ์†Œ์™€ ์ตœ๋Œ€ ์คŒ ๋ ˆ๋ฒจ์„ ์ง€์ •ํ•˜๋ฉด ์‚ฌ์šฉ์ž์˜ ์ง€๋„๋‚ด ํ•€์น˜ ๋ฒ”์œ„๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

minMaxZoomPreference: MinMaxZoomPreference(10, 16),

GoogleMap ์œ„์ ฏ์€ ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ์ž ์ƒํ˜ธ์ž‘์šฉ์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•œ๋‹ค.

๊ทธ์ค‘ onTap๊ณผ onLongPress๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ™”๋ฉด์„ ์งง๊ฒŒ ๋˜๋Š” ๊ธธ๊ฒŒ ํ„ฐ์น˜ํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋ฉฐ, ํ„ฐ์น˜๋œ ์ง€์ ์˜ ์œ„์น˜๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” LatLng ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

LatLng์€ ์ง€๋„์ƒ์˜ ์œ„๋„(latitude)์™€ ๊ฒฝ๋„(longitude)๋ฅผ ํฌํ•จํ•œ ์ขŒํ‘œ ์ •๋ณด๋กœ, ์ดํ›„ ๋งˆ์ปค ์ถ”๊ฐ€, ๊ฒฝ๋กœ ์ƒ์„ฑ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ์— ํ™œ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.

GoogleMap(
	onTap: (LatLng latLng) {
		print("Tapped at: $latLng");
	},
	onLongPress: (LatLng latLng) {
		print("Long pressed at: $latLng");
    ...
},

์ง€๋„ ์ด๋™๊ณผ ๊ด€๋ จ๋œ onCameraMoveStarted, onCameraMove, onCameraIdle์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์ž.

์ง€๋„ ์ด๋™(์Šคํฌ๋กค)์˜ ์‹œ์ž‘ -> ์ด๋™ ์ค‘ -> ์ข…๋ฃŒ๋ผ๋Š” ์—ฐ์†์ ์ธ ํ๋ฆ„์„ ํ˜ธ์ถœํ•ด์ฃผ๋Š” ์ฝœ๋ฐฑ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.

onCameraMoveStarted, onCameraIdel์€ ๊ฐ๊ฐ ์ด๋™(์Šคํฌ๋กค)์ด ๋ฐœ์ƒํ•œ ์‹œ์ž‘๊ณผ ๋ ์ง€์ ์— ํ•œ ๋ฒˆ์”ฉ๋งŒ ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ์ด๋‹ค.
onCameraMove๋Š” ์‹œ์ž‘๊ณผ ๋์˜ ์‚ฌ์ด์ธ ์ด๋™ ์ค‘์ผ ๋•Œ์— ํ˜ธ์ถœ๋˜๋ฉฐ, CameraPosition ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ฐธ๊ณ ๋กœ CameraPosition์—๋Š” bearing, tilt, zoom, target ์ •๋ณด๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.

 GoogleMap(
	onCameraMoveStarted: () {
		print("Camera move started");
	},
	onCameraMove: (CameraPosition position) {
		print("Camera moving to: ${position.target}");
	},
	onCameraIdle: () {
		print("Camera idle");
	},
    ...
),

gestureRecognizers๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ง€๋„์™€ ์ œ์Šค์ณ(ํ„ฐ์น˜) ๊ฐ„์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์›น์„ ์ œ๊ณตํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” webGestureHandling์ด๋ผ๋Š” Web ์ „์šฉ ํ•ธ๋“œ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Practical Map Features in Action

๊ธฐ๋ณธ์ ์ธ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ Google ์ง€๋„ ์‚ฌ์šฉ์„ ์œ„ํ•œ ํ•„๋“œ๋“ค์— ๋Œ€ํ•ด์„œ ์‚ดํŽด ๋ดค์œผ๋‹ˆ, ์ด์ œ ์ง€๋„๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

Getting the Userโ€™s Current Location

์—ฌ๋Ÿฌ ์ง€๋„ ์•ฑ์„ ์‚ฌ์šฉํ•ด ๋ณด๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‚ด ์œ„์น˜๋ฅผ ์ง€๋„์ƒ์— ํ‘œ์‹œํ•ด ์ฃผ๋Š”๋ฐ, ํ˜„์žฌ ์šฐ๋ฆฌ๊ฐ€ ๋„์šด ๊ตฌ๊ธ€ ๋งต ์œ„์—๋Š” ๋‚ด ์œ„์น˜๊ฐ€ ํ‘œ์‹œ๋˜์ง€ ์•Š๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๊ฑด ๋ฐ”๋กœ ์œ„์น˜ ๊ถŒํ•œ์„ ๋ถ€์—ฌ ๋ฐ›์ง€ ๋ชปํ•˜์˜€๊ธฐ ๋•Œ๋ฌธ์— Google Map SDK๊ฐ€ ์ž๋™์œผ๋กœ GPS ์œ„์น˜ ์ •๋ณด๋ฅผ ๋””๋ฐ”์ด์Šค์—์„œ ๋ฐ›์•„์˜ค์ง€ ๋ชปํ•˜๊ณ  ์žˆ์–ด์„œ๋‹ค.

์œ„์น˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” GCP์˜ Geolocation API๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๋ฐ, ๋‹คํ–‰ํžˆ ๋ชจ๋ฐ”์ผ์—์„œ๋Š” ๋‚ด์žฅ GPS๋กœ ์œ„์น˜ ์ •๋ณด๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์–ด ๋น„์šฉ ์—†์ด ๋‚ด ์œ„์น˜๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

Dependencies

๊ถŒํ•œ ๊ด€๋ จ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด permission_handler๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์ž.

permission_handler: <latest_version>

Android

- Android > app > src > main > AndroidMenifest.xml

์œ„์น˜ ๊ถŒํ•œ์„ ์š”์ฒญํ•˜๊ธฐ ์œ„ํ•ด AndroidMenifest ํŒŒ์ผ์— ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION ํผ๋ฏธ์…˜ ํƒœ๊ทธ๋ฅผ ์ถ”๊ฐ€ํ•˜์ž.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <application
     ...
    />
</manifest>

iOS

- iOS > Runner > Info.plist

์œ„์น˜ ๊ถŒํ•œ ๋ฉ”์‹œ์ง€์— ์ ์ ˆํ•œ ๋‚ด์šฉ์„ ์ถ”๊ฐ€ํ•ด์„œ Info.plist ํŒŒ์ผ์— ํ‚ค๋ฅผ ๋“ฑ๋กํ•ด์ฃผ์ž.

<key>NSLocationWhenInUseUsageDescription</key>
<string>your_request_message</string>

- iOS > Podfile

Podfile ํ•˜๋‹จ์— ์•„๋ž˜์™€ ๊ฐ™์ด ์š”์ฒญ ๊ถŒํ•œ์„ ์‚ฌ์šฉํ•˜๋„๋ก PERMISSION_LOCATION_WHENINUSE ๊ถŒํ•œ์„ ๋“ฑ๋กํ•ด ์ฃผ์ž

post_install do |installer|
  installer.pods_project.targets.each do |target|
    flutter_additional_ios_build_settings(target)
        # Permission settings
    target.build_configurations.each do |config|
      config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
        '$(inherited)',
        'PERMISSION_LOCATION_WHENINUSE=1',
      ]
    end
  end
end

ํ•ด๋‹น ๊ธ€์€ Google ์ง€๋„๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธ€์ด๊ธฐ ๋•Œ๋ฌธ์—, ํผ๋ฏธ์…˜์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์ง„ํ–‰ํ•˜์ง€ ์•Š๊ฒ ๋‹ค.

๊ตฌ๊ธ€ ์ง€๋„๋ฅผ ๋…ธ์ถœํ•˜๊ธฐ ์ „์— ์ ์ ˆํ•œ ๋‹จ๊ณ„์—์„œ ๊ถŒํ•œ์„ ์š”์ฒญํ•˜์ž.

await Permission.locationWhenInUse.request();
// status.isGranted (ํ—ˆ์šฉ)

์•ฑ์„ ๋‹ค์‹œ ์‹คํ–‰ํ•ด ๋ณด๋ฉด, ์ •์ƒ์ ์œผ๋กœ ๊ถŒํ•œ์„ ์š”์ฒญํ•˜๊ณ  ๊ตฌ๊ธ€ ์ง€๋„์— ๋‚ด ์œ„์น˜๊ฐ€ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค !

๊ตฌ๊ธ€ ์ง€๋„์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‚ด ์œ„์น˜๋Š” ๊ถŒํ•œ์ด ์žˆ์œผ๋ฉด ์ž๋™์œผ๋กœ Maps SDK๊ฐ€ ์œ„์น˜๋ฅผ ์ง€๋„์— ํ‘œ์‹œํ•ด์ฃผ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ขŒํ‘œ๋ฅผ ํ™•์ธํ•  ์ˆ˜๊ฐ€ ์—†๋‹ค.

๊ตฌ๊ธ€ ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ ๋‚ด ์œ„์น˜ ์ขŒํ‘œ ์ •๋ณด๊ฐ€ ํ•„์š”ํ•˜๊ฑฐ๋‚˜ ๋˜๋Š” ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๋‚ด ์œ„์น˜ ํ‘œ์‹œ UI๊ฐ€ ๋งˆ์Œ์— ๋“ค์ง€ ์•Š๋Š”๋‹ค๋ฉด, ๊ฒฐ๊ตญ ๋‚ด ์œ„์น˜๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค.

Flutter์—์„œ ์œ„์น˜ ์ •๋ณด๋ฅผ ๋‹ค๋ฃจ๋Š” location ํŒจํ‚ค์ง€๋กœ ์ขŒํ‘œ ๊ฐ’์„ ํ™•์ธํ•ด๋ณด์ž.

location: <latest_version>

Location์˜ ๋‚ด์žฅ ํ•จ์ˆ˜์ธ getLocation๋งŒ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด ๋‚ด ํ˜„์žฌ ์œ„์น˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

final Location location = Location();
await location.getLocation();

location ํŒจํ‚ค์ง€ ์™ธ์—๋„ geolocator ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•ด์„œ๋„ ๋‚ด ์œ„์น˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

Controlling the Map View

์ง€๋„๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์‚ฌ์šฉ์ž์˜ ๋ฐ˜์‘์— ๋”ฐ๋ผ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•˜๊ฑฐ๋‚˜ ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•œ๋‹ค. ์ด๋ฒˆ์—๋Š” ๋™์ž‘์„ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋„๋ก, GoogleMapController๋ฅผ ํ™œ์šฉํ•ด ์ง€๋„๋ฅผ ์ œ์–ดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

GoogleMapController๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์ž.

onMapCreated๋Š” GoogleMap์ด ์ฒ˜์Œ ๋ Œ๋”๋ง๋˜๊ณ  ์ดˆ๊ธฐํ™”๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋‹ค. ์ด ์‹œ์ ์— ์ œ๊ณต๋˜๋Š” GoogleMapController ์ธ์Šคํ„ด์Šค๋ฅผ, ์•ž์—์„œ ์„ ์–ธํ•œ ์ปจํŠธ๋กค๋Ÿฌ ๋ณ€์ˆ˜์— ๋„˜๊ฒจ์ฃผ๋ฉด ์ง€๋„๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

GoogleMapController? _controller;

GoogleMap(
	onMapCreated: (GoogleMapController controller) async {
		_controller = controller;
	},
    ...
),

Flutter์— ์กด์žฌํ•˜๋Š” ๋‹ค์–‘ํ•œ ์ปจํŠธ๋กค๋Ÿฌ๋“ค๊ณผ ์ดˆ๊ธฐํ™” ๊ณผ์ •์€ ๊ฑฐ์˜ ์œ ์‚ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์‚ฌ์šฉํ•˜๊ณ  ์ดํ•ดํ•˜๋Š”๋ฐ ์–ด๋ ค์›€์€ ์—†์„ ๊ฒƒ์ด๋ผ ์ƒ๊ฐํ•œ๋‹ค.

MapController๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€๋„๋ฅผ ์ด๋™์‹œ์ผœ ๋ณด์ž.

์ง€๋„๋ฅผ ์ด๋™ ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์—๋Š” ํ•œ ๋ฒˆ์— ์ฆ‰์‹œ ์ด๋™ํ•˜๋Š” ๋ฐฉ์‹๊ณผ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๋ฅผ ์ฃผ๋ฉฐ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ด๋™ํ•˜๋Š” ๋ฐฉ์‹์ด ์žˆ๋‹ค.

์ด๋Š” ๋งˆ์น˜ ScrollController์—์„œ jumpTo์™€ animateTo๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•˜๋‹ค. ์ฆ‰, ์• ๋‹ˆ๋ฉ”์ด์…˜ ์œ ๋ฌด์˜ ์ฐจ์ด์ผ ๋ฟ ๊ธฐ๋Šฅ์€ ๋™์ผํ•˜๋‹ค.

MapController์—์„œ๋Š” moveCamera, animateCamera๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

_controller?.moveCamera(cameraUpdate);
_controller?.animateCamera(cameraUpdate);

๊ณตํ†ต์ ์œผ๋กœ CameraUpdate๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋™ํ•  ์œ„์น˜, ์คŒ ๋ ˆ๋ฒจ ๋˜๋Š” ๋ณด์—ฌ์ค„ ๋ฒ”์œ„(LatLngBounds) ๋“ฑ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

CameraUpdate์˜ newLatLng ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ์›ํ•˜๋Š” ์ขŒํ‘œ๋กœ ์ด๋™ํ•˜๋ฉด์„œ ์คŒ ๋ ˆ๋ฒจ์€ ํ˜„์žฌ ์ˆ˜์ค€์„ ์œ ์ง€ํ•˜๋ฉด์„œ ์ด๋™์‹œํ‚ค๊ณ  ์‹ถ์„ ๋–„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

CameraUpdate.newLatLng(LatLng(latitude, longitude)),

์ด๋ฒˆ์—๋Š” ์ขŒํ‘œ ์ด๋™์€ ํ•˜์ง€ ์•Š๊ณ , ์คŒ ๋ ˆ๋ฒจ๋งŒ ์กฐ์ •ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด zoomBy, zoomTo, zoomIn, zoomOut์„ ํ™œ์šฉํ•˜๋ฉด ๋˜๋Š”๋ฐ, ๊ฐ๊ฐ์˜ ๊ธฐ๋Šฅ์—๋Š” ๋ฏธ๋ฌ˜ํ•œ ์ฐจ์ด๊ฐ€ ์žˆ์œผ๋‹ˆ ์ ์ ˆํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜์…”์•ผ ํ•œ๋‹ค.

๊ธฐ๋ณธ์ ์ธ ๋‚ด์žฅ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ„ํŽธํ•˜๊ฒŒ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด zoomIn, zoomOut์„ ํ˜ธ์ถœํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋Š” ํŒจํ‚ค์ง€ ์ž์ฒด์—์„œ ํŽธ๋ฆฌํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋ผ๊ณ  ๋งŒ๋“ค์–ด ๋†“์€ ํ•จ์ˆ˜์ด๊ณ , ์„ค๋ช…์„ ๋ณด๋ฉด zoomBy(ยฑ1)์™€ ๋™์ผํ•œ ๊ธฐ๋Šฅ์ด๋ผ๊ณ  ์•ˆ๋‚ดํ•˜๊ณ  ์žˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด zoomBy๋Š” ์–ด๋–ค ๊ธฐ๋Šฅ์ธ๊ฑธ๊นŒ? ๋ฐ”๋กœ zoom์„ ์ƒ๋Œ€์ ์œผ๋กœ ์ฆ๊ฐ€ ๋˜๋А ๊ฐ์†Œ์‹œํ‚ค๋Š” ๊ฒƒ์œผ๋กœ ํ˜„์žฌ ์คŒ ๋ ˆ๋ฒจ์— ๋น„๋ก€ํ•ด์„œ ์ž‘๋™ํ•˜๊ฒŒ ๋œ๋‹ค.
zoomBy(3) ์ด๋ผ๋ฉด ํ˜„์žฌ ์คŒ ๋ ˆ๋ฒจ์— 3๋งŒํผ ํ™•๋Œ€๋œ๋‹ค๋Š” ์˜๋ฏธ์ด๋‹ค. ๋ฐ˜๋Œ€๋กœ zoomBy(-1)์„ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด ํ˜„์žฌ ์คŒ ๋ ˆ๋ฒจ์—์„œ -1๋งŒํผ๋งŒ ์ถ•์†Œ๋œ๋‹ค๋Š” ์˜๋ฏธ๊ฐ€ ๋œ๋‹ค.

๊ฒฐ๊ตญ zoomIn, zoomOut์€ ํ˜„์žฌ ์คŒ์—์„œ ยฑ1๋งŒํผ ํ™•๋Œ€/์ถ•์†Œํ•˜๋Š” ๋‚ด์žฅ ํ•จ์ˆ˜์ธ ๊ฒƒ์ด๋‹ค.

zoomTo๋Š” ์ ˆ๋Œ€์  ์คŒ ๋ ˆ๋ฒจ์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ, ํ˜„์žฌ ์คŒ ๋ ˆ๋ฒจ๊ณผ ์ƒ๊ด€์—†์ด ํŠน์ • ์คŒ ๋ ˆ๋ฒจ๋กœ ๋ฐ”๋กœ ํ™•๋Œ€/์ถ•์†Œ๋œ๋‹ค.

In & Out zoomBy(+2/-4) zoomTo(+20/-2)
jumpTo animateTo jumpTo animateTo jumpTo animateTo

- zoomIn & zoomOut

controller?.moveCamera(CameraUpdate.zoomIn());
controller?.animateCamera(CameraUpdate.zoomIn());
controller?.moveCamera(CameraUpdate.zoomOut());
controller?.animateCamera(CameraUpdate.zoomOut());

- zoomBy(by: number)

controller?.moveCamera(CameraUpdate.zoomBy(by));
controller?.animateCamera(CameraUpdate.zoomBy(by));

- zoomTo(to: number)

controller?.moveCamera(CameraUpdate.zoomTo(to));
controller?.animateCamera(CameraUpdate.zoomTo(to));

์คŒ ๋ ˆ๋ฒจ์„ ์œ ์ง€ํ•˜๋ฉด์„œ ํฌ์ง€์…˜์„ ์ด๋™์‹œํ‚ฌ ๋•Œ์— CameraUpdate.newLatLng(position: LatLng) ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค๊ณ  ํ–ˆ๋Š”๋ฐ, ๊ทธ๋ ‡๋‹ค๋ฉด ์คŒ ๋ ˆ๋ฒจ์„ ์ง€์ •ํ•˜๋ฉด์„œ ํฌ์ง€์…˜์„ ์ด๋™์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด ์–ด๋–ค ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„๊นŒ ?

CameraUpdate.newLatLngZoom ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์คŒ ๋ ˆ๋ฒจ๊ณผ ํฌ์ง€์…˜์„ ๋‘˜ ๋‹ค ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ ํฌ์ง€์…˜๊ณผ ์คŒ ๋ชจ๋‘ ํ•„์ˆ˜์ ์œผ๋กœ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค.

_controller?.moveCamera(CameraUpdate.newLatLngZoom(position, zoom));
_controller?.animateCamera(CameraUpdate.newLatLngZoom(position, zoom));

์ถ”๊ฐ€๋กœ newCameraPosition์ด๋ผ๋Š” ํ•จ์ˆ˜๊ฐ€ ์กด์žฌํ•˜๋Š”๋ฐ, ์–ผํ•๋ณด๋ฉด newLatLngZoom๊ณผ ๋น„์Šทํ•ด ๋ณด์ด์ง€๋งŒ, ํฌ์ง€์…˜์˜ ์ขŒํ‘œ ๊ฐ’์„ ์ „๋‹ฌํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ CameraPosition ์ž์ฒด๋ฅผ ์ƒˆ๋กญ๊ฒŒ ์ƒ์„ฑํ•ด์„œ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. ์ฆ‰, ์ดˆ๊ธฐ GoogleMap ๊ฐ์ฒด์— ์ง€์ •ํ•œ initialCameraPosition ํ•„๋“œ์˜ ๊ฐ’์„ ์ƒˆ๋กญ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ bearing, tilt, zoom, target์„ ๋ชจ๋‘ ์ปจํŠธ๋กค์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

CameraUpdate.newCameraPosition(CameraPosition(
   target: position,
   zoom: zoom,
   bearing: bearing,
   tilt: tilt
   ),
);

ํฌ์ง€์…˜๊ณผ ์คŒ ๋ ˆ๋ฒจ์„ ์ปจํŠธ๋กคํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ํŠน์ • ์•ฑ๋“ค์˜ ๊ฒฝ์šฐ ๋‹จ์ˆœํ•œ ํฌ์ง€์…˜, ์คŒ์„ ์ปจํŠธ๋กค ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ์•„๋‹Œ ์ •ํ™•ํ•œ ๋ฒ”์œ„ ๋‚ด์— ๋ทฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ์ด๋Ÿฐ ๊ธฐ๋Šฅ์€ newLatLngBounds ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด ๋‚จ์„œ์ชฝ, ๋ถ๋™์ชฝ ์ขŒํ‘œ์˜ ๋ฒ”์œ„ ๋‚ด์— ์ž๋™์œผ๋กœ ํฌ์ง€์…˜์„ ์ด๋™์‹œ์ผœ ์ •ํ™•ํ•œ ๋ฒ”์œ„๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

final LatLngBounds bounds = LatLngBounds(
	southwest: const LatLng(34.0, 126.0),
	northeast: const LatLng(38.0, 130.0),
);

controller?.moveCamera(CameraUpdate.newLatLngBounds(bounds, zoom));
controller?.animateCamera(CameraUpdate.newLatLngBounds(bounds, zoom));
LatLng LatLngZoom
jumpTo animateTo jumpTo animateTo

LatLngBounds newCameraPosition
jumpTo animateTo jumpTo animateTo

์ง€๊ธˆ๊นŒ์ง€๋Š” ํฌ์ง€์…˜์„ ์ด๋™์‹œํ‚ฌ ๋•Œ์— ์ง€๋„์ƒ ์ขŒํ‘œ๋ฅผ ์‚ฌ์šฉํ•ด ์ง€๋„๋ฅผ ์›€์ง์ด๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ดค๋Š”๋ฐ, ๋””๋ฐ”์ด์Šค์˜ ๋ฌผ๋ฆฌ์  ์œ„์น˜๋กœ ์ง€๋„๋ฅผ ์ด๋™์‹œ์ผœ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๋„ ๋ฐœ์ƒํ•œ๋‹ค.

๋‹คํ–‰ํžˆ CameraUpdate์— scrollBy ํ•จ์ˆ˜๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ๋ฌผ๋ฆฌ์  ์œ„์น˜๋กœ์˜ ์ด๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

scrollBy๋ฅผ ์‚ฌ์šฉํ•ด x์ถ•, y์ถ•์˜ ์ด๋™ํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•˜๋ฉด ๋˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ x์ถ•๊ณผ y์ถ•์€ ํ˜„์žฌ ์œ„์น˜์—์„œ ์–ผ๋งŒํผ์˜ ํ”ฝ์…€์„ ์›€์ง์ผ์ง€๋ฅผ ๊ฒฐ์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

๋””๋ฐ”์ด์Šค์˜ ๊ฐ€๋กœ์‚ฌ์ด์ฆˆ๊ฐ€ 400์ธ ๊ฒฝ์šฐ x์— 200์„ ๋„ฃ์–ด์ฃผ๊ฒŒ ๋˜๋ฉด, ์ •ํ™•ํžˆ ํ˜„์žฌ ๋ณด๊ณ  ์žˆ๋Š” ๋ฌผ๋ฆฌ์  ๋ทฐ์˜ 200 ํ”ฝ์…€๋งŒํผ ์›€์ง์ด๊ฒŒ ๋œ๋‹ค.

controller?.moveCamera(CameraUpdate.scrollBy(x, y));
controller?.animateCamera(CameraUpdate.scrollBy(x, y));
jumpTo animateTo

MapController์˜ moveCamera, animateCamera๋กœ ํฌ์ง€์…˜์„ ์ด๋™ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ๋ชจ๋‘ ์•Œ์•„๋ดค๋‹ค. ์ƒ๊ฐ๋ณด๋‹ค ์‰ฝ๊ฒŒ ์ง€๋„๋ฅผ ์ปจํŠธ๋กคํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค !

์ถ”๊ฐ€๋กœ MapController์—๋Š” ์–ด๋–ค ๊ธฐ๋Šฅ๋“ค์ด ์žˆ๋Š”์ง€ ๊ณ„์†ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

MapController๋Š” GoogleMap์— ์—ฐ๊ฒฐ๋œ ์ปจํŠธ๋กค๋Ÿฌ๊ธฐ ๋•Œ๋ฌธ์— ๋‹น์—ฐํžˆ ๋‹ค์–‘ํ•œ ์ •๋ณด๋“ค๋„ ์ œ๊ณต ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

๋Œ€ํ‘œ์ ์œผ๋กœ getLatLng, getScreenCoordinate, getVisibleResion์— ๋Œ€ํ•ด์„œ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.
์œ„ ํ•จ์ˆ˜๋Š” ๋ชจ๋‘ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ž‘๋™๋˜๋ฉฐ, ์šฐ๋ฆฌ๊ฐ€ ์•ž์„œ ๋‹ค๋ค˜๋˜ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”์„œ๋“œ๋“ค๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ง€๋„์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

getLatLng์—๋Š” ํ™”๋ฉด ์ขŒํ‘œ๊ณ„(ScreenCoordinate)๋ฅผ ์ง€๋„ ์ขŒํ‘œ๊ณ„(LatLng)๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์œผ๋กœ ๋””๋ฐ”์ด์Šค์˜ ๋ฌผ๋ฆฌ์  x, y์œ„์น˜๊ฐ€ ์ง€๋„์ƒ์˜ ์–ด๋А ์œ„๋„/๊ฒฝ๋„์ธ์ง€๋ฅผ ์•Œ๋ ค์ค€๋‹ค.

getScreenCoordinate๋Š” getLatLng์™€๋Š” ๋ฐ˜๋Œ€๋กœ ์œ„๋„/๊ฒฝ๋„ ์ขŒํ‘œ๋ฅผ ๋””๋ฐ”์ด์Šค์˜ ๋ฌผ๋ฆฌ์  ํ”ฝ์…€๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋งˆ์ปค๋‚˜ ์ปค์Šคํ…€ ์œ„์ ฏ์„ ์ •ํ™•ํ•œ ํ™”๋ฉด ์œ„์น˜์— Overlay ํ•˜๊ณ  ์‹ถ์„ ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

getVisibleResion์€ ํ˜„์žฌ ๋””๋ฐ”์ด์Šค ํ™”๋ฉด์—์„œ ๋ณด์—ฌ์ง€๊ณ  ์žˆ๋Š” ์ง€๋„์˜ ๋‚จ์„œ/๋ถ๋™ ์ขŒํ‘œ ์ •๋ณด์ธ LatLngBounds ํ˜•ํƒœ๋กœ ์‹ค์ œ ๋ Œ๋”๋ง ๋˜์–ด์ง€๊ณ  ์žˆ๋Š” ์ง€๋„ ์ขŒํ‘œ๋ฅผ ์•Œ๋ ค์ฃผ๋ฏ€๋กœ, ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์ด๋‚˜ UI ์ œ์–ด ๋“ฑ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•œ ์‹œ์ ์— ํ™œ์šฉํ•˜๊ธฐ์— ์œ ์šฉํ•œ ๊ธฐ๋Šฅ์ด๋‹ค.

await controller?.getLatLng(ScreenCoordinate(x: x, y: y))
await controller?.getScreenCoordinate(LatLng(latitude, longitude))
await controller?.getVisibleRegion();

์ง€๋„์˜ ์คŒ ๋ ˆ๋ฒจ์„ ์•Œ๊ณ  ์‹ถ๋‹ค๋ฉด, getZoomLevel์„ ํ˜ธ์ถœํ•˜์—ฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

await controller?.getZoomLevel();

์ปค์Šคํ…€ ํƒ€์ผ๋กœ ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ํƒ€์ผ์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์บ์‹ฑ์„ ์ง€์›Œ์ฃผ์–ด์•ผ ๋ณ€๊ฒฝ๋œ ํƒ€์ผ์„ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, clearTileCache(tileOverlayId)๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ด์ „ ํƒ€์ผ์„ ํด๋ฆฌ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งˆ์ปค๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ๋“ค๋„ ์ œ๊ณตํ•˜๋Š”๋ฐ, ์•„์ง ๋งˆ์ปค์— ๋Œ€ํ•ด์„œ ์ œ๋Œ€๋กœ ๋ฐฐ์šฐ์ง€ ์•Š์•˜์œผ๋‹ˆ ์ด ๋ถ€๋ถ„์€ ๋งˆ์ปค ์‚ฌ์šฉ๋ฒ•์„ ์•Œ์•„๋ณด๋ฉด์„œ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜์ž.

Placing Markers Where You Want

๋‹จ์ˆœํžˆ ์ง€๋„๋งŒ ๋ณด์—ฌ์ฃผ๋Š” ์„œ๋น„์Šค๋ผ๋ฉด ์ง€๊ธˆ๊นŒ์ง€ ์•Œ์•„๋ณธ ์‚ฌ์šฉ๋ฒ•์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ์„œ๋น„์Šค๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์šด์˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.
ํ•˜์ง€๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ์œ„์น˜์— Marker(๋งˆ์ปค)๋ฅผ ๋ฐฐ์น˜ํ•˜๊ณ  ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜๊ณ ์ž ํ•œ๋‹ค๋ฉด, ๋งˆ์ปค๋ฅผ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•˜๊ณ  ์ œ์–ดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„์•ผ ํ•œ๋‹ค.

์œ„์˜ ์ง€๋„์— ํ‘œ์‹œ๋œ ๊ฒƒ์ด ๋ฐ”๋กœ Google Map์—์„œ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณตํ•ด์ฃผ๋Š” ๋งˆ์ปค์ด๋‹ค. ์ผ๋ถ€ UI ์ˆ˜์ •์€ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๊ธฐ๋ณธ UI๊ฐ€ ๋งˆ์Œ์— ๋“ค์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋‹น์—ฐํžˆ ์ปค์Šคํ…€๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

๋ณธ๊ฒฉ์ ์œผ๋กœ Marker(๋งˆ์ปค)๋ฅผ ์ง€๋„ ๊ณต๊ฐ„์œ„์— ๋…ธ์ถœ์‹œํ‚ค๊ณ  ์ œ์–ดํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž.

GoogleMap ํ•„๋“œ๋ฅผ ๋ณด๋ฉด markers๊ฐ€ ์žˆ๋Š”๋ฐ, ํƒ€์ž…์„ ๋ณด๋ฉด Set๋กœ ๋˜์–ด ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

์™œ List๊ฐ€ ์•„๋‹Œ Set์ผ๊นŒ ? ๋งˆ์ปค๋Š” ๋ฐ˜๋“œ์‹œ ๊ณ ์œ ํ•œ markerId๋ฅผ ๊ฐ€์ ธ์•ผ ํ•˜๋Š”๋ฐ, List์˜€๋‹ค๋ฉด ์‹ค์ˆ˜๋กœ ์ค‘๋ณต๋œ ID๋กœ ๋งˆ์ปค๋ฅผ ์ถ”๊ฐ€ํ•ด๋„ ๋ง‰์„ ๋ฐฉ๋ฒ•์ด ์—†๊ณ , List์— ๋น„ํ•ด Set์€ contains, add, remove ๋“ฑ์˜ ์—ฐ์‚ฐ์ด ๋น ๋ฅด๋‹ค๊ณ  ์•Œ๋ ค์ ธ ์žˆ๋‹ค.

GoogleMap(
	markers: {
    	...markers
    }
)

Marker๋Š” ํ•„์ˆ˜๋กœ ๊ณ ์œ ํ•œ ID์ธ markerId์™€ ์ง€๋„ ์ขŒํ‘œ๊ณ„์ธ position์„ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค.

Marker(
	markerId: MarkerId(uniqueId),
    position: LatLng(latitude, longitude)
)

๋งŒ์ผ ์ค‘๋ณต๋œ ID๋กœ markerId๊ฐ€ ์—ฌ๋Ÿฌ๊ฐœ ํฌํ•จ๋  ๊ฒฝ์šฐ Set ํƒ€์ž…์— ์˜ํ•ด ๋Šฆ๊ฒŒ ๋“ค์–ด์˜จ id๊ฐ€ ์•ž์„œ ์ƒ์„ฑ๋œ id๋ฅผ ๋ฎ์–ด์“ฐ๊ธฐ ๋•Œ๋ฌธ์—, ๊ฒฐ๊ตญ ๊ฐ€์žฅ ๋งˆ์ง€๋ง‰์œผ๋กœ ์„ ์–ธ๋œ markerId๋งŒ ๋‚จ๊ฒŒ ๋œ๋‹ค.

Marker๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋งˆ์ปค๋ฅผ ํƒญํ•ด๋ณด๋ฉด, ์ž๋™์œผ๋กœ ํฌ์ง€์…˜์ด ์ด๋™ํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ ์ด๊ฑด ๋‚ด์žฅ๋œ ๊ธฐ๋Šฅ์œผ๋กœ ์˜ต์…˜์„ ํ†ตํ•ด ์ œ์–ด๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

infoWindow ํ•„๋“œ์— InfoWindow๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งˆ์ปค๊ฐ€ ํƒญํ–ˆ์„ ๋•Œ์—, ๋ณด์—ฌ์ฃผ๋Š” ์ •๋ณด์ฐฝ์„ ๋…ธ์ถœ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

title, snippet ํ•„๋“œ์— ํƒญ์˜ ๋ช…์นญ๊ณผ ์งง์€ ์„ค๋ช…์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๊ณ , anchor๋กœ ์˜คํ”„์…‹ ์กฐ์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋˜ํ•œ ๋งˆ์ปค๊ฐ€ ํƒญํ–ˆ์„ ๋•Œ์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 infoWindow: InfoWindow(
	title: "NYC",
	snippet: "New York City",
	anchor: const Offset(0.5, 0),
	onTap: () {},
)

๋งˆ์ปค์˜ ๊ธฐ๋ณธ ์•„์ด์ฝ˜์€ ๋‹จ์ˆœํ•œ ๋นจ๊ฐ„์ƒ‰์ด์ง€๋งŒ, ๊ฐ„๋‹จํ•˜๊ฒŒ ์ƒ‰์ƒ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.
BitmapDescriptor์˜ defaultMarkerWithHue(hue) ๋ฉ”์„œ๋“œ์— ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” ์ƒ‰์กฐ(Hue)๋ฅผ ๋„˜๊ฒจ์ฃผ๋ฉด ๋œ๋‹ค.

์—ฌ๊ธฐ์„œ Hue๋Š” ์ƒ‰์กฐ๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ์ƒ‰์ƒํ™˜(Color Wheel) ์ƒ์—์„œ์˜ ๊ฐ๋„(0ยฐ~360ยฐ)๋กœ ํ‘œํ˜„๋œ๋‹ค.

๋ฏธ๋ฆฌ ์ง€์ •๋œ hueRed(0.0), hueOrange(30.0), hueYellow(60.0), hueGreen(120.0), hueCyan(180.0), hueBlue(240.0), hueViolet(270.0), hueMagenta(300.0), hueRose(330.0)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ๋„ ๋˜๊ณ , ์ˆซ์ž๋ฅผ ์ง์ ‘ ๋„ฃ์–ด์ฃผ๋ฉด ์ƒ‰์ƒํ™˜์— ๋งž๋Š” ์ƒ‰์กฐ ๊ฐ’์œผ๋กœ ๋ณ€ํ•˜๊ฒŒ ๋œ๋‹ค.

icon: BitmapDescriptor.defaultMarkerWithHue(
	BitmapDescriptor.hueAzure,
),
icon: BitmapDescriptor.defaultMarkerWithHue(35),

ratation์€ ๋งˆ์ปค๋ฅผ ํšŒ์ „์‹œํ‚ค๋Š” ๊ฐ๋„์ด๊ณ , alpha๋Š” ํˆฌ๋ช…๋„๋ฅผ ์กฐ์ •ํ•˜๊ฒŒ ๋œ๋‹ค. flat์€ ๋ˆ•๋Š” ํšจ๊ณผ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ, ์นด๋ฉ”๋ผ ๊ฐ๋„์™€ rotation์„ ์„ค์ •ํ•˜์—ฌ ์ง€๋„ ๋ฐฉํ–ฅ์— ๋งž๊ฒŒ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.

๋งˆ์ปค์—์„œ๋„ infoWindow์™€ ๋™์ผํ•˜๊ฒŒ anchor๋กœ ์˜คํ”„์…‹ ์กฐ์ •์„ ํ•˜์—ฌ ์„ธ๋ฐ€ํ•˜๊ฒŒ ์œ„์น˜๋ฅผ ์กฐ์ • ํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

zIndex๋Š” double ํƒ€์ž…์œผ๋กœ ์ง€๋„ ์œ„์— ์—ฌ๋Ÿฌ ๋งˆ์ปค๊ฐ€ ์˜ค๋ฒ„๋ ˆ์ด ๋˜๋Š” ๊ฒฝ์šฐ ์–ด๋–ค ๋งˆ์ปค๋ฅผ ๋” ์œ„์— ๋ณด์ด๊ฒŒ ํ• ์ง€๋ฅผ ๊ฒฐ์ •ํ•˜๋Š” ๊ฐ’์ด๋‹ค. ํŠน์ • ๋งˆ์ปค๋“ค์„ ๋†’ํžˆ๊ณ  ์‹ถ๊ฑฐ๋‚˜ ๊ทœ์น™์— ์˜ํ•ด ๋” ์œ„์— ๋ณด์ด๊ฒŒ ํ•  ๋งˆ์ปค๋ฅผ ์ง€์ •ํ•˜๊ณ  ์‹ถ์„ ๋–„ ๊ฐ ๋งˆ์ปค๋งˆ๋‹ค ์ ์ ˆํ•œ ๊ฐ’์„ ์„ธํŒ…ํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

infoWindow icon alpha rotation flat

๋งˆ์ปค์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ์œผ๋กœ๋Š” ํƒญ ํ–ˆ์„ ๋•Œ์˜ onTap, ๊ธธ๊ฒŒ ํƒญํ•˜๋ฉด ๋“œ๋ž˜๊ทธ ๊ธฐ๋Šฅ์ด ํ™œ์„ฑํ™” ๋˜๋ฉด์„œ onDragStart๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ , ๋“œ๋ž˜๊ทธ ์ค‘์ธ ๊ฒฝ์šฐ onDrag, ํƒญ์„ ๋†“์•˜์„ ๋•Œ์˜ onDragEnd์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๊ฐ€ ์ž‘๋™ํ•œ๋‹ค. ๋“œ๋ž˜๊ทธ์— ๋Œ€ํ•œ ์ฝœ๋ฐฑ์—๋Š” ์ง€๋„ ์ขŒํ‘œ๊ณ„ ์ •๋ณด๊ฐ€ ์ œ๊ณต๋œ๋‹ค.
๋“œ๋ž˜๊ทธ ๊ธฐ๋Šฅ์„ ๋น„ํ™œ์„ฑํ™” ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด draggable๋ฅผ false๋กœ ์„ค์ •ํ•˜๋ฉด ๋งˆ์ปค์— ๋Œ€ํ•œ ๋“œ๋ž˜๊ทธ๊ฐ€ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š”๋‹ค.

์•ž์„œ ๋งˆ์ปค์˜ ๊ธฐ๋ณธ ์•„์ด์ฝ˜์— ๋Œ€ํ•œ ์ƒ‰์ƒ ๋ณ€๊ฒฝ์— ๊ด€ํ•ด์„œ ์•Œ์•„๋ดค๋Š”๋ฐ, ๊ธฐ๋ณธ ์•„์ด์ฝ˜์ด ์•„๋‹Œ ์ปค์Šคํ…€ ์•„์ด์ฝ˜์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ๋„ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

๋งˆ์ปค๋ฅผ ์ปค์Šคํ…€ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋””์ž์ธ๋œ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๊ณ , Flutter์—์„œ ์ƒ์„ฑํ•œ ์œ„์ ฏ์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

asset์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” BitmapDescriptor ํƒ€์ž…์œผ๋กœ ํ”„๋กœ์ ํŠธ ๋‚ด์— ํฌํ•จ๋œ ์ด๋ฏธ์ง€๋ฅผ ๋ถˆ๋Ÿฌ์™€ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๋น„๋™๊ธฐ์ ์œผ๋กœ asset์„ ๋งŒ๋“ค์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ง์ ‘ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ์•ˆ๋˜๊ณ  ์ƒ์„ฑ๋œ BitmapDescriptor๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

BitmapDescriptor? _bikeIcon;

_bikeIcon = await BitmapDescriptor.asset(
	createLocalImageConfiguration(context),
	'assets/images/bike.png',
	width: 64,
	height: 64,
);

Marker(
	markerId: const MarkerId("1"),
	position: const LatLng(40.7128, -74.0059),
	icon: _bikeIcon ?? BitmapDescriptor.defaultMarker,
),

๋„คํŠธ์›Œํฌ ์ด๋ฏธ์ง€๋Š” ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ• ๊นŒ ? ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ ์กฐ๊ธˆ ๋ณต์žกํ•ด์ง€๋Š”๋ฐ, ๋„คํŠธ์›Œํฌ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋ฐ›์•„ ๋ฐ”์ดํŠธ ํ˜•ํƒœ(Uint8List)๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค BitmapDescriptor์˜ asset์ด ์•„๋‹Œ bytes ๋‚ด์žฅ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๋„คํŠธ์›Œํฌ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋ฐ›๊ธฐ ์œ„ํ•ด http ํŒจํ‚ค์ง€๋ฅผ ์ถ”๊ฐ€ํ•ด ์ฃผ์ž.

dependencies:
	http: <latest_version>

๋งˆ์ปค์— ์‚ฌ์šฉํ•  ๋„คํŠธ์›Œํฌ ์ด๋ฏธ์ง€ URL์„ ์ถ”๊ฐ€ํ•˜๊ณ , ๋‹ค์šด๋กœ๋“œ ๋ฐ›์€ ๋’ค Uint8List ํƒ€์ž…์„ BitmapDescriptor๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ฝ”๋“œ์ด๋‹ค.

Future<BitmapDescriptor> getNetworkMarkerIcon() async {
	final http.Response response = await http.get(Uri.parse(imageUrl));
    if (response.statusCode != 200) {
      throw Exception("Failed to load image.");
    }

    final Uint8List imageBytes = response.bodyBytes;

    final ui.Codec codec = await ui.instantiateImageCodec(
      imageBytes,
      targetWidth: 100,
      targetHeight: 100,
    );
    final ui.FrameInfo frameInfo = await codec.getNextFrame();
    final ByteData? byteData =
        await frameInfo.image.toByteData(format: ui.ImageByteFormat.png);
    if (byteData == null) return BitmapDescriptor.defaultMarker;

    return BitmapDescriptor.bytes(byteData.buffer.asUint8List());
  }

Flutter์—์„œ ๋ Œ๋”๋งํ•˜๋Š” ์œ„์ ฏ(Widget)์€ ์–ด๋–ป๊ฒŒ ์ปค์Šคํ…€ ๋งˆ์ปค๋กœ ์‚ฌ์šฉํ• ๊นŒ ?

์ง์ ‘ ๋งŒ๋“  ์œ„์ ฏ์„ Google Maps์˜ ๋งˆ์ปค๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์ƒ๊ฐ๋ณด๋‹ค ๋‹จ์ˆœํ•˜์ง€๋Š” ์•Š๋‹ค. ๋งˆ์ปค๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ BitmapDescriptorํ˜•ํƒœ์˜ ์ด๋ฏธ์ง€๋งŒ ์•„์ด์ฝ˜์œผ๋กœ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์œ„์ ฏ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๋ ค๋ฉด ์ปค์Šคํ…€ ์œ„์ ฏ์„ ์ด๋ฏธ์ง€๋กœ ๋ Œ๋”๋งํ•œ ๋’ค, ๋ฐ”์ดํŠธ ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ•˜๋Š”๋ฐ, ํ™”๋ฉด์„ ์บก์ณํ•  ๋•Œ์— ์‚ฌ์šฉ๋˜๋Š” RepaintBoundary ์œ„์ ฏ์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ์œ„์ ฏ์„ ์ด๋ฏธ์ง€๋กœ ์บก์ฒ˜ํ•˜์—ฌ ๋งˆ์ปค ์•„์ด์ฝ˜์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ๋งํ’์„  ํ˜•ํƒœ, ์‚ฌ์šฉ์ž ํ”„๋กœํ•„, ํ…์ŠคํŠธ๊ฐ€ ํฌํ•จ๋œ ๋‹ค์–‘ํ•œ ๋””์ž์ธ ๋งˆ์ปค๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋จผ์ € ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋Š” UI๋ฅผ ์œ„์ ฏ์œผ๋กœ ๋ถ„๋ฆฌํ•œ ๋’ค, ๊ตฌ๊ธ€ ์ง€๋„๊ฐ€ ๋ Œ๋”๋ง ๋˜๋Š” ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ์˜์—ญ์œผ๋กœ ๋ฐฐ์น˜์‹œ์ผœ ์ฃผ์ž. Stack์„ ์‚ฌ์šฉํ•ด์„œ ๊ตฌ๊ธ€ ์ง€๋„ ์•„๋ž˜์— ๋ฐฐ์น˜์‹œ์ผœ๋„ ๋œ๋‹ค.

final GlobalKey _markerKey = GlobalKey();

Positioned(
	top: -9999,
	left: -9999,
	child: RepaintBoundary(
		key: _markerKey,
        child: const CustomMarkerWidget(),
        ),
	),

RepaintBoundary ์œ„์ ฏ์˜ GlobalKey๋ฅผ ์ „๋‹ฌํ•˜์—ฌ, ๋ Œ๋”๋ง ํ›„ ์œ„์ ฏ์„ ์ด๋ฏธ์ง€๋กœ ์ƒ์„ฑํ•˜์—ฌ Uint8List ํƒ€์ž…์„ BitmapDescriptor๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

์‚ฌ์‹ค ์•ž์„œ ์‚ดํŽด๋ณธ ๋„คํŠธ์›Œํฌ ์ด๋ฏธ์ง€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹๊ณผ ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค๊ณ  ๋ณด๋ฉด ๋œ๋‹ค. ๋„คํŠธ์›Œํฌ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋ฐ›์•„ ์ฒ˜๋ฆฌํ•˜๋А๋ƒ, ์œ„์ ฏ ์ž์ฒด๋ฅผ ์ด๋ฏธ์ง€๋กœ ์ƒ์„ฑํ•˜๋А๋ƒ์˜ ๋ฐฉ์‹๋งŒ ๋‹ค๋ฅผ ๋ฟ ๋™์ผํ•˜๊ฒŒ bytes ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

Future<BitmapDescriptor> getMarkerWidgetIcon(GlobalKey key) async {
    final RenderRepaintBoundary boundary =
        key.currentContext!.findRenderObject() as RenderRepaintBoundary;

    final ui.Image image = await boundary.toImage(pixelRatio: 3.0);
    final ByteData? byteData =
        await image.toByteData(format: ui.ImageByteFormat.png);
    if (byteData == null) return BitmapDescriptor.defaultMarker;
    final Uint8List markerIcon = byteData.buffer.asUint8List();
    return BitmapDescriptor.bytes(markerIcon);
  }

์œ„์ ฏ์˜ ์ดˆ๊ธฐ initState์—์„œ ํ˜ธ์ถœ์‹œ ์ฃผ์˜ํ•  ์ ์ด ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋œ ํ›„ ํ˜ธ์ถœ ๋˜์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋˜๋Š” ์‹œ์ ์˜ ํ”„๋ ˆ์ž„์„ ๋”œ๋ ˆ์ด ์ฃผ๊ณ  ํ˜ธ์ถœํ•ด์ฃผ๋ฉด ๋œ๋‹ค.


void initState() {
	super.initState();
	_loadWidgetIcon();
}


Future<void> _loadWidgetIcon() async {
WidgetsBinding.instance.addPostFrameCallback((_) async {
	await Future.delayed(const Duration(milliseconds: 50));
	 _widgetIcon = await GoogleMapHelper.getMarkerWidgetIcon(_markerKey);
	});
}

์ปค์Šคํ…€ ๋งˆ์ปค๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š” ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์ˆ˜์ ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋ Œ๋”๋ง ์ง€์—ฐ์ด๋‚˜ ๋”œ๋ ˆ์ด๊ฐ€ ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค. ๋”ฐ๋ผ์„œ ์‚ฌ์ „์— ๋ Œ๋”๋งํ•œ ์ด๋ฏธ์ง€๋ฅผ ์บ์‹ฑํ•˜๊ฑฐ๋‚˜, ์ดˆ๊ธฐํ™” ๋‹จ๊ณ„์—์„œ ๋ฏธ๋ฆฌ ์ƒ์„ฑํ•ด๋‘๋Š” ๋“ฑ์˜ ์ตœ์ ํ™” ์ „๋žต์ด ์ค‘์š”ํ•˜๋‹ค.

์ด ๊ธ€์—์„œ๋Š” ์ปค์Šคํ…€ ๋งˆ์ปค ์ƒ์„ฑ ๋ฐฉ๋ฒ•์˜ ํ•ต์‹ฌ๋งŒ ๊ฐ„๋‹จํžˆ ๊ตฌํ˜„ํ–ˆ์ง€๋งŒ, ์‹ค์ œ ์„œ๋น„์Šค ํ™˜๊ฒฝ์—์„œ๋Š” ๋งˆ์ปค๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๊ฒฝ์šฐ์—๋Š” ํฐ ๋ฌธ์ œ๊ฐ€ ์—†์„ ์ˆ˜๋„ ์žˆ์–ด๋„, ์ง€์†์ ์œผ๋กœ ๋ฐ˜๋ณต ์ƒ์„ฑํ•˜๋Š” ๊ตฌ์กฐ๋Š” ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ปค์Šคํ…€ ๋งˆ์ปค๋ฅผ ๋„์ž…ํ•  ๋•Œ์—๋Š” ์„ฑ๋Šฅ๊ณผ ์œ ์—ฐ์„ฑ ๋ชจ๋‘๋ฅผ ๊ณ ๋ คํ•œ ์ „๋žต์ด ํ•„์š”ํ•˜๊ฒ ๋‹ค.

basic asset network widget

๋งˆ์ปค๋ฅผ ๊ตฌ์„ฑํ•  ๋•Œ์— ์ค‘์š”ํ•œ ๋””์ž์ธ ์ ์ธ ์š”์†Œ์™€ ์‚ฌ์šฉ์„ฑ ๊ฐœ์„ ์ด ๋ฐ”๋กœ ํด๋Ÿฌ์Šคํ„ฐ(Cluster)์ด๋‹ค.
์ˆ˜ ๋งŽ์€ ๋งˆ์ปค๊ฐ€ ํ•œ ํ™”๋ฉด์— ๋™์‹œ์— ํ‘œ์‹œ๋  ๊ฒฝ์šฐ, ์ง€๋„๊ฐ€ ๋ณต์žกํ•ด์ง€๊ณ  ์‹œ์ธ์„ฑ์ด ๋–จ์–ด์ง€๋ฉฐ, ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ํฌ๊ฒŒ ์ €ํ•˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ํด๋Ÿฌ์Šคํ„ฐ๋ง์„ ํ™œ์šฉํ•˜์—ฌ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•ด์•ผ ํ•œ๋‹ค.

ํด๋Ÿฌ์Šคํ„ฐ๋Š” ์„œ๋กœ ๊ฐ€๊นŒ์šด ์œ„์น˜์— ์žˆ๋Š” ๋งˆ์ปค๋“ค์„ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์–ด ํ•˜๋‚˜์˜ ๋งˆ์ปค(Icon)๋กœ ํ‘œํ˜„ํ•จ์œผ๋กœ์จ, ์ง€๋„๋ฅผ ๊น”๋”ํ•˜๊ฒŒ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ์ •๋ณด๋ฅผ ํšจ๊ณผ์ ์œผ๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.

ํด๋Ÿฌ์Šคํ„ฐ(Cluster)๋Š” ์—ฌ๋Ÿฌ ํŒจํ‚ค์ง€๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋น„๊ต์  ์†์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ง€๋„์ƒ์˜ ๋งˆ์ปค ๋ฐ์ดํ„ฐ๋ฅผ ์–ด๋–ป๊ฒŒ ๊ทธ๋ฃนํ™”ํ• ์ง€, ์คŒ ๋ ˆ๋ฒจ์— ๋”ฐ๋ผ ์–ด๋–ป๊ฒŒ ํ‘œํ˜„ํ• ์ง€, ์ปค์Šคํ…€ UI๋Š” ์–ด๋–ป๊ฒŒ ์ ์šฉํ• ์ง€ ๋“ฑ ๋‹ค๋ค„์•ผ ํ•  ์š”์†Œ๋“ค์ด ๋งŽ๋‹ค. ๋”ฐ๋ผ์„œ ํด๋Ÿฌ์Šคํ„ฐ์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ณ„๋„์˜ ๊ธ€์—์„œ ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ณ , ์ด ๊ธ€์—์„œ๋Š” ํด๋Ÿฌ์Šคํ„ฐ๋ง ๊ตฌํ˜„์— ๋Œ€ํ•œ ์„ค๋ช…์€ ์ƒ๋žตํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

Displaying Radius with Map Circles

์ง€๋„ ์œ„์— ํŠน์ • ์œ„์น˜๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋ฐ˜๊ฒฝ(Radius)์„ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œํ˜„ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ์žˆ๊ฒŒ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‚ด ์œ„์น˜ ๊ธฐ์ค€ 1km ์ด๋‚ด๋ฅผ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์ง€์—ญ์˜ ๋ฒ”์œ„๋‚˜ ์ œํ•œ ๊ตฌ์—ญ์„ ์‹œ๊ฐํ™”ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ Circle ์œ„์ ฏ์„ ํ™œ์šฉํ•˜๋ฉด ๊ตฌ๊ธ€ ์ง€๋„ ์œ„์— ์†์‰ฝ๊ฒŒ ์› ํ˜•ํƒœ์˜ ์˜ค๋ฒ„๋ ˆ์ด๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

circles ํ•„๋“œ์— ์œ„์—์„œ ์‚ดํŽด๋ณธ ๋งˆ์ปค์™€ ๋™์ผํ•˜๊ฒŒ Set ํƒ€์ž…์— Circle ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

๊ณ ์œ ํ•œ circleId๋ฅผ ์ƒ์„ฑํ•˜๊ณ , center ํ•„๋“œ์— ์ง€๋„ ์ขŒํ‘œ๊ณ„๋ฅผ ์ง€์ •ํ•ด์ค€๋’ค radius์— ๋ฒ”์œ„๋ฅผ ๋ฏธํ„ฐ(meter) ๊ฐ’์œผ๋กœ ์ƒ์„ฑํ•ด์ฃผ๋ฉด ์ง€๋„์œ„์— ์›ํ˜•์˜ ๋ฒ”์œ„๊ฐ€ ์ง€์ •๋˜๊ฒŒ ๋œ๋‹ค.

๋ฒ”์œ„ ์•ˆ์— ์ƒ‰์ƒ์€ fillColor๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๊ณ , ํ…Œ๋‘๋ฆฌ ์„ ์€ storkeColor์— ์›ํ•˜๋Š” ์ƒ‰์ƒ์„ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค. ํ…Œ๋‘๋ฆฌ ์„ ์˜ ๋‘๊ป˜๋ฅผ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด strokeWidth ํ•„๋“œ์— ์›ํ•˜๋Š” ๋‘๊ป˜๋ฅผ ์ฃผ๋ฉด๋œ๋‹ค.

๋งˆ์ปค๋ฅผ ๋‹ค๋ฃฐ ๋•Œ์™€ ๋™์ผํ•˜๊ฒŒ zIndex, visible, onTap, consumeTapEvents ํ•„๋“œ๋„ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ํ™œ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

circles: {
	...
	Circle(
		circleId: const CircleId(uniqueId),
		center: const LatLng(latitude, longitude),
		radius: 1000,
        ...
	),
},

Drawing Polygons on the Map

์ด๋ฒˆ์—๋Š” ๋ฒ”์œ„๊ฐ€ ์•„๋‹Œ ๊ตฌ์—ญ์„ ์ง€์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

๋ฒ”์œ„๊ฐ€ ์•„๋‹Œ ๊ตฌ์—ญ(๋ฉด)์„ ์ง€๋„์— ์˜ค๋ฒ„๋ ˆ์ดํ•˜๋ ค๋ฉด Polygon์„ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

์˜ˆ๋ฅผ๋“ค์–ด ๋Œ€ํ•œ๋ฏผ๊ตญ, ์„œ์šธ์‹œ, ๋‰ด์š•์ฒ˜๋Ÿผ ํŠน์ • ๋„์‹œ/ํ–‰์ •๊ตฌ์—ญ/๋‹จ์ง€๋ฅผ ๋ฐ˜ํˆฌ๋ช… ์˜ค๋ฒ„๋ ˆ์ด๋กœ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜, ์ง€๋ช…์„ ํƒญํ–ˆ์„ ๋•Œ ํ•ด๋‹น ๊ตฌํš์˜ ๊ฒฝ๊ณ„๋ฅผ ๊ทธ๋ ค ๊ฐ•์กฐํ•˜๋Š” ๋ฐ ์“ฐ์ธ๋‹ค. ๋‹ค๋งŒ ํด๋ฆฌ๊ณค์„ ๊ทธ๋ฆฌ๋ ค๋ฉด ํ•ด๋‹น ์ง€์—ญ์˜ ๊ฒฝ๊ณ„ ์ขŒํ‘œ(์œ„๋„/๊ฒฝ๋„ ๋ฆฌ์ŠคํŠธ)๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ, ํ–‰์ •๊ฒฝ๊ณ„/์ž์ ๋„ ๊ฐ™์€ ๊ฒฝ๊ณ„ ๋ฐ์ดํ„ฐ(๊ณต๊ณต๋ฐ์ดํ„ฐ, ์ง€๋„ ๊ฒฝ๊ณ„ API, ์˜คํ”ˆ ์ง€์˜ค๋ฐ์ดํ„ฐ)๋ฅผ ํ™•๋ณดํ•ด์•ผ ์ž์„ธํ•œ ๊ตฌ์—ญ์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.

์•ž์„œ ์‚ดํŽด๋ณธ ๋งˆ์ปค์™€ ์จํด์ฒ˜๋Ÿผ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ๊ฑฐ์˜ ๋™์ผํ•˜๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

Set ํƒ€์ž…์˜ polygons ํ•„๋“œ์— Polygon ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

ํด๋ฆฌ๊ณค์—๋Š” ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด ์žˆ๋Š”๋ฐ, ๋ฐ”๋กœ points์™€ holes์ด๋‹ค.

points๋Š” ํด๋ฆฌ๊ณค์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์‹ถ์€ ๊ตฌ์—ญ์˜ ์ง€๋„ ์ขŒํ‘œ๊ณ„๋“ค์ด๊ณ , holes๋Š” ์ œ์™ธํ•˜๊ณ ์ž ํ•˜๋Š” ๊ตฌ์—ญ์˜ ์ง€๋„ ์ขŒํ‘œ๊ณ„๋“ค์ด๋‹ค. ์‰ฝ๊ฒŒ ์„ค๋ช…ํ•ด์„œ ์„œ์šธ์‹œ๋ฅผ ํด๋ฆฌ๊ณค์œผ๋กœ ์ง€์ •ํ•˜๊ณ  ์‹ถ์€๋ฐ, ๊ตฌ์—ญ ๋‚ด์— ์ข…๋กœ๊ตฌ๋Š” ์ œ์™ธ์‹œํ‚ค๊ณ  ์‹ถ๋‹ค๋ฉด points ํ•„๋“œ์— ์„œ์šธ์‹œ์˜ ๊ฒฝ๊ณ„ ์ขŒํ‘œ๋ฅผ ๋ฐฐ์—ด๋กœ ์ง€์ •ํ•œ ๋’ค, holes ํ•„๋“œ์— ์ข…๋กœ๊ตฌ์— ํ•ด๋‹นํ•˜๋Š” ๊ฒฝ๊ณ„ ์ขŒํ‘œ๋“ค์„ ๋ฐฐ์—ด๋กœ ๋„ฃ์–ด์ฃผ๊ฒŒ ๋˜๋ฉด ์„œ์šธ์‹œ์˜ ๊ตฌ์—ญ์ด ์žกํžˆ๋ฉด์„œ ๊ทธ ์•ˆ์— ์žˆ๋Š” ์ข…๋กœ๊ตฌ๋Š” ์ œ์™ธ๋˜๊ฒŒ ๋œ๋‹ค.

ํด๋ฆฌ๊ณค์˜ ์˜ต์…”๋„ ํ•„๋“œ๋กœ geodesic ํ•„๋“œ๊ฐ€ ์žˆ๋Š”๋ฐ, ์ด๊ฑด ์ง€๊ตฌ์˜ ๊ณก๋ฅ ์„ ๋ฐ˜์˜ํ• ์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒƒ์œผ๋กœ ๊ฒฝ๊ณ„ ์ขŒํ‘œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์ฐจ์ด๊ฐ€ ์—†์ง€๋งŒ ๋‹ค๊ฐํ˜•์˜ ๊ฐ ๋ณ€์„ ํ‰๋ฉด ์ง์„ ์ด ์•„๋‹Œ ์ง€๊ตฌ ๊ณก๋ฅ ์„ ๋ฐ˜์˜ํ•˜๊ณ  ์‹ถ์„ ๋•Œ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ž์„ธํ•œ ์„ค๋ช…์€ ์•„๋ž˜์—์„œ ์„ค๋ช…ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

๋งˆ์ปค, Circle๊ณผ ๋™์ผํ•˜๊ฒŒ ์ƒ‰์ƒ ์ปฌ๋Ÿฌ๋‚˜ ๋‘๊ป˜ ๋“ฑ์˜ ์˜ต์…˜๋“ค์˜ ์˜๋ฏธ์™€ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ๋™์ผํ•˜๋‹ค.

polygons: {
	Polygon(
		polygonId: const PolygonId(uniqueId),
		points: [
			...
			LatLng(latitude, longitude),
		],
		holes: [
			...
			LatLng(latitude, longitude),
		],
	)
},

Creating Routes with Polylines

์ง€๋„ ์œ„์— ๋‘ ๊ฐœ ์ด์ƒ์˜ ์ขŒํ‘œ๋ฅผ ์—ฐ๊ฒฐํ•ด ๊ฒฝ๋กœ๋ฅผ ์‹œ๊ฐํ™”ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” Polyline์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

๋งต์ด๋‚˜ ์ง€๋„๋ฅผ ํ™œ์šฉํ•˜๋Š” ์„œ๋น„์Šค๋“ค์—์„œ ์ž์ฃผ ๊ฒฝํ—˜ํ•ด๋ณธ ๊ธฐ๋Šฅ์ผ ๊ฒƒ์ด๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋„ค๋น„๊ฒŒ์ด์…˜ ์„œ๋น„์Šค์—์„œ ์ฐจ๋Ÿ‰์˜ ์ด๋™ ๊ฒฝ๋กœ๋ฅผ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜, ์—ฌํ–‰ ์„œ๋น„์Šค์—์„œ ๋น„ํ–‰๊ธฐ์˜ ์šดํ•ญ ๋ฃจํŠธ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.

Polyline์€ ์ด๋Ÿฌํ•œ ๊ฒฝ๋กœ๋ฅผ ๋‹จ์ˆœํ•œ ์ง์„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ๊ณก์„ , ์ƒ‰์ƒ ๋ณ€๊ฒฝ, ์ ์„  ํŒจํ„ด ๋“ฑ ๋‹ค์–‘ํ•œ ์Šคํƒ€์ผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์‹œ๊ฐ์ ์œผ๋กœ ๊ฒฝ๋กœ๋ฅผ ๋”์šฑ ์ง๊ด€์ ์ด๊ณ  ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ฒŒ ๋งŒ๋“ ๋‹ค.

์ด๋ฏธ ์•ž์„œ Marker, Circle, Polygon ๊ธฐ๋Šฅ์„ ์‚ดํŽด๋ณด๋ฉด์„œ, google_maps_flutter ํŒจํ‚ค์ง€๊ฐ€ ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๋Š” ์ „๋ฐ˜์ ์ธ ํŒจํ„ด์„ ์–ด๋А ์ •๋„ ์ตํ˜”์„ ๊ฒƒ์ด๋‹ค. ๋”ฐ๋ผ์„œ Polyline์˜ ์‚ฌ์šฉ ๋ฐฉ๋ฒ•๊ณผ ์˜ต์…˜ ์—ญ์‹œ ์˜ˆ์ƒ๊ณผ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

Polyline๋„ Set ํƒ€์ž…์œผ๋กœ ๋‹จ์ผ ๊ฒฝ๋กœ๊ฐ€ ์•„๋‹Œ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ฒฝ๋กœ๋ฅผ PolylineId ๋ผ๋Š” ๊ณ ์œ  ID๋กœ ์ƒ์„ฑํ•˜๊ฒŒ ๋œ๋‹ค.

Polygon์—์„œ์™€ ๋™์ผํ•˜๊ฒŒ points ํ•„๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€๋„ ์ขŒํ‘œ๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ์ง€์ •ํ•˜๋ฉด, ์ด๋ฅผ ํ•˜๋‚˜์˜ ์ด์–ด์ง„ ์„ (Line) ํ˜•ํƒœ๋กœ ์ง€๋„ ์œ„์— ํ‘œ์‹œํ•˜๊ฒŒ ๋œ๋‹ค.

์‹ค์ œ๋กœ ๋„ค๋น„๊ฒŒ์ด์…˜ ์„œ๋น„์Šค์—์„œ ๊ฒฝ๋กœ๋ฅผ ์š”์ฒญํ•˜๋ฉด, ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋ฐ›์€ ๊ฒฝ์œ ์ง€์™€ ๋ชฉ์ ์ง€ ์ขŒํ‘œ๋“ค์ด Polyline์˜ points ๋ฆฌ์ŠคํŠธ์— ๋งคํ•‘๋˜์–ด, ์ง€๋„ ์œ„์— ๊ฒฝ๋กœ๊ฐ€ ์‹œ๊ฐํ™” ๋˜๋Š” ๊ตฌ์กฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋œ๋‹ค.
์„ ๋“ค์„ ์ตœ๋Œ€ํ•œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋งคํ•‘ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋•Œ๋กœ๋Š” ๊ฒฝ๋กœ์— ๋”ฐ๋ผ ์ˆ˜์‹ญ ๊ฐœ์—์„œ ์ˆ˜์ฒœ ๊ฐœ ๋˜๋Š” ๊ทธ ์ด์ƒ์˜ ์ง€๋„ ์ขŒํ‘œ๊ณ„๋“ค์„ ์‚ฌ์šฉํ•˜๊ธฐ๋„ ํ•œ๋‹ค.

๋ผ์ธ(Line)์„ ํ‘œ์‹œํ•  ๋•Œ์— ์ค‘์š”ํ•œ ๋ถ€๋ถ„์ด ํ•˜๋‚˜ ์žˆ๋Š”๋ฐ, ๋ฐ”๋กœ ์žฅ๊ฑฐ๋ฆฌ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋ฆฌ๋Š” ๊ฒฝ์šฐ์ด๋‹ค.

๋‹จ์ˆœํžˆ ์ขŒํ‘œ๋ฅผ ์ง์„ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜๋ฉด ์‹ค์ œ ์ง€๊ตฌ ์œ„์˜ ์ตœ๋‹จ ๊ฒฝ๋กœ์™€๋Š” ๋‹ค๋ฅด๊ฒŒ ํ‘œ์‹œ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ธ๋ฐ, ์ด๋Ÿฌํ•œ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜๋Š” ์ด์œ ๋Š” Polyline์ด ํ‰๋ฉด ์ขŒํ‘œ๊ณ„๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์„ ์„ ๊ทธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” geodesic ์†์„ฑ์„ ์„ค์ • ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

geodesic๋Š” ์•ž์„œ Polygon์—์„œ๋„ ์‚ฌ์šฉ ํ•˜์˜€๋˜ ์˜ต์…˜ ์ด์—ˆ๋Š”๋ฐ, ์ข€ ๋” ์ž์„ธํžˆ ์„ค๋ช…์„ ํ•ด๋ณด์ž๋ฉด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€๋„๋Š” ํ‰๋ฉด ํˆฌ์˜ ์œ„์— ๊ทธ๋ ค์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ์ง€์ ์„ ์„ ์œผ๋กœ ์ด์„ ๋•Œ ๋‹จ์ˆœ ์ง์„ ์œผ๋กœ ํ‘œ์‹œ๋œ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค์ œ ์ง€๊ตฌ๋Š” ๊ตฌ ํ˜•ํƒœ์— ๊ฐ€๊น๊ณ , ๋‘ ์ง€์  ์‚ฌ์ด์˜ ์‹ค์ œ ์ตœ๋‹จ ๊ฒฝ๋กœ๋Š” ๋Œ€๊ถŒ(Great-circle) ๊ฒฝ๋กœ์ด๊ธฐ ๋•Œ๋ฌธ์—, ๋‹จ๊ฑฐ๋ฆฌ๋‚˜ ๊ตญ์†Œ ๊ตฌ์—ญ์—์„œ ์‚ฌ์šฉํ•  ๋•Œ์—๋Š” ํฐ ์ฐจ์ด๊ฐ€ ์—†์ง€๋งŒ ๋น„ํ–‰ ๊ฒฝ๋กœ๋‚˜ ๊ตญ๊ฐ€ ๊ฐ„ ์ตœ๋‹จ๊ฑฐ๋ฆฌ์ฒ˜๋Ÿผ ์žฅ๊ฑฐ๋ฆฌ ๋ผ์ธ์„ ์‹œ๊ฐํ™”๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋Š” ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด๋‹ค ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ผ์ธ์„ ๊ทธ๋ฆด ์ˆ˜ ์ž‡๊ฒŒ ๋œ๋‹ค.

์ฐจ์ด๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ์ง€๋„ ์ƒ์—์„œ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๋Œ€ํ•œ๋ฏผ๊ตญ์—์„œ ๋ฏธ๊ตญ์˜ ์ขŒํ‘œ๋ฅผ ์ง€์ •ํ•ด ๋ณด๋ฉด false(default)์ธ ๊ฒฝ์šฐ ํƒœํ‰์–‘์„ ๊ฐ€๋กœ์ง€๋ฅด๋Š” ์ผ์ง์„  ๋ผ์ธ์œผ๋กœ ํ‘œ์‹œ๋˜์ง€๋งŒ, true๋กœ ๋ณ€๊ฒฝํ•ด ๋ณด๋ฉด, ์‹ค์ œ ๋น„ํ–‰๊ธฐ ๊ฒฝ๋กœ์™€ ์œ ์‚ฌํ•œ ๋ถ๊ทน ๋ผ์ธ์„ ๊ฐ€๋กœ์ง€๋ฅด๋Š” ๊ณก๋ฅ ๋กœ ํ‘œ์‹œ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

Polyline ๋””์ž์ธ ์š”์†Œ๋กœ๋Š” color, width๋กœ ์„  ์ƒ‰์ƒ๊ณผ ๋‘๊ป˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ํˆฌ๋ช…๋„๋Š” Color ๊ฐ์ฒด์˜ withOpacity๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

startCap, endCap์€ Cap.buttCap(ํ‰ํ‰), Cap.roundCap(๋‘ฅ๊ทผ), Cap.squareCap(์‚ฌ๊ฐํ˜•)์œผ๋กœ ์‹œ์ž‘๊ณผ ๋์˜ ์„  ๋ชจ์–‘์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, iOS Google Map SDK์—๋Š” ์ง€์›ํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ์œผ๋กœ Android์™€ Web์—์„œ๋งŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์„ (Line)์˜ ํŒจํ„ด์€ patterns ํ•„๋“œ์— ๋ฐฐ์—ด๋กœ PatternItem์„ ๋ฐฐ์—ด๋กœ ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ํ•˜๋‚˜์˜ ์—ฐ๊ฒฐ๋œ ์„ ์ด ์•„๋‹Œ ํŠน์ • ๊ตฌ๊ฐ„๋งˆ๋‹ค ๋Š๊ธด ์ ์„  ๋“ฑ์„ ๋งŒ๋“ค์–ด ์ค„ ์ˆ˜ ์žˆ๋‹ค. ์ ์„ ์„ ๋งŒ๋“ค๊ณ  ์‹ถ๋‹ค๋ฉด PatternItem.dash(double) + PatternItem.gap(double)์„ ์กฐํ•ฉํ•ด์„œ ์›ํ•˜๋Š” ๊ธธ์ด์™€ ๊ฐ„๊ฒฉ์„ ์ปค์Šคํ…€ํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ผ์ธ์˜ ๋ชจ์„œ๋ฆฌ ๋ถ€๋ถ„๋„ ๋””์ž์ธ ๋ณ€๊ฒฝ์ด ๊ฐ€๋Šฅํ•œ๋ฐ jointType ํ•„๋“œ์— JointType์œผ๋กœ .mitered(default), .bevel, .round๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

- .mitered : ๋‚ ์นด๋กœ์šด ๋ชจ์„œ๋ฆฌ
- .bevel : ๋ชจ์„œ๋ฆฌ๋ฅผ ๊น์•„์„œ ํ‰ํ‰ํ•˜๊ฒŒ
- .round : ๋ชจ์„œ๋ฆฌ๋ฅผ ๋‘ฅ๊ธ€๊ฒŒ

๊ตฌ๊ธ€ ์ง€๋„๋ฅผ ์ปจํŠธ๋กคํ•˜๊ณ , ์ง€๋„ ์œ„์— ์ •๋ณด๋ฅผ ์‹œ๊ฐํ™”ํ•˜๊ธฐ ์œ„ํ•ด Marker, Circle, Polygon, Polyline ๊ฐ™์€ ๋””์ž์ธ ์š”์†Œ๋“ค์„ ์‚ดํŽด๋ณด์•˜๋‹ค. ์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•๊ณผ ํ•ต์‹ฌ ์˜ต์…˜ ์œ„์ฃผ๋กœ ์ •๋ฆฌํ–ˆ์ง€๋งŒ, ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ์ง€๋„ ๋ทฐ์˜ ์คŒ ๋ ˆ๋ฒจ, ๊ธฐ์šธ๊ธฐ/ํšŒ์ „, ์ œ์Šค์ฒ˜ ์ œ์–ด, ๋™์  ์Šคํƒ€์ผ๋ง ๋“ฑ ๊ณ ๋ คํ•ด์•ผ ํ•  ์š”์†Œ๊ฐ€ ๋งŽ์•„, ์™„์„ฑ๋„ ๋†’์€ ์ง€๋„ ์„œ๋น„์Šค๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ์ผ์€ ๊ฒฐ์ฝ” ๊ฐ„๋‹จํ•˜์ง€ ์•Š๋‹ค. ์•ž์œผ๋กœ ์ด๋Ÿฌํ•œ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๊ณผ ์ตœ์ ํ™” ๋ฐฉ๋ฒ•์„ ์‹ค๋ฌด์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์ œ๋กœ ์ง€์†์ ์œผ๋กœ ๋‹ค๋ค„๋ณผ ์˜ˆ์ •์ด๋‹ค.

Geocoding Basics

์ง€๋„๋ฅผ ๋‹ค๋ฃจ๋‹ค ๋ณด๋ฉด ๋‹จ์ˆœํžˆ ๋งˆ์ปค๋‚˜ ์›, ํด๋ฆฌ๊ณค ๊ฐ™์€ ์š”์†Œ๋ฅผ Overlay(์˜ค๋ฒ„๋ ˆ์ด)ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•  ๋•Œ๊ฐ€ ๋งŽ์€๋ฐ, ์‚ฌ์šฉ์ž๋Š” ๋ณดํ†ต "์ด ์ขŒํ‘œ๊ฐ€ ์–ด๋–ค ์ฃผ์†Œ์ธ์ง€" ๋˜๋Š” "์ด ์ฃผ์†Œ๊ฐ€ ์ง€๋„์—์„œ ์–ด๋””์ฏค์ธ์ง€"๋ฅผ ์•Œ๊ณ  ์‹ถ์–ดํ•œ๋‹ค.

์ด ๊ณผ์ •์„ ๋ฐ”๋กœ ์ง€์˜ค์ฝ”๋”ฉ(Geocoding), ๋ฆฌ๋ฒ„์Šค ์ง€์˜ค์ฝ”๋”ฉ(Reverse Geocoding) ์ด๋ผ๊ณ  ํ•œ๋‹ค.

์ง€๋„ ์ขŒํ‘œ๊ณ„(์œ„๋„/๊ฒฝ๋„)๋ฅผ ์ฃผ์†Œ๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ๊ณผ์ •์„ ๋ฆฌ๋ฒ„์Šค ์ง€์˜ค์ฝ”๋”ฉ(Reverse Geocoding)์ด๋ผ๊ณ  ํ•˜๋ฉฐ, ์ง€๋„ ์œ„์˜ ํŠน์ • ๊ณต๊ฐ„์„ ๋ˆŒ๋ €์„ ๋•Œ์—, ๊ทธ ์ง€๋„ ์ขŒํ‘œ๊ณ„๋ฅผ ์–ด๋–ค ํ–‰์ •๊ตฌ์—ญ์ด๋‚˜ ๋„๋กœ๋ช… ์ฃผ์†Œ ๋“ฑ ์‚ฌ๋žŒ์ด ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ์ง€์˜ค์ฝ”๋”ฉ(Geocoding)์€ ์ฃผ์†Œ๋ฅผ ์ง€๋„ ์ขŒํ‘œ๊ณ„๋กœ ๋ฐ”๊ฟ”์ฃผ๋Š” ๊ณผ์ •์„ ๋งํ•œ๋‹ค.
์‚ฌ์šฉ์ž๊ฐ€ ํŠน์ • ์ฃผ์†Œ์— ๋งˆ์ปค๋ฅผ ์ฐ๊ณ  ์‹ถ๋‹ค๋ฉด, ์ฃผ์†Œ๋ฅผ ์ง€์˜ค์ฝ”๋”ฉํ•˜์—ฌ ์ง€๋„ ์ขŒํ‘œ๊ณ„๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ง€๋„ ๊ณต๊ฐ„ ์œ„์— ํ‘œ์‹œํ•ด์ฃผ๋ฉด ๋˜๋Š” ๊ฒƒ์ด๋‹ค.

์ง€๋„๋Š” ๋ณธ์งˆ์ ์œผ๋กœ ์ขŒํ‘œ๊ณ„ ์œ„์—์„œ ๋™์ž‘ํ•˜์ง€๋งŒ, ์‚ฌ๋žŒ์€ ์ขŒํ‘œ๋ณด๋‹ค๋Š” ์ฃผ์†Œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์‚ฌ๊ณ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์ง€๋„ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค ๋•Œ๋Š” ์ฃผ์†Œ์™€ ์ขŒํ‘œ๋ฅผ ์ƒํ˜ธ ๋ณ€ํ™˜ํ•˜๋Š” ๊ธฐ๋Šฅ์ด ๋ฐ˜๋“œ์‹œ ํ•„์š”ํ•˜๊ฒŒ ๋œ๋‹ค.

Google Maps์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ์ด ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์„๊นŒ ? GCP์˜ Google Maps Platform์˜ Geocoding API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์‰ฝ๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋‹ค.

์•ž์„œ, Android์™€ iOS์—์„œ ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด API Key๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„ ํ”„๋กœ์ ํŠธ์— ์ ์šฉํ–ˆ์—ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์—ฌ๊ธฐ์„œ Geocoding API๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ•  ๋–„์— ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋œ๋‹ค.

GCP์—์„œ API Key๋ฅผ ๋ฐœ๊ธ‰ํ•  ๋•Œ ๋ณด์•ˆ์„ ์œ„ํ•ด ํ”ํžˆ ํ‚ค ์ œํ•œ์‚ฌํ•ญ - ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œํ•œ์„ ๊ฑธ์–ด๋‘” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜๊ณ  ์žˆ์„ ๊ฒƒ์ด๋‹ค.
์ด ๋ฐฉ์‹์€ ์ง€๋„ SDK๋ฅผ ์•ฑ์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•  ๋•Œ์—๋Š” ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ, Geoconding API๋Š” ๋ณดํ†ต HTTP ์š”์ฒญ ๊ธฐ๋ฐ˜์˜ Web API ํ˜•ํƒœ๋กœ ์ œ๊ณต๋˜๊ธฐ ๋–„๋ฌธ์—, ์•ฑ ์•ˆ์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋ฉด ํ‚ค ์ œํ•œ์ด ์ ์šฉ๋œ API ํ‚ค ์‚ฌ์šฉ์œผ๋กœ ๊ถŒํ•œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค.
์ฆ‰, ์ง€๋„๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ํ”Œ๋žซํผ ๋ณ„๋กœ ๋ฐœ๊ธ‰ ๋ฐ›์€ API ํ‚ค๋กœ๋Š” Geocoding API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜๊ฐ€ ์—†๋‹ค.

ํ”Œ๋žซํผ๋ณ„ API ํ‚ค ์ œํ•œ์„ ๋‘์ง€ ์•Š๊ณ  ๋ชจ๋“  ํ™˜๊ฒฝ์—์„œ ๊ณตํ†ต์œผ๋กœ ์‚ฌ์šฉํ•˜๋„๋ก ์„ค์ •ํ•œ๋‹ค๋ฉด, Geocoding API๋ฅผ ํ™œ์„ฑํ™” ํ•ด์ฃผ๋ฉด ๋˜์ง€๋งŒ, ๋ณด์•ˆ ๊ฐ•ํ™”๋ฅผ ์œ„ํ•ด ํ”Œ๋žซํผ๋ณ„ ์ œํ•œ์„ ์œ ์ง€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด, ๊ธฐ์กด ํ‚ค๋Š” SDK ์ „์šฉ์œผ๋กœ ์œ ์ง€ํ•˜๊ณ  ์ถ”๊ฐ€๋กœ API Key๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

ํ…Œ์ŠคํŠธ ๋‹จ๊ณ„์—์„œ๋Š” ์ œํ•œ ์—†๋Š” API ํ‚ค ํ•˜๋‚˜๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜๋‹ค. ํ•˜์ง€๋งŒ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ํ‚ค๊ฐ€ ๊ทธ๋Œ€๋กœ ๋…ธ์ถœ๋˜๋ฉด ๋ณด์•ˆ์ƒ ํฐ ์œ„ํ—˜์ด ์ƒ๊ธฐ๋Š”๋ฐ, ๋ˆ„๊ตฌ๋‚˜ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ด API๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์–ด ๊ณผ๊ธˆ์ด๋‚˜ ์ฟผํ„ฐ ์†Œ์ง„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ์‹ค์ œ ์„œ๋น„์Šค์—์„œ๋Š” ํ”Œ๋žซํผ๋ณ„๋กœ ํ‚ค๋ฅผ ๋‚˜๋ˆ  ์ œํ•œ์„ ๋‘๊ฑฐ๋‚˜, ์„œ๋ฒ„๋ฅผ ํ†ตํ•ด Geocoding API๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ IP ์ œํ•œ์„ ์ ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ์•ˆ์ „ํ•˜๋‹ค.

์ƒˆ๋กœ์šด API ํ‚ค๋ฅผ ๋ฐœ๊ธ‰ ๋ฐ›์•„๋ณด์ž.

ํ‚ค ๋ฐœ๊ธ‰์€ ์•ž์„œ ํ•ด๋ณธ ๊ฒƒ๊ณผ ๋™์ผํ•œ ์ ˆ์ฐจ๋กœ GCP > ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ๋งŒ๋“ค๊ธฐ > API ํ‚ค๋กœ ์ด๋™ํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ œํ•œ์‚ฌํ•ญ์„ ์—†์Œ์œผ๋กœ ํ•ด์ฃผ๋„๋ก ํ•˜์ž.
๋‹ค๋งŒ, ํ…Œ์ŠคํŠธ ์šฉ๋„๊ฐ€ ์•„๋‹ˆ๋ผ ์‹ค์ œ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ์‚ฌ์šฉ๋  ์˜ˆ์ •์ด๋ผ๋ฉด IP ์ฃผ์†Œ ์ œํ•œ๊ณผ ๊ฐ™์€ ๋ณด์•ˆ์„ค์ •์„ ๊ผญ ์ ์šฉํ•ด ํด๋ผ์ด์–ธํŠธ์—์„œ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ , ์„œ๋ฒ„์—์„œ Geocoding API๋ฅผ ํ”„๋ก์‹œ๋กœ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•ˆ์ „ํ•˜๋‹ค.

SDK๋ฅผ ์ถ”๊ฐ€ํ•œ ๊ฒƒ๊ณผ ๊ฐ™์ด API ์ œํ•œ์‚ฌํ•ญ์— Geocoding API๋ฅผ ์„ ํƒํ•ด ํ™œ์„ฑํ™” ํ•ด์ฃผ๋„๋ก ํ•˜์ž.

์ž ์ด์ œ ํ™œ์„ฑํ™”๋œ Geocoding API ํ‚ค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์˜ค์ฝ”๋”ฉ/์—ญ์ง€์˜ค์ฝ”๋”ฉ์„ ํ˜ธ์ถœํ•ด ๋ณด๋„๋ก ํ•˜์ž.

Geocoding API์˜ ์—”๋“œํฌ์ธํŠธ Base URL์ด๋‹ค. ๊ธฐ๋ณธ์€ json์ด์ง€๋งŒ xml์„ ์›ํ•  ๊ฒฝ์šฐ xml๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

https://maps.googleapis.com/maps/api/geocode/json

์˜ต์…”๋„ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ key๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ key๊ฐ€ ์•ž์„œ ์ƒ์„ฑํ•œ API Key๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

์–ธ์–ด๋„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, language=ko๋กœ ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ถ”๊ฐ€ํ•ด ์ฃผ๋ฉด ํ•œ๊ตญ์–ด๋กœ ์‘๋‹ต ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด ์ค€๋‹ค.

๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ง€์˜ค์ฝ”๋”ฉ/์—ญ์ง€์˜ค์ฝ”๋”ฉ์„ ์š”์ฒญํ•˜๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์•Œ์•„๋ณด์ž.

์ง€์˜ค์ฝ”๋”ฉ(Geocoding)์€ ์ฃผ์†Œ๋ฅผ ์ง€๋„ ์ขŒํ‘œ๊ณ„๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญํ• ์ด๋ฏ€๋กœ ์˜ต์…”๋„ ํŒŒ๋ผ๋ฏธํ„ฐ์— address๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ๋ณ€ํ™˜ํ•˜๊ณ ์ž ํ•˜๋Š” ์ฃผ์†Œ๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด ๋˜๊ณ , ๋ฐ˜๋Œ€์ธ ์—ญ์ง€์˜ค์ฝ”๋”ฉ(Reverse Geocoding)์„ ์š”์ฒญํ•˜๋Š” ๊ฒฝ์šฐ latlng ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋˜๋Š”๋ฐ latitude(์œ„๋„), longitude(๊ฒฝ๋„)๋ฅผ ์ฝค๋งˆ๋กœ ๊ตฌ๋ถ„ํ•ด์„œ ์š”์ฒญํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
์—ฌ๊ธฐ์„œ ์ฃผ์˜์ ์€ ์œ„๋„/๊ฒฝ๋„์˜ ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์•ˆ๋œ๋‹ค.

?key={api_key}&language=ko&address=์„œ์šธํŠน๋ณ„์‹œ ์ข…๋กœ๊ตฌ ์‚ฌ์ง๋กœ 161
?key={api_key}&language=ko&latlng=37.580467, 126.976944

์‘๋‹ต JSON ๊ตฌ์กฐ๋ฅผ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.

๊ณตํ†ต์ ์œผ๋กœ ์‘๋‹ต ์ƒํƒœ์ธ status์™€ ์›ํ•˜๋Š” ๊ฒฐ๊ณผ ๊ฐ’ results ํ•„๋“œ๋ฅผ ๋‚ด๋ ค์ค€๋‹ค.

results๋Š” ๋ฐฐ์—ด ๊ตฌ์กฐ๋กœ ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ๋‹ด๊ฒจ์„œ ๋‚ด๋ ค์˜ค๊ฒŒ ๋œ๋‹ค.
์ผ๋ฐ˜์ ์œผ๋กœ ์ง€์˜ค์ฝ”๋”ฉ์„ ์š”์ฒญํ•˜๋Š” ๊ฒฝ์šฐ ๋Œ€๋ถ€๋ถ„ 1๊ฐœ์˜ ๊ฒฐ๊ณผ ๊ฐ’๋งŒ ๋‚˜์˜ค์ง€๋งŒ, ์ •ํ™•ํ•œ ์ฃผ์†Œ๊ฐ€ ์•„๋‹Œ ํŠน์ • ํ–‰์ •๊ตฌ์—ญ๊นŒ์ง€๋งŒ ์ž…๋ ฅํ•˜๋ฉด ๋™์ผํ•œ ๋ช…์นญ์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฒ•์ •๋™/ํ–‰์ •๋™์ด ์กด์žฌํ•  ์ˆ˜ ์žˆ์–ด ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐฐ์—ด์— ๋‹ด๊ฒจ ๋ฐ˜ํ™˜๋  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด, โ€œaddress=์ค‘์•™๋กœโ€ ์™€ ๊ฐ™์ด ์š”์ฒญํ•˜๋ฉด ์ „๊ตญ์— ์กด์žฌํ•˜๋Š” โ€œ์ค‘์•™๋กœโ€๋ผ๋Š” ๋„๋กœ๋ช… ์ฃผ์†Œ๊ฐ€ ๋ชจ๋‘ ๊ฒ€์ƒ‰๋˜์–ด ์—ฌ๋Ÿฌ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ˜ํ™˜๋œ๋‹ค.
๋ฐ˜๋ฉด, โ€œ์„œ์šธํŠน๋ณ„์‹œ ์ข…๋กœ๊ตฌ ์‚ฌ์ง๋กœ 161โ€์ฒ˜๋Ÿผ ์ •ํ™•ํ•œ ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๋‹จ์ผ ๊ฒฐ๊ณผ๋งŒ ๋ฐ˜ํ™˜๋˜๋ฏ€๋กœ ์ด ๊ฒฝ์šฐ์—๋Š” results ๋ฐฐ์—ด์˜ ์ฒซ ๋ฒˆ์งธ ๊ฒฐ๊ณผ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

์—ญ์ง€์˜ค์ฝ”๋”ฉ์˜ ๊ฒฝ์šฐ๋Š” ์กฐ๊ธˆ ๋‹ค๋ฅด๋‹ค.

ํ•˜๋‚˜์˜ ์ขŒํ‘œ ๊ฐ’์„ ์ฃผ์†Œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์—์„œ ๊ทธ ์œ„์น˜๊ฐ€ ์†ํ•œ ๋„๋กœ๋ช… ์ฃผ์†Œ, ์ง€๋ฒˆ ์ฃผ์†Œ, ํ–‰์ •๋™, ๋ฒ•์ •๋™, ์‹œ/๊ตฐ/๊ตฌ, ๊ตญ๊ฐ€ ๋‹จ์œ„ ๋“ฑ ์—ฌ๋Ÿฌ ์ฃผ์†Œ ๊ณ„์ธต์ด ํ•จ๊ป˜ ๋งค์นญ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐฐ์—ด๋กœ ๋‚ด๋ ค์˜ค๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด์–ด์„œ, ํŠน์ • ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•ด ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ ๊ฐ€์žฅ ์‹ ๋ขฐ๋„๊ฐ€ ๋†’์€ ์ฒซ ๋ฒˆ์งธ ๊ฒฐ๊ณผ ๊ฐ’๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๋“ฑ์˜ ์ ์ ˆํ•œ ๋ฐฉ๋ฒ•์„ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

Google Maps Platform Geocoding API Overview์— ์ ‘์†ํ•ด ๋ณด๋ฉด Geocoding API๊ฐ€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ ํ•˜๋Š”์ง€์— ๋Œ€ํ•œ ๊ฒฐ๊ณผ ๊ฐ’์˜ ์ฃผ์†Œ ๊ณ„์ธต๋“ค์„ ์‹œ๊ฐ์ ์œผ๋กœ ํ™•์ธํ•ด ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

results ๋ฐฐ์—ด์— ๋‹ด๊ฒจ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋“ค์€ ์–ด๋–ค ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€๋„ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.

Google Maps Platform - Geocoding request and response
โ—๏ธGeocoding API์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณต์‹๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๊ธธ ๋ฐ”๋žŒ

results ๋ฐฐ์—ด์˜ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋Š” ์ง€์˜ค์ฝ”๋”ฉ๊ณผ ์—ญ์ง€์˜ค์ฝ”๋”ฉ ๋ชจ๋‘ ๋™์ผํ•œ ํ˜•ํƒœ๋ฅผ ๊ฐ€์ง„๋‹ค.

์ด ์ค‘์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ํ•„๋“œ๋Š” ๋ฐ”๋กœ formatted_address์™€ geometry์ด๋‹ค.

formatted_address๋Š” ์‚ฌ๋žŒ์ด ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ์ตœ์ข… ์ฃผ์†Œ ๋ฌธ์ž์—ด์„ ๋‹ด๊ณ  ์žˆ๊ณ , geometry๋Š” ์œ„์น˜ ๊ด€๋ จ ์„ธ๋ถ€ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

์ขŒํ‘œ๋ฅผ ์ฃผ์†Œ ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์—ญ์ง€์˜ค์ฝ”๋”ฉ์˜ ๊ฒฝ์šฐ์—๋Š” formatted_address ํ•„๋“œ์— ๋‹ด๊ธด ์ฃผ์†Œ ๋ฌธ์ž์—ด์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๊ณ , ๋ฐ˜๋Œ€๋กœ ์ฃผ์†Œ๋ฅผ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ง€์˜ค์ฝ”๋”ฉ์˜ ๊ฒฝ์šฐ์—๋Š” geometry ํ•„๋“œ์˜ ์ขŒํ‘œ ์ •๋ณด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋œ๋‹ค.

formatted_address๋Š” ๋ฐ˜ํ™˜ ํƒ€์ž…์ด ๋ฌธ์ž์—ด๋กœ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ geometry ํ•„๋“œ๋Š” location, location_type, viewport ๊ตฌ์กฐ๋กœ ์ด๋ฃจ์–ด์ ธ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, location์˜ lat, lng์˜ ์œ„๋„/๊ฒฝ๋„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

viewport๋Š” northeast(๋ถ๋™์ชฝ), southwest(๋‚จ์„œ์ชฝ) ์ขŒํ‘œ ์ •๋ณด๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ์ขŒํ‘œ๊ฐ€ ํ™”๋ฉด์— ๋ณด์—ฌ์ค„ ๋•Œ์˜ ์ ์ ˆํ•œ ๋ฒ”์œ„๋ฅผ ์ œ์•ˆํ•˜๋Š” ์‚ฌ๊ฐํ˜• ์˜์—ญ์˜ ๋ฐ์ดํ„ฐ์ด๋‹ค. ์•ž์„œ Geocoding API ์˜ค๋ฒ„๋ทฐ์—์„œ ํ™•์ธํ•ด ๋ณด๋Š” ์‚ฌ๊ฐํ˜• ๋ฐ•์Šค์˜ ์˜์—ญ์ด๋ผ๊ณ  ๋ณด์‹œ๋ฉด ๋œ๋‹ค.

์ถ”๊ฐ€๋กœ ๊ฒฝ์šฐ์— ๋”ฐ๋ผ์„œ bounds๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋Š”๋ฐ, ํ–‰์ •๊ตฌ์—ญ์ด๋‚˜ ์šฐํŽธ๋ฒˆํ˜ธ, ํŠน์ • ๊ตฌ์—ญ์ฒ˜๋Ÿผ ๋ฒ”์œ„๋ฅผ ๊ฐ€์ง€๋Š” ๊ฒฐ๊ณผ์ผ ๊ฒฝ์šฐ ์‹ค์ œ ๊ฒฝ๊ณ„๋ฅผ northeast, southwest ์ขŒํ‘œ๋กœ ์ œ๊ณตํ•ด์ค€๋‹ค.
๊ฑด๋ฌผ์ด๋‚˜ ๋„๋กœ๋ช… ์ฃผ์†Œ์ฒ˜๋Ÿผ ํŠน์ • ์ง€์ (point)์„ ๋‚˜ํƒ€๋‚ด๋Š” ๊ฒฐ๊ณผ์—๋Š” ํฌํ•จ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์˜ต์…”๋„ํ•˜๊ฒŒ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ์ •ํ™•๋„ ์ˆ˜์ค€์„ location_type์œผ๋กœ ๋ฐ˜ํ™˜ํ•ด ์ฃผ๋Š”๋ฐ, ํ•ด๋‹น ํƒ€์ž…๋“ค์— ๋Œ€ํ•ด์„œ๋Š” ๊ณต์‹๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜์‹œ๋Š” ๊ฒƒ์„ ์ถ”์ฒœ๋“œ๋ฆฌ๋ฉฐ ๋Œ€์ฒด์ ์œผ๋กœ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š” ํƒ€์ž…์€ ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

  • ROOFTOP: ๊ฑด๋ฌผ ์ง€๋ถ• ๋‹จ์œ„๊นŒ์ง€ ์ •ํ™•(๊ฐ€์žฅ ์‹ ๋ขฐ๋„๊ฐ€ ๋†’์Œ)
  • RANGE_INTERPOLATED: ๋„๋กœ ๊ตฌ๊ฐ„ ๋‚ด์—์„œ ๋ณด๊ฐ„๋œ ์ขŒํ‘œ
  • GEOMETRIC_CENTER: ๋„๋กœ/์žฅ์†Œ์˜ ์ค‘์‹ฌ์ 
  • APPROXIMATE: ๋Œ€๋žต์ ์ธ ์œ„์น˜

๋‚˜๋จธ์ง€ ํ•„๋“œ์— ๋Œ€ํ•ด์„œ๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ๋งŒ ์‚ดํŽด๋ณด๋„๋ก ํ•˜์ž.

address_components๋Š” ์ฃผ์†Œ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ์š”์†Œ๋“ค์„ ๋ฐฐ์—ด๋กœ ์ œ๊ณตํ•˜๋Š” ํ•„๋“œ์ด๋‹ค. ๊ฐ ์š”์†Œ๋Š” longname(์ „์ฒด ์ด๋ฆ„), short_name(์ถ•์•ฝ/์ฝ”๋“œ), types(ํ•ด๋‹น ์š”์†Œ์˜ ์—ญํ• /๋‹จ๊ณ„) ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ด ํ–‰์ •๊ตฌ์—ญ์˜ ๊ณ„์ธต ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
์˜ˆ๋ฅผ ๋“ค์–ด, "administrative_area_level_1"์€ ๊ด‘์—ญ์‹œยท๋„ ๋‹จ์œ„, "sublocality_level_1"์€ ๊ตฌ ๋‹จ์œ„, "premise"๋Š” ๊ฑด๋ฌผ ๋ฒˆํ˜ธ์ฒ˜๋Ÿผ ์„ธ๋ถ€์ ์ธ ๋‹จ์œ„๋ฅผ ์˜๋ฏธํ•œ๋‹ค.
๋”ฐ๋ผ์„œ _types
์ •๋ณด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ฃผ์†Œ๋ฅผ ์‹œ/๋„/๊ตฌ/๋™ ๋‹จ์œ„๋กœ ์†์‰ฝ๊ฒŒ ๋ถ„๋ฆฌํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋˜ํ•œ place_id๋Š” Google Maps์—์„œ ์žฅ์†Œ๋ฅผ ์‹๋ณ„ํ•˜๋Š” ๊ณ ์œ ํ•œ ID๋ฅผ ๋‹ด๊ณ  ์žˆ์–ด, ๋™์ผํ•œ ์žฅ์†Œ๋ฅผ ์žฌ์กฐํšŒํ•˜๊ฑฐ๋‚˜ ๋‹ค๋ฅธ API์™€ ์—ฐ๋™ํ•  ๋•Œ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

์ตœ์ƒ์œ„ types ํ•„๋“œ๋Š” ํ•ด๋‹น ๊ฒฐ๊ณผ์˜ ์œ ํ˜•์„ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ๋„๋กœ๋ช… ์ฃผ์†Œ์ธ์ง€(street_address), ํ–‰์ •๊ตฌ์—ญ์ธ์ง€(administrative_area_level_1), ์šฐํŽธ๋ฒˆํ˜ธ์ธ์ง€(postal_code) ๋“ฑ์„ ์•Œ๋ ค์ฃผ์–ด ๊ฒฐ๊ณผ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ฑฐ๋‚˜ ์ƒํ™ฉ์— ๋งž๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.

types์— ์†ํ•˜๋Š” ์ •ํ™•ํ•œ ํƒ€์ž…๋“ค์€ ๊ณต์‹๋ฌธ์„œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ plus_code๋Š” Google์ด ์ œ๊ณตํ•˜๋Š” Plus Codes ์ •๋ณด๋กœ, ์ฃผ์†Œ๊ฐ€ ์—†๋Š” ๊ณณ๋„ ์ขŒํ‘œ ๊ธฐ๋ฐ˜ ์ฝ”๋“œ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์œผ๋กœ ์‚ฌ๋ง‰์ด๋‚˜ ๋ฐ”๋‹ค ํ•œ๊ฐ€์šด๋ฐ์ฒ˜๋Ÿผ ๊ธฐ์กด์˜ ๋„๋กœ๋ช… ์ฃผ์†Œ๋‚˜ ํ–‰์ • ๊ตฌ์—ญ์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ณณ๋„ ์œ„๋„ยท๊ฒฝ๋„๋ฅผ ๊ณ ์œ ํ•œ ์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•ด ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Plus Codes๋Š” ์ „ ์„ธ๊ณ„ ์–ด๋””์„œ๋“  ๊ณ ์œ ํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ global_code์™€ ํŠน์ • ์ง€์—ญ ๋งฅ๋ฝ์—์„œ ์งง๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋Š” compound_code ๋‘ ๊ฐ€์ง€ ํ˜•ํƒœ๋ฅผ ์ œ๊ณตํ•œ๋‹ค.
๋”ฐ๋ผ์„œ plus_code๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ฃผ์†Œ ์ฒด๊ณ„๊ฐ€ ์—†๋Š” ์œ„์น˜๋„ ์†์‰ฝ๊ฒŒ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜ ์ง€๋„์—์„œ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

์ฐธ๊ณ ๋กœ ์—ญ์ง€์˜ค์ฝ”๋”ฉ(ReverseGeocoding)์—์„œ๋Š” results ํ•„๋“œ๋‚ด ๋ฐฐ์—ด ์™ธ์—๋„ ์ตœ์ƒ์œ„ ํ•„๋“œ์—์„œ ๋ณ„๋„๋กœ plus_code๋ฅผ ํฌํ•จํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ Google Maps์—์„œ ์ œ๊ณตํ•˜๋Š” Geocoding API๋ฅผ ์‚ฌ์š”ํ•˜์—ฌ ์ง€์˜ค์ฝ”๋”ฉ(Geocoding)/์—ญ์ง€์˜ค์ฝ”๋”ฉ(ReverseGeocoding) ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋‹ค.

Geocoding API๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์†์‰ฝ๊ฒŒ ๋ณ€ํ™˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ ์™ธ์—๋„ ๋””ํ…Œ์ผํ•œ ๋ฐ์ดํ„ฐ๋“ค๋„ ํ•จ๊ป˜ ์ œ๊ณต๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๋ฉ”๋ฆฌํŠธ๊ฐ€ ์žˆ์ง€๋งŒ ๋‹น์—ฐํžˆ ๋ฌด๋ฃŒ๋Š” ์•„๋‹ˆ๋ฉฐ ์ผ์ •๋Ÿ‰์„ ์ดˆ๊ณผํ•˜๋ฉด ๊ณผ๊ธˆ์ด ๋ฐœ์ƒํ•œ๋‹ค(์›”๋ณ„ ๋ฌด๋ฃŒ ์‚ฌ์šฉ๋Ÿ‰์€ ์ œ๊ณต).

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” Google Maps๋ฅผ ํ™œ์šฉํ•œ ์ง€๋„ ์„œ๋น„์Šค ๋งฅ๋ฝ์—์„œ Geocoding API๋ฅผ ์†Œ๊ฐœํ•˜์˜€์ง€๋งŒ, ๊ตฌ๊ธ€์ด ์•„๋‹ˆ๋”๋ผ๋„ ๋‹ค์–‘ํ•œ ์„œ๋น„์Šค์—์„œ Geocoding ๊ธฐ๋Šฅ์„ ์ œํ•œ์ ์œผ๋กœ ๋ฌด๋ฃŒ๋กœ ์ œ๊ณตํ•˜๊ธฐ๋„ ํ•˜๋‹ˆ Geocoding API๋ฅผ ์‹ค์ œ ์„œ๋น„์Šค์— ์ ์šฉ ํ•˜์‹ ๋‹ค๋ฉด ๋ฐ˜๋“œ์‹œ ์š”๊ธˆ ์ •์ฑ…์„ ํ™•์ธํ•˜๊ณ  ๋น„์šฉ์„ ๊ณ ๋ คํ•ด์•ผ ํ•œ๋‹ค.


Flutter๋กœ ๋ชจ๋ฐ”์ผ ํ”Œ๋žซํผ(iOS/Android)๋งŒ ๊ฐœ๋ฐœํ•˜๋Š” ์ƒํ™ฉ์—์„œ๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋„ค์ดํ‹ฐ๋ธŒ API๋กœ ์ด๋ฏธ ๋‚ด์žฅ๋˜์–ด ์žˆ๋Š” Geoding ์„œ๋น„์Šค๋ฅผ ๊ทธ๋Œ€๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, Android์˜ Geocoder์™€ iOS์˜ CLGeocoder๋ฅผ ํ˜ธ์ถœํ•˜๋Š” geocoding ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋ฌด๋ฃŒ๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‹ค๋งŒ ์›น์„ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฉฐ, OS ๋„ค์ดํ‹ฐ๋ธŒ API์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์งง์€ ์‹œ๊ฐ„์— ๊ณผ๋„ํ•œ ํ˜ธ์ถœ์„ ํ•˜๋ฉด ์š”์ฒญ ์ œํ•œ(Rate Limit)์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ๊ณ  ํŠน์ • ๊ตญ๊ฐ€์—์„œ๋Š” ์„œ๋น„์Šค์— ์ œํ•œ์„ ๋ฐ›์„ ์ˆ˜๋„ ์žˆ๋‹ค.

Geocode.xyz๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” geocode ํŒจํ‚ค์ง€๋Š” ๋ชจ๋ฐ”์ผ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์›น์—์„œ๋„ 1์ดˆ๋‹น 1๊ฑด์˜ ์ œํ•œ์œผ๋กœ๋Š” ๋ฌด๋ฃŒ๋กœ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ, ์œ ๋ฃŒ ํ”Œ๋žœ๋„ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋‹ค.

์†Œ๊ฐœํ•œ Google Geocoding API์™€ geocoding, geocode ํŒจํ‚ค์ง€ ์™ธ์—๋„ ์ง€์˜ค์ฝ”๋”ฉ/์—ญ์ง€์˜ค์ฝ”๋”ฉ์„ ์ œ๊ณตํ•˜๋Š” ์„œ๋น„์Šค๋“ค์ด ๋งŽ์ด ์žˆ์œผ๋ฏ€๋กœ ๋ณธ์ธ์˜ ์„œ๋น„์Šค์— ๊ฐ€์žฅ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” ์„œ๋น„์Šค๋ฅผ ์„ ํƒํ•ด์„œ ์‚ฌ์šฉํ•˜์‹œ๋ฉด ๋œ๋‹ค.

๋งˆ๋ฌด๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ Google Maps Platform์˜ API ํ‚ค๋ฅผ ๋ฐœ๊ธ‰๋ฐ›์•„ Flutter์—์„œ ๊ตฌ๊ธ€ ๋งต์„ ๋„์šฐ๊ณ , ์ง€๋„ ์‚ฌ์šฉ์„ ์œ„ํ•œ ๊ธฐ๋ณธ ์˜ต์…˜ ์ •๋ณด ๋ฐ Marker/Circle/Polyline/Polygon ๋“ฑ์˜ ์˜ค๋ฒ„๋ ˆ์ด(Overlay) ๊ธฐ๋Šฅ๊ณผ ์ง€๋„๋ฅผ ์ œ์–ดํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฉ๋ฒ•๋“ค์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋‹ค.

๋˜ํ•œ ์ง€๋„๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฐ์— ํ•ต์‹ฌ ๊ธฐ๋Šฅ์ธ ์ง€์˜ค์ฝ”๋”ฉ(Geocoding)๊ณผ ์—ญ์ง€์˜ค์ฝ”๋”ฉ(Reverse Geocoding)์„ Geocoding API๋ฅผ ํ†ตํ•ด ์–ด๋–ป๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š”์ง€๋„ ์‚ดํŽด๋ณด์•˜๋‹ค. ์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๋‹จ์ˆœํžˆ ์œ„๋„/๊ฒฝ๋„ ์ขŒํ‘œ๋งŒ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์‚ฌ์šฉ์ž๊ฐ€ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์†Œ ๋ฐ์ดํ„ฐ์™€์˜ ์—ฐ๊ฒฐ์ด ๊ฐ€๋Šฅํ•ด์ ธ ์œ„์น˜ ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค์˜ ํ™œ์šฉ๋„๊ฐ€ ํฌ๊ฒŒ ๋†’์•„์ง„๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.

Google Maps Platform์˜ ๊ธฐ๋Šฅ์„ ์ตœ๋Œ€ํ•œ ์ด๋ฒˆ ๊ธ€์—์„œ ๋‹ค๋ฃจ๋ ค๊ณ  ํ•˜์˜€์ง€๋งŒ, ๋‚ด์šฉ๋„ ๋„ˆ๋ฌด ๊ธธ์–ด์ ธ ์›ํ•˜๋Š” ์œ„์น˜์˜ ์žฅ์†Œ๋ฅผ ๊ฒ€์ƒ‰ํ•ด ์ฃผ๋Š” Places API, ๋‘ ์ง€์ ์˜ ์ตœ์  ๊ฒฝ๋กœ๋ฅผ ํƒ์ƒ‰ํ•ด ์ฃผ๋Š” Directions API์— ๋Œ€ํ•ด์„œ๋Š” ๋ณ„๋„๋กœ ๋‹ค๋ค„๋ณผ ์˜ˆ์ •์ด๊ณ , ๊ฐ API๋“ค์˜ ๊ธฐ๋Šฅ์„ ์ด๋ฒˆ ๊ธ€์—์„œ ์‚ดํŽด๋ณธ ์˜ค๋ฒ„๋ ˆ์ด(Overlay) ๊ธฐ๋Šฅ๋“ค๊ณผ ๊ฒฐํ•ฉํ•˜์—ฌ ์ข€ ๋” ์‹ค์šฉ์ ์ธ UI๋ฅผ ๊ตฌํ˜„ํ•˜์—ฌ ์†Œ๊ฐœํ•ด ๋ณด๋„๋ก ํ•˜๊ฒ ๋‹ค.

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค โ—๏ธ

profile
Flutter Developer

0๊ฐœ์˜ ๋Œ“๊ธ€