서비스에서 지도를 띄워두고 장소를 검색하는 기능을 개발하게 됐다. 여러가지를 고려하느라 시간이 조금 걸렸는데, 고민한 내용들을 타임라인처럼 적어보려 한다.
첫 단계에서 고민을 많이 했다. 내가 후보군에 올려놓은 지도 API는 다음 3가지였다.
일단 기획된 서비스 대상이 국내에 한정되어 있어서, 국내 기업이 지원하는 API가 장소 정보에 대한 신뢰성이 더 높을 것 같았다. 이래놓고 구글 API 썼다.
처음엔 카카오 지도 API를 쓰려고 했다. 과거에 리액트 프로젝트로 웹 서비스를 구현하면서 지도 기능이 필요하여 JavaScript API를 가져다 써본 경험이 있었기 때문이다.
하지만 리액트 네이티브 프로젝트에서 API를 가져다 쓰려면 웹뷰
가 필요했다. 나는 되도록 어플리케이션 내부에서 동작하는 것을 원했기 때문에, 잠시 보류하고 일단 다른 API 중에 내 요구사항에 맞는 것이 있는지 먼저 살펴보기로 했다.
지도 기능을 처음 요청받자마자 당연히 쓰려고 했던 카카오 지도 API를 포기하게 되니, 네이버 지도 API와 구글 지도 API 중에 고민해야했다. 찾아보니 둘다 리액트 네이티브 프로젝트에 API를 적용할 수 있도록 일종의 브릿지 역할을 하는 npm 모듈이 있었다.
네이버 지도 API는 아무래도 결과의 신뢰성 측면에서 써볼만했지만, 개발을 하면서 오류가 나거나 했을 때 참고할 수 있는 레퍼런스는 구글 지도 API 쪽이 압도적이었다.
> yarn add react-native-maps
> yarn add react-native-google-places-autocomplete
Podfile을 업데이트해준다.
> cd ios
> pod install
가입된 구글 계정으로 Google Cloud Platform(GCP) 콘솔에 접속하면 API 등록을 할 수 있다. 나는 다음 3개의 API 키가 필요하다. 참고로 Places API 키는 iOS, Android 용이 따로 구분되어 있지 않은 것 같다.
따라서, 다음 순서대로 진행하며 3개를 등록해주었다. iOS 용 키 등록과 Android 용 키 등록이 약간 다르니 주의해서 진행한다.
API 키를 올릴 새 프로젝트를 생성해준다.
사용자 인증 정보 탭에 들어간다.
API 키를 생성해준다.
나는 3개의 API 키를 사용할 것이기 때문에 일단 이렇게 3개를 등록했고, 이후에 각각을 용도에 맞게 수정했다.
iOS 에서 사용할 API 키를 수정하기 전에, 확인해놓아야 하는 것이 있다. Xcode로 리액트 네이티브 프로젝트의 .xcodeproj
파일을 열어서 TARGET
을 선택하고 General
탭을 클릭하여 Bundle Identifier
를 복사해두어야 한다.
API 키 수정 화면에서, iOS 제한사항에 속성을 추가한 뒤 복사한 Bundle Identifier
를 붙여넣었다. 우측의 API Key는 따로 복사해둔 후 수정사항을 저장한다.
Android 도 API 설정을 수정하기 전에 확인해야 하는 것이 2가지 있다. 리액트 네이티브 프로젝트의 android
경로에서 다음 명령어를 입력한다.
> .gradlew signingReport
터미널에서 SHA-1 키를 복사한다.
다시 Android API 키 수정 화면으로 이동해서, Android 제한사항을 추가한 후 복사해둔 디지털 지문을 붙여넣고 저장한다. 패키지 이름
은 android/src/main/java/com/프로젝트명/MainActivity.java
파일에서 최상단에 있는 패키지명을 기입하면 된다. 마찬가지로 우측의 API 키를 복사해두자.
Places API 키 수정 화면에서는 우측의 API 키만 복사해두면 된다.
다음 3개의 라이브러리를 등록해준다. 처음 사용할 때는 카드 등록 과정을 거쳐야 한다.
리액트 네이티브 프로젝트의 ios/프로젝트명/AppDelegate.mm
파일을 열어서 내용을 확인하고 다음과 같이 추가한다. iOS API 키 수정하기에서 복사해 둔 키를 넣는다.
// AppDelegate.mm
#import "AppDelegate.h"
...
#import <GoogleMaps/GoogleMaps.h>
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[GMSServices provideAPIKey:@"API 키"];
self.moduleName = @"프로젝트명";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
ios/Podfile
파일을 열어서 다음과 같이 추가한다.
# Podfile
...
target '프로젝트명' do
pod 'Google-Maps-iOS-Utils', :git => 'https://github.com/Simon-TechForm/google-maps-ios-utils.git', :branch => 'feat/support-apple-silicon'
rn_maps_path = '../node_modules/react-native-maps'
pod 'react-native-google-maps', :path => rn_maps_path
config = use_native_modules!
...
ios/프로젝트명/Info.plist
파일을 열어서 다음과 같이 추가한다. <string>
부분은 동의 팝업이 뜰 때 나타나는 문구이므로 원하는 대로 적는다.
<!-- Info.plist -->
...
<dict>
...
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>서비스 이용을 위해 위치 정보 접근 권한이 필요합니다.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>서비스 이용을 위해 위치 정보 접근 권한이 필요합니다.</string>
...
</dict>
...
android/app/src/res/AndroidManifest.xml
파일에 다음과 같이 추가한다. Android 키 수정하기에서 복사해둔 키를 넣는다.
<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
...
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
...
<application ...>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="API 키"/>
<uses-library android:name="org.apache.http.legacy" android:required="false"/>
...
</application>
</manifest>
세팅이 끝났으니 실행을 해서 확인해볼 차례이다. 지도를 표시해줄 MapView
컴포넌트와 장소 검색창을 표시해줄 GooglePlacesAutoComplete
컴포넌트를 띄워보자.
const MapSearch: React.FC = () {
...
return (
<View>
<MapView
provider={PROVIDER_GOOGLE}
initialRegion={{
latitude: 37.541,
longitude: 126.986,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
style={styles.map}
/>
...
</View>
)
}
export default MapSearch;
GooglePlacesAutoComplete
컴포넌트의 스타일링은 react-native-google-places-autocomplete 문서에 자세히 나와있다.
const MapSearch: React.FC = () {
...
return (
<View>
...
<GooglePlacesAutocomplete
minLength={2}
placeholder="장소를 검색해보세요!"
query={{
key: 'API 키',
language: "ko",
components: "country:kr",
}}
keyboardShouldPersistTaps={"handled"}
fetchDetails={true}
onPress={(data, details) => {console.log(data, details);}}
onFail={(error) => console.log(error)}
onNotFound={() => console.log("no results")}
keepResultsAfterBlur={true}
enablePoweredByContainer={false}
styles={styles.search}
/>
</View>
)
}
export default MapSearch;
스타일링까지 완료한 최종 결과물은 이렇다.
사실 구현하는 데는 그렇게 오래 걸리지 않았는데, 내가 무엇을 했는지 하나하나 글로 정리하는 게 쉽지 않았다. 여유 없이 되는 대로 개발하면 나중에 정리하기가 훨씬 어려운 것 같다 ^^; 여유있게 생각하는 습관을 들여야겠다.
그리고 API 키를 지금 상태로 코드에 넣어두는 것은 바람직하지 않다. 나중에 따로 빼서 프라이빗하게 관리할 수 있게 적용할 예정이다.(TO-DO!)
좋은 정보 감사합니다.