https://www.youtube.com/watch?v=3yXT-jNL8jA
description: |
1. 이건 꼭 알아야 한다^1
[? 질문] Flutter 앱에서 구글 맵을 “그냥 위젯 하나 띄우는 수준”이 아니라, 실제 서비스처럼 안정적으로 붙이려면 무엇을 먼저/반드시 해야 하는가^2
[= 답] Google Cloud Console에서 프로젝트 생성 → Maps SDK(플랫폼별) 사용 설정 → API 키 생성/복사 → Android/iOS 각 OS별 설정 파일에 키를 정확히 주입 → Flutter 코드에서 GoogleMap 위젯과 initialCameraPosition(카메라/뷰포트)를 제공하는 순서로 진행해야 한다.[^3]
[? 질문] 이 강의에서 만들려는 앱의 “기능 목표(요구사항)”는 무엇인가[^4]
[= 답] 사용자가 길을 가다 “기억해두고 싶은 장소”를 발견했을 때, 위도/경도 + 사진 + 제목/설명을 저장하고, 이를 CRUD(생성/조회/수정/삭제) 하며, 저장된 위경도로 지도(구글맵)에서 위치를 다시 보여주는 “장소 기록 앱”을 만든다.[^5]
[? 질문] 구글 맵은 유료라는데, 학습/초기 개발 단계에서 왜 ‘크게 걱정하지 말라’고 하는가[^6]
[= 답] 구글 맵은 할당량(쿼터) 기반 과금이지만, 일정 수준까지는 무료 구간이 충분히 크고, 만약 일일 접속이 “몇십만” 단위를 넘어 유료로 전환될 정도라면 이미 서비스가 성장해 비용을 감당할 상황일 가능성이 높다고 설명한다.[^7]
2. 큰 그림[^8]
이 콘텐츠는 Flutter 중급 단계에서 Google Maps 연동을 실제 앱 프로젝트에 통합하는 과정을 다룬다.[^9] 단순히 패키지 추가로 끝나지 않고, Google Cloud Console 설정, API 키 발급, Android/iOS 플랫폼별 환경설정 파일 수정, 그리고 Flutter 코드에서 지도를 띄우기 위한 카메라(뷰포트) 설정까지 “실무에서 막히는 지점”을 순서대로 밟는다.[^10] 또한 앞서 배운 SQLite(sqflite) 및 카메라 연동까지 결합해 “장소 기록 + 지도 표시” 앱으로 확장하려는 맥락을 제시한다.[^11]
- 플랫폼별 설정의 현실: Google Maps는 설정이 자주 바뀌고(Android/iOS도 다름), 책만 보고 따라 하기 어려운 “고개”가 많다고 강조한다.[^12]
- API 키/콘솔 세팅이 핵심 병목: 코드보다 콘솔에서 프로젝트/SDK 사용 설정/키 발급/키 제한이 먼저이며, 여기서 실수하면 지도가 안 뜨는 문제가 생긴다.[^13]
- 지도 표시의 최소 요건: Flutter에서
GoogleMap을 띄우려면initialCameraPosition이 필수이며, 이는 “어디를 어떤 확대 수준으로 볼지(위도/경도+줌)”를 의미한다.[^14]
3. 하나씩 살펴보기[^15]
3.1 오늘 프로젝트가 다루는 것: 지도 연동의 필요, 무료/유료, 그리고 ‘변하는 설정’[^16]
강의는 “이번 프로젝트는 무엇을 하느냐”로 시작하며, 지도 연동(구글 맵 중심) 을 Flutter 앱에 붙여보겠다고 선언한다.[^17] 지도 연동은 현실적으로 네이버/다음/구글 등 다양한 선택지가 있는데, 간단한 수준이라면 네이버/다음도 가능하다고 말한다.[^18] 다만 “아주 대용량” 혹은 접속량이 큰 상황에서는 서비스마다 정책 차이가 있는데, 특히 구글 맵은 기본적으로 유료(과금 모델) 이라고 짚는다.[^19]
여기서 “유료면 부담 아닌가?”라는 학습자의 불안을 예상하고 다음 논리를 제시한다.[^20]
- 구글은 유료이지만, 무료로 쓸 수 있는 범위가 매우 크다.[^21]
- 일정 수준을 넘어서야 유료화되며, 그 수준이 “몇십만” 단위라면, 그쯤 되면 이미 서비스가 성공해 비용을 감당할 가능성이 높다는 식으로 설명한다.[^22]
- 따라서 학습/프로토타이핑 단계에서는 “크게 걱정하지 말라”는 결론으로 정리한다.[^23]
또한 중요한 배경 설명으로, 구글 맵 연동은 계속 바뀌는 영역이라고 강조한다.[^24] 구글 측 콘솔/설정 화면이 바뀌어 책의 절차가 금방 낡고, 설정을 건드려야 하는 지점이 여러 군데라 “혼자 책 보고 하면 시행착오가 많다”고 말한다.[^25] 이 말은 곧, 강의에서 “실제로 어디를 눌러야 하는지”를 보여주며 따라가게 하겠다는 학습 설계로 이어진다.[^26]
[!IMPORTANT] 설정이 ‘자주 바뀌는’ 영역이라는 경고
구글 맵은 콘솔 UI와 설정 방식이 변동되어, 정적인 문서/책만으로는 그대로 재현이 어려울 수 있으니 “어디를 눌러야 하는지” 흐름을 이해하고 따라가라고 한다.[^24]
3.2 앱 요구사항 정의: 장소 기록(위도/경도+사진+텍스트)과 CRUD, 그리고 지도 표시[^27]
강의는 “단순히 지도만 띄우고 끝내지 않겠다”는 방향을 잡는다.[^28] 바로 이전 프로젝트에서 배운 SQLite(sqflite) 를 그대로 활용해, 실제로 쓸 만한 응용 앱을 만들겠다고 한다.[^29]
여기서 앱의 원래 목적(시나리오)을 다음처럼 제시한다.[^30]
- 사용자가 길을 가다가 “좋은 장소”를 발견했을 때 “메모해 두고 싶다”.[^31]
- 그 장소 기록에는 최소한 다음 정보가 필요하다:
- 위도(latitude)와 경도(longitude)[^32]
- 사진(카메라로 촬영/연동)[^33]
- 간단한 제목 또는 설명 텍스트[^34]
- 이 데이터를 대상으로 CRUD(Create/Read/Update/Delete) 기능을 구현한다.[^35]
- 그리고 저장된 위도/경도를 사용해 나중에 지도와 연동하여 위치를 보여준다.[^36]
- 지도는 구글 지도를 쓸 것이며, 이를 위해 반드시 API 키가 필요하다고 못 박는다.[^37]
이 구성이 의미하는 바는, 단순 UI 데모가 아니라:
- 모바일 기기 기능(지도/카메라)
- 로컬 DB(SQLite)
를 합쳐 “현업형 미니 프로젝트”로 만드는 흐름이다.[^38]
3.3 Google Cloud Console vs Firebase 구분, 그리고 ‘키(Access key) 필요’의 맥락[^39]
구글 맵은 구글이 제공하는 서비스이므로 무한정 사용이 아니라 할당량(Quota) 기반으로 무료/유료가 갈린다고 다시 설명한다.[^40] 그리고 영화 API 때처럼, 구글 맵도 사용하려면 승인 키(Access key / API key) 를 발급받아 그 키로 앱을 개발해야 한다고 말한다.[^41]
이어서 학습자가 흔히 헷갈릴 수 있는 지점을 짚는다.[^42]
- “구글 클라우드”는 Firebase가 아니다.[^43]
- Firebase는 별도 법인/서비스로 분리되어 있고, 지금 다루는 맵 키 발급은 Google Cloud Console(콘솔 클라우드 구글 닷컴) 쪽에서 진행한다.[^44]
이 구분은 이후 단계(콘솔에서 프로젝트 만들고 API 활성화)를 정확히 따라가기 위한 전제다.[^45]
3.4 Flutter 프로젝트 생성 및 패키지 설치: google_maps_flutter 추가[^46]
강의는 Flutter 프로젝트를 하나 만들고 실행해본 다음, 외부 라이브러리를 먼저 장착하겠다고 한다.[^47] 패키지 추가는 늘 하던 방식대로 pub.dev에서 google_maps_flutter를 찾아 설치(버전 문자열 복사)하고, pubspec.yaml의 dependencies에 추가하는 흐름이다.[^48]
진행 포인트는 다음과 같다.[^49]
- pub.dev에서 google_maps_flutter를 검색한다.[^50]
- 설치 화면의 버전 표기를 복사해서
pubspec.yaml에 붙여 넣는다.[^51] - 의존성 추가 후(예: “get dependencies”) 라이브러리 장착을 확인한다.[^52]
- 강의자는 스펠링/자동 import가 꼬일 수 있으니, import 구문도 미리 복사해둘 수 있다고 팁을 준다(필요할 때 붙여넣기).[^^53]
[!TIP] import/문자열을 미리 복사해두는 습관
자동 import가 안 되거나 철자 실수로 시간을 쓰는 경우가 있으니, 패키지명/주요 구문을 메모해두면 효율적이라고 말한다.[^53]
3.5 Google Cloud Console에서 프로젝트 만들기 → Maps SDK 활성화[^54]
패키지를 추가했으면 이제 “가장 중요한” API 키 발급 단계로 넘어간다.[^55] 강의자는 구글 쪽(google … 콘솔)으로 들어가 “개발자 콘솔”에서 작업한다고 안내한다.[^56]
콘솔에서의 흐름은 다음처럼 설명된다.[^57]
- 로그인을 먼저 한다.[^58]
- 상단의 프로젝트 영역에서 새 프로젝트를 만든다.[^59]
- 프로젝트 이름은 임의로 지정 가능(예: “맵테스트”).[^60]
- “만들기”를 누르면 프로젝트가 생성된다.[^61]
- 생성된 프로젝트에서 API 및 서비스로 들어간다.[^62]
- 라이브러리로 들어가면 구글이 제공하는 여러 서비스 목록이 보인다.[^63]
- 이 서비스들은 사용량에 따라 과금될 수 있으며, 보통 결제정보/인증을 나중에 붙이지만 처음 시작 시엔 없어도 된다고 언급한다.[^64]
- 검색창에 map(맵)을 검색한다.[^65]
- 여기서 중요한 것이 Maps SDK for Android, 그리고 iOS도 аналог하게 활성화해야 한다는 점이다.[^66]
- 강의자는 “이것(안드로이드)과 이것을 쓸 겁니다”라고 하며 Android용 SDK를 눌러 사용(Enable) 을 활성화하면, 시간이 지난 뒤 “관리(Manage)”로 바뀌고 “API 사용 설정됨” 상태가 된다고 설명한다.[^67]
이 단계는 “키 발급” 전의 선행 조건으로 제시된다.[^68]
3.6 API 키 생성/복사, 키 제한(앱 제한/API 제한) 이해, 키 재생성 주의사항[^69]
SDK를 활성화한 뒤, 키 및 사용자 인증 정보(credential) 메뉴로 이동해 API 키를 만든다.[^70] 강의자는 이미 키를 만들어 둔 상태라 화면이 다를 수 있지만, 처음 하는 사람은 “키 생성”이 보일 것이며 이를 눌러 키를 하나 만들면 된다고 안내한다.[^71]
생성한 뒤에는 다음을 강조한다.[^72]
- 키를 반드시 복사해두라.[^73]
- 나중에 지도 요청/표시 때마다 이 키를 사용하게 되고, 사용량 과금도 이 키를 기준으로 집계된다고 설명한다.[^74]
이어서 “키 제한(Restriction)” 옵션을 설명한다.[^75]
- 애플리케이션 제한 설정:
- 웹사이트, 특정 IP, Android 앱, iOS 앱 등 “어디에서 키를 쓸지”를 제한할 수 있다.[^76]
- “없음”으로 하면 제한 없이 다 쓸 수 있다는 의미다.[^77]
- 강의자는 보통 단일 Android 앱 수준이라면 “없음”으로 둬도 된다고 말한다.[^78]
- API 제한 사항:
- 특정 API에서만 키를 쓰게 제한하는 옵션(Select API)임을 언급한다.[^79]
- 이번 실습에서는 “딱히 제한 없이” 공통으로 쓰겠다는 뉘앙스로 설명한다.[^80]
- 키 재생성:
- 키를 새로 만들면 새 키는 즉시 사용 가능하지만, 기존 키는 24시간 이내에 영구적으로 비활성화된다고 설명한다.[^81]
- 즉, 잠깐 동안 키가 두 개가 공존하는 상황이 발생한다.[^82]
- 강의자의 강의용 키는 강의 후 초기화할 예정이라, 시청자가 그대로 쓰기는 어렵다는 점도 말한다.[^83]
또한 키를 코드/소스 어딘가에 임시로 붙여넣어둘 수는 있지만, “나중에 지워야 한다”는 주의도 준다.[^84]
[!WARNING] 키 유출/하드코딩 주의(강의자의 경고 맥락)
키를 일단 복사해 소스 어딘가에 담아두되, “임시”이며 나중에 지우라고 말한다. 강의자는 자신의 강의용 키는 강의 후 초기화한다고도 언급한다.[^83]
3.7 “지도 안 뜰 때” 체크포인트: 콘솔에서 Enable/Disable 상태 확인[^85]
강의자는 설정 화면이 바뀌며, 나중에 “별 실수한 것 같지 않은데 지도가 안 나오는” 상황이 생길 수 있다고 한다.[^86] 그때 확인할 포인트로, 라이브러리에서 해당 SDK가 Enable(사용 설정) 되어 있는지 확인하라고 안내한다.[^87]
- 화면에 “Enable/Disable” 표시가 있을 수 있는데, Enable이 보이면 현재 Disable 상태이므로 Enable을 눌러야 한다는 식으로 설명한다.[^88]
- Android 쪽 Maps SDK 설정 화면에서 그런 토글을 확인하라고 말한다.[^89]
이 부분은 “콘솔 설정이 바뀌어도 본질은 ‘활성화 여부’ 확인”이라는 문제 해결 안내로 기능한다.[^90]
3.8 플랫폼별 키 주입: Flutter 공통 코드가 아니라 Android/iOS 디렉토리를 수정해야 한다[^91]
이제부터가 “골치 아픈” 구간이라고 말한다.[^92] 이유는 이 작업이 Flutter(Dart) 코드로만 끝나는 게 아니라, 각 플랫폼의 환경 설정 파일에 키를 넣어야 하기 때문이다.[^93]
강의자는 Flutter 프로젝트 구조를 보여주며 다음 원칙을 설명한다.[^94]
- Flutter의 대부분 로직은 공통 디렉토리(다트 코드)에서 작업한다.[^95]
- 하지만 권한/기기 승인/플랫폼 전용 설정은 OS별 디렉토리에서 별도로 한다.[^96]
- Android 설정은
android/아래에서[^97] - iOS 설정은
ios/아래에서[^98] - macOS, web, windows도 각자 디렉토리에서 환경설정을 한다는 식으로 확장 설명한다.[^99]
- Android 설정은
이 설명은 “왜 내가 Dart 코드를 고쳤는데도 동작이 안 되지?” 같은 혼란을 방지하기 위한 맥락 제공이다.[^100]
3.9 Android 설정: AndroidManifest.xml에 메타데이터로 API 키 넣기[^101]
Android에서 API 키를 넣는 위치를 구체 경로로 안내한다.[^102]
android/app/src/main/AndroidManifest.xml파일을 연다.[^103]<application>태그 아래에meta-data항목을 추가한다.[^104]- 이 메타데이터는 XML 규격이며, 다음을 넣는다고 설명한다:
android:name에com.google.android.geo.API_KEY(강의 발화상 “Android geoapi 키”로 읽어줌) 형태의 키 이름[^105]android:value에 방금 복사한 API 키 값[^106]
- 태그를 닫아 한 세트로 완성하면 Android 쪽 키 세팅이 “일단 오케이”라고 말한다.[^107]
여기서 강의자는 “Android는 간단하다”는 톤으로 iOS 대비를 예고한다.[^108]
3.10 iOS 설정(1): AppDelegate.swift에 GMSServices.provideAPIKey(...) + import GoogleMaps[^109]
iOS는 ios/Runner/ 쪽을 수정한다고 말하며, 주로 손대는 파일이 두 가지라고 언급한 뒤, 키 값은 AppDelegate.swift 에 넣는다고 한다.[^110]
iOS 키 주입 방식은 다음과 같이 안내된다.[^111]
AppDelegate.swift에서 (강의에서는 “불 바로 밑”이라고 지칭) 다음 호출을 추가:GMSServices.provideAPIKey("여기에 키")[^112]
- 그리고 기본 제공이 아니므로 라이브러리를 불러와야 한다며:
import GoogleMaps를 추가하라고 한다.[^113]
즉, iOS는 “키를 넣는 호출 + import” 두 가지를 해야 한다는 점이 강조된다.[^114]
3.11 iOS 설정(2): Info.plist에 Flutter 관련 환경 변수 추가[^115]
다음으로 iOS의 Info.plist 에서 제일 아래로 내려가 환경 변수를 하나 추가해야 한다고 한다.[^116] 이는 “아이폰의 환경 변수” 중 하나를 추가하는 것으로 설명된다.[^117]
강의에서 지시하는 키/값은 다음 구조로 제시된다.[^118]
key(키 이름):io.flutter.embedded_views_preview(강의 발화는IO 플루터 점 ... 임베디드 ... 뷰 ... 프리뷰형태로 읽음)[^119]
value(값):true[^120]
강의자는 지금까지 한 설정을 정리하며 다음 매핑을 다시 확인시킨다.[^121]
- Android:
AndroidManifest.xml에 메타데이터로 키 설정[^122] - iOS:
AppDelegate.swift에 키 제공 호출 +Info.plist에 위 환경 변수 추가[^123]
3.12 Android/iOS SDK “사용 설정됨” 재확인: 둘 다 켜져 있어야 한다[^124]
설정을 끝낸 뒤 강의자는 다시 콘솔로 돌아가 확인할 것을 요구한다.[^125]
- Maps SDK for Android: 관리 화면에서 “API 사용 설정됨”인지 확인[^126]
- Maps SDK for iOS: 마찬가지로 iOS용도 “사용(Enable)” 상태인지 확인[^127]
결론은 “양쪽 다 켜져 있어야 양쪽에서 쓸 수 있다”는 것이다.[^128]
3.13 Flutter 코드에서 지도 띄우기: GoogleMap 위젯 배치와 기본 Scaffold 구성[^129]
이제 “진짜로 맵을 불러오자”며 소스 코드로 돌아온다.[^130] 메인 화면을 단순화하고, StatefulWidget 기반의 MyHomePage를 만들고, Scaffold를 구성한다.[^131]
AppBar타이틀을 “보물지도”라고 설정한다.[^132]body에서 지도를 띄우려면GoogleMap(...)을 배치하면 된다고 말하며 “너무 간단하죠”라고 표현한다.[^133]- 다만 실제로는 바로 에러가 나는데, 그 이유는
GoogleMap에 필수 파라미터인 카메라(초기 카메라 위치) 를 넣지 않았기 때문이라고 이어진다.[^134]
또한 화면 구성상 GoogleMap을 Container로 감싸보는 시도를 한다.[^135]
3.14 필수 개념: initialCameraPosition과 카메라(뷰포트) 비유[^136]
코드에서 에러가 뜨자, 강의자는 “카메라” 개념을 설명한다.[^137]
- 지도는 있지만, 사용자가 처음 보게 될 영역을 마치 망원경/카메라로 어느 부분을 바라볼지 정해야 한다는 비유를 든다.[^138]
- 이 “바라보는 영역(뷰포트)”을 지정하는 것이며, 이를
CameraPosition이라고 부른다고 설명한다.[^139]
따라서 해야 할 일은 다음이다.[^140]
CameraPosition변수를 하나 만들고[^141]- 그 안에:
target:LatLng(위도, 경도)형태로 위치를 지정[^142]zoom: 얼마나 확대해서 볼지를 지정[^143]
- 그리고
GoogleMap위젯의initialCameraPosition:에 이CameraPosition을 전달한다.[^144]
3.15 위도(latitude)/경도(longitude) 설명과 줌 레벨 감각(12 vs 14~15)[^145]
CameraPosition.target 타입을 따라가보면 LatLng를 받는다고 하며, 여기서 위도/경도를 설명한다.[^146]
- 위도는 영어로 latitude, 경도는 longitude라고 설명한다.[^147]
- 위도는 예로 “38선” 같은 개념을 들어 설명한다.[^148]
- “북극 쪽으로 올라가는” 방향의 각을 위도, 태평양을 기준으로 좌우로 왔다 갔다 하는 각을 경도라고 말한다.[^149]
zoom은 “얼마나 줌인할 것이냐”이며, 다음 감각을 제시한다:- 줌 12 정도: “한 9개 동(지역 단위)을 한꺼번에 보는” 꽤 넓은 범위[^150]
- 줌 14~15 정도: 골목길 등 상세하게 볼 때[^151]
- 실습에서는 줌을 12로 둔다.[^152]
3.16 테스트용 위경도 구하기: Google 지도에서 URL의 3D/4D 값 읽기[^153]
초기 위치(위경도)를 “임의로” 잡아야 하니, 테스트 방법을 보여준다고 한다.[^154] 여기서 강의자는 브라우저에서 구글 지도(google maps) 로 들어가 특정 장소(예: 서울역)를 검색한다.[^155]
그리고 “위경도 찾는 방법”으로 다음 요령을 설명한다.[^156]
- 구글 지도에서 원하는 장소를 검색한다(예: 서울역).[^157]
- 주소(URL)를 자세히 보면 경도가 숨어 있다고 말하며, URL을 복사해 텍스트로 붙여넣어 확인한다.[^158]
- URL 문자열 중에
3D...와4D...형태로 들어있는 숫자 값이 있는데, 이것이 위도/경도라고 설명한다.[^159]- 3D가 위도, 4D가 경도라고 짚는다.[^160]
- 순서는 위도를 먼저, 경도를 뒤에 넣어야 한다고 강조한다.[^161]
이 값들을 LatLng(latitude, longitude)에 넣어 초기 위치로 사용한다는 것이다.[^162]
+++ 상세 예시(강의 흐름 재현)
- 구글 지도에서 “서울역” 검색 → 결과 위치가 잡힘.[^155]
- 주소(URL) 복사 → 문자열에서
3D가 붙은 값과4D가 붙은 값을 찾음.[^159] LatLng(3D값, 4D값)순으로 입력.[^161]
+++
3.17 GoogleMap(initialCameraPosition: ...)로 연결, 실행 시 에러 발생까지[^163]
위에서 만든 CameraPosition을 GoogleMap에 넣으려는 과정에서, 강의자는 “그냥 바로 쓰면 안 되고 initialCameraPosition: 네임드 파라미터로 넣어야 한다”는 식으로 한 번 더 정리한다.[^164] 즉 최종적으로는:
GoogleMap(initialCameraPosition: position,
)
같은 형태로 넣어줘야 한다고 말한다.[^165]
그리고 “잘 되나 실행해보자”고 하며 컴파일 후 실행한다.[^166] 실행 결과 “뭔가 에러가 폭 떨어졌다”고 말하며, 화면 위로 올라가서 에러를 보겠다고 하는 지점에서 영상 제공 텍스트가 끝난다.[^167]
즉, 이 파트의 마지막은:
- 콘솔/키/플랫폼 설정 및 지도 위젯 코드까지는 넣었고[^168]
- 실행했더니 에러가 발생했으며, 다음 단계에서 에러 메시지를 기반으로 추가 설명/수정을 하려는 전개 직전에서 종료된다.[^169]
4. 핵심 통찰[^170]
- 구글 맵 연동의 난이도는 “위젯 사용법”보다 콘솔 설정 + 플랫폼별 환경설정에 더 크게 걸려 있다.[^171]
- Flutter는 공통 코드가 강점이지만, 지도/권한/키 같은 요소는 결국 Android/iOS 각각의 설정 파일을 건드려야 한다.[^172]
- “지도 안 뜸” 문제는 코드보다 먼저 Maps SDK 활성화(Enable) 여부와 API 키 주입 위치부터 의심해야 한다.[^173]
GoogleMap의 최소 요건은initialCameraPosition이며, 이는 “처음에 어디를 어떤 줌으로 볼지”를 결정하는 앱의 기본 UX다.[^174]- 위경도는 외부에서 가져오면 되는데, 강의는 구글 지도 URL의
3D/4D값에서 위도/경도 추출하는 실용적 방법을 제시한다.[^175]
- 실행 행동 항목(콘텐츠가 요구하는 체크리스트)
- 콘솔에서 프로젝트 생성 후 Maps SDK for Android / iOS를 각각 Enable 했는지 확인한다.[^176]
- API 키를 만든 뒤 복사해두고, AndroidManifest 및 iOS(AppDelegate, Info.plist)에 정확히 넣는다.[^177]
- Flutter 코드에서
GoogleMap을 쓸 때initialCameraPosition을 반드시 제공한다.[^178]
5. 헷갈리는 용어 정리 (해당 시에만)[^179]
CRUD: Create/Read/Update/Delete. 데이터(장소 기록)를 생성·조회·수정·삭제하는 기본 기능 묶음.[^180]
Quota(할당량): API를 무료로 사용할 수 있는 호출/사용량 한도. 한도를 넘으면 과금될 수 있다.[^181]
Google Cloud Console: 구글 API(맵 등)의 프로젝트 생성, API 활성화, 키(자격 증명) 발급을 하는 관리 콘솔.[^182]
Maps SDK for Android / iOS: 각 플랫폼에서 구글 맵을 사용하기 위해 콘솔에서 활성화해야 하는 API/SDK 항목.[^183]
API Key(승인 키/Access key): 구글 맵 API 사용을 인증하고 사용량 과금/통계를 연결하는 키.[^184]
CameraPosition: 지도를 어떤 위치(LatLng)를 중심으로 어느 확대(zoom)로 보여줄지 결정하는 “초기 뷰포트” 설정.[^185]
LatLng: 위도(latitude)와 경도(longitude)로 좌표를 표현하는 타입/구조.[^186]
zoom: 지도의 확대 수준. 값이 커질수록 더 확대(상세)된다.[^187]
AndroidManifest.xml: Android 앱의 권한/메타데이터/구성 정보를 담는 핵심 설정 파일. 구글 맵 키도 메타데이터로 주입한다.[^188]
AppDelegate.swift: iOS 앱 초기화 흐름에서 SDK 설정을 넣는 진입점 파일. 구글 맵 키 제공 호출을 넣는다.[^189]
Info.plist: iOS 앱의 환경 변수/설정 값을 담는 plist 파일. Flutter/임베디드 뷰 관련 키를 추가하라고 안내한다.[^190]
참고(콘텐츠 정보)[^191]
- 제목: 플러터 모바일 앱개발 | 모바일 앱 개발자를 위한 Flutter(플러터) 제대로 배우기 Part.4 중급3 구글 맵과 장소기록, 도서API 이용앱 | 취업·실무·창업 | 에어클래스[^192]
- 채널: 에어클래스[^193]
- 길이: 39분 28초[^194]
- 링크: https://www.youtube.com/watch?v=3yXT-jNL8jA[^195]
[^3]: @[10:48] "API 키를 구글에 가서 따야 됩니다" / @[23:27] AndroidManifest 경로 안내 / @[26:25] iOS AppDelegate에 provideAPIKey / @[38:50] initialCameraPosition 필요 [^4]: @[04:49] "원래 목적은... 장소 기록" [^5]: @[05:03] "장소 기록" / @[05:12] "위도와 경도" / @[05:18] "사진" / @[05:25] "제목이나 설명" / @[05:33] "crud" [^6]: @[01:06] "유료입니다" [^7]: @[01:09] "굉장히 많은 수까지 무료" / @[01:24] "몇십만... 넘어간다 하면" [^8]: @[01:49] "구글 맵 연동하는 거" / @[03:41] "SQL 라이트... 같이" / @[07:04] "카메라 연동" [^9]: @[03:57] "구글맵 플로터... 패키지" [^10]: @[02:24] "설정... 여러 군데를 건드려야" / @[03:13] "안드로이드 다르고 아이폰 다르고" [^11]: @[03:41] "SQ 라이트 배웠잖아요" / @[07:04] "카메라 연동" [^12]: @[01:57] "계속 바뀌어요" / @[02:13] "책하고 내용이 계속 바뀌니까" [^13]: @[07:33] "API 키 따는 거" / @[13:55] "요금... 결제 정보" [^14]: @[32:00] "이닛 카메라" 에러 / @[32:41] "뷰포트를 지정" / @[35:03] "줌" [^15]: @[07:39] "프로젝트를 하나 만들고... 들어가 보도록" [^16]: @[00:03] "구글 맵 연동" / @[01:57] "계속 바뀌어요" [^17]: @[01:49] "구글 맵 연동... 볼 거예요" [^18]: @[00:40] "간단한 거는 네이버나 다음" [^19]: @[01:06] "구글은... 유료" [^20]: @[01:09] "크게 걱정을 안 해도" [^21]: @[01:09] "굉장히 많은 수까지 무료" [^22]: @[01:24] "몇십만... 넘어간다 하면" [^23]: @[01:21] "넘어가야지 유료화... 크게 걱정 안 해도" [^24]: @[01:57] "계속 바뀌어요" [^25]: @[02:24] "여러 군데를 건드려야" / @[02:30] "시행 착오" [^26]: @[03:13] "안드로이드 다르고 아이폰 다르고... 오늘 다 배우려 그래요" [^27]: @[04:49] "장소 기록" / @[05:33] "crud" [^28]: @[03:35] "조금 더 단순하게... 않고" [^29]: @[03:41] "SQ 라이트... 응용 프로그램" [^30]: @[04:49] "원래 목적" [^31]: @[04:49] "길을 가다가... 메모" [^32]: @[05:07] "위도와 경도" [^33]: @[05:18] "사진" [^34]: @[05:25] "제목이나 설명" [^35]: @[05:33] "crud" [^36]: @[05:41] "위도와 경도로... 지도 연동" [^37]: @[05:58] "API 키" [^38]: @[03:41] SQLite + @[07:04] 카메라 + @[05:41] 지도연동 [^39]: @[06:22] "구글 클라우드... 파이어베이스 아니에요" [^40]: @[04:17] "무한적... 할당량" [^41]: @[04:27] "반드시... 어세스 키" [^42]: @[06:22] "구글 클라우드... 파이어베이스 아니에요" [^43]: @[06:22] "파이어베이스 아니에요" [^44]: @[06:32] "콘솔 클라우드 구글 닷컴" [^45]: @[07:33] "API 키 따는 거... 제일 먼저" [^46]: @[08:35] "외부 라이브러리... 장착" [^47]: @[07:39] "프로젝트... 만들고" / @[08:27] 실행 확인 [^48]: @[09:12] "구글 맵스 플러터" / @[09:55] pubspec에 추가 [^49]: @[09:24] "인스톨... 버전" / @[10:35] "패키지를 먼저" [^50]: @[09:01] "구글... 맵스" [^51]: @[09:33] "복사" [^52]: @[10:44] "라이브러리 장착" [^53]: @[10:06] "미리 복사를... 스펠링" [^54]: @[11:43] "프로젝트... 만들어 줘야" [^55]: @[07:33] "API 키... 제일 먼저" [^56]: @[10:52] "구글에 가서" / @[11:20] "개발자 콘솔" [^57]: @[11:47] 로그인 / @[11:51] 프로젝트 생성 / @[13:00] API 및 서비스 [^58]: @[11:43] "로그인을 먼저" [^59]: @[11:51] "프로젝트 부분" [^60]: @[12:14] "아무거나... 맵테스트" [^61]: @[12:23] "만들기" [^62]: @[12:56] "API 및 서비스" [^63]: @[13:26] "라이브러리" [^64]: @[13:55] "과금... 결제 정보... 처음엔 없어도" [^65]: @[14:02] "맵이라고" [^66]: @[14:25] "SDK... 안드로이드" / @[29:26] iOS도 확인 [^67]: @[14:39] "사용... 관리... API 사용 설정됨" [^68]: @[04:38] "키값... 받아야" [^69]: @[15:50] "키 및 사용자 인증 정보" / @[16:36] "키 생성" [^70]: @[15:50] "키 및 사용자 인증 정보" [^71]: @[16:36] "키 생성" [^72]: @[16:50] "복사" [^73]: @[16:50] "복사" [^74]: @[16:59] "사용량이 과금" [^75]: @[17:05] "애플리케이션 제한" [^76]: @[17:14] "웹사이트... 특정 IP... 안드로이드... 아이폰" [^77]: @[17:22] "없음이라 그러면 다 쓴다" [^78]: @[17:29] "없음으로 하시면" [^79]: @[17:46] "API 제한 사항" [^80]: @[18:05] "딱히 제한 없이" [^81]: @[18:24] "새키... 현재 키는 24시간" [^82]: @[18:33] "키가 잠깐 동안 두 개" [^83]: @[18:44] "강의 끝난 다음에... 초기" [^84]: @[19:30] "임시로... 나중에 지우셔야" [^85]: @[20:26] "네이블 디에이블" / @[21:20] "이네이블 눌러" [^86]: @[21:22] "별 크게 실수... 지도 안" [^87]: @[20:41] "이네이블... 눌러" [^88]: @[21:14] "이네이블 되어 있으면... 디스에이블" [^89]: @[20:46] "안드로이드... 이네이블" [^90]: @[21:26] "이쪽으로 와서 네이블" [^91]: @[22:12] "특정 환경... OS 디렉토리" [^92]: @[21:37] "골체 아파요" [^93]: @[21:42] "코드로... 환경 설정" [^94]: @[22:06] "디렉토리" 설명 [^95]: @[22:00] "소스 코드를 공통" [^96]: @[22:25] "권한 설정... 안드로이드 다르고" [^97]: @[22:29] "안드로이드는... 여기" [^98]: @[22:41] "iOS... 여기서" [^99]: @[22:52] "맥... 웹... 윈도우즈" [^100]: @[23:05] "각각마다 환경 설정... 차이" [^101]: @[23:23] Android로 이동 [^102]: @[23:27] "앱... 소스... 메인" [^103]: @[23:45] "Android manifest.xml" [^104]: @[24:25] "어플리케이션... 밑에" [^105]: @[24:47] "com google... Android geoapi 키" [^106]: @[25:06] "여기에다가 키값" [^107]: @[25:31] "안드로이드는 일단 오케이" [^108]: @[25:36] "다음 iOS" [^109]: @[26:03] "델리게이트 스위프트" [^110]: @[25:46] "Runner" / @[26:05] "여기에 넣어" [^111]: @[26:25] provideAPIKey [^112]: @[26:25] "GMSS... provide API key" [^113]: @[27:00] "import... 구글맵스" [^114]: @[27:12] "됐어요" [^115]: @[27:27] "p 리스트" [^116]: @[27:33] "제일 밑으로" [^117]: @[27:44] "아이폰의 환경 변수" [^118]: @[27:52] "키는 키라고" [^119]: @[28:06] "io flutter... embedded... views preview" [^120]: @[28:25] "true" [^121]: @[28:42] 설정 정리 [^122]: @[28:42] "안드로이드는 매니페스트" [^123]: @[28:32] "피리스트... 스위프트" [^124]: @[29:16] "API 사용 설정" 확인 [^125]: @[29:10] "잠깐만... 확인" [^126]: @[29:16] "안드로이드... API 사용 설정" [^127]: @[29:26] "iOS... 사용" [^128]: @[29:35] "그래야지 양쪽 다" [^129]: @[29:41] "맵을 한번 불러" [^130]: @[29:41] "소스에서... 맵" [^131]: @[30:23] "stf... 마이 홈페이지" [^132]: @[31:02] "보물지도" [^133]: @[31:22] "구글 맵 하면 돼요" [^134]: @[32:00] "이닛 카메라" 에러 [^135]: @[31:49] "컨테이너로 감싸" [^136]: @[32:19] "카메라가 뭐냐면요" [^137]: @[32:08] "에러가 뜬" [^138]: @[32:24] "망원경" [^139]: @[32:41] "뷰포트를 지정... 카메라" [^140]: @[32:50] "CameraPosition" [^141]: @[32:50] "변수" [^142]: @[33:22] "latlng" [^143]: @[34:58] "줌" [^144]: @[38:50] "initial camera position" [^145]: @[33:39] latitude / @[34:02] longitude / @[35:08] zoom [^146]: @[33:08] "target... latlng" [^147]: @[33:39] "라티튜드" / @[34:02] "롱지..." [^148]: @[34:02] "38선" [^149]: @[34:24] "태평양... 정도" [^150]: @[35:08] "12 정도... 9나 동" [^151]: @[35:13] "골목길... 14 15" [^152]: @[35:13] "12 정도로" [^153]: @[35:30] "테스트... 보여" [^154]: @[35:25] "시작 위경도... 임으로" [^155]: @[36:22] "서울역" [^156]: @[36:49] "주소를... 경도가 숨어" [^157]: @[36:32] "서울역" [^158]: @[36:55] "카피" / @[36:57] "붙여 놓고" [^159]: @[37:09] "3D... 4D" [^160]: @[37:29] "3가... 위도" / @[37:56] "경도" [^161]: @[38:05] "위도를 먼저... 경도를 뒤" [^162]: @[33:22] LatLng + @[38:05] 순서 [^163]: @[38:50] "initialCameraPosition" [^164]: @[38:27] "포지션을 써야 돼... 네임드" [^165]: @[38:50] "이니셜 카메라 포지션" [^166]: @[39:06] "실행해 봅시다" [^167]: @[39:18] "에러가 폭" [^168]: @[27:19] Android+iOS 설정 완료 / @[38:50] 코드 연결 [^169]: @[30:02] "에러... 다시 설명" 예고 / @[39:18] 에러 발생 [^170]: @[02:30] "시행 착오" / @[22:12] 플랫폼별 설정 / @[32:41] 카메라 [^171]: @[02:24] "여러 군데" / @[21:42] "환경 설정" [^172]: @[22:12] "OS 디렉토리" / @[23:27] AndroidManifest / @[26:03] iOS AppDelegate [^173]: @[21:22] "지도 안" / @[29:16] API 사용 설정 확인 [^174]: @[32:00] initial camera 에러 / @[34:58] zoom [^175]: @[37:09] "3D... 4D" [^176]: @[29:16] Android / @[29:26] iOS 사용 확인 [^177]: @[24:25] Android meta-data / @[26:25] iOS provideAPIKey / @[28:06] Info.plist true [^178]: @[38:50] initialCameraPosition [^179]: @[05:33] CRUD / @[04:17] 할당량 / @[06:22] 구글 클라우드 [^180]: @[05:33] "crud" [^181]: @[04:17] "할당량" [^182]: @[06:32] "콘솔 클라우드" [^183]: @[14:28] "SDK... 안드로이드" / @[29:26] iOS [^184]: @[06:02] "API 키" / @[04:38] "승인 키" [^185]: @[32:50] "카메라 포지션" / @[34:58] "줌" [^186]: @[33:22] "latlng" [^187]: @[35:03] "줌" [^188]: @[23:45] AndroidManifest.xml [^189]: @[26:03] delegate swift [^190]: @[27:27] Info.plist [^191]: @[00:03] 콘텐츠 도입부 맥락 [^192]: 사용자 제공 메타데이터(제목) [^193]: 사용자 제공 메타데이터(채널) [^194]: 사용자 제공 메타데이터(길이) [^195]: 사용자 제공 메타데이터(링크)