@react-native-seoul/naver-login
React Native 네이버 로그인 라이브러리 입니다.
Supported platforms
Supported typing
- TypeScript
- Flow
Installation
2.x
버전은 2.x branch 의 설치 가이드와 사용법을 따라주세요.
# npm
npm install @react-native-seoul/naver-login --save
# yarn
yarn add @react-native-seoul/naver-login
0.60
RN version >= - Auto Linking 이 적용됩니다.
- iOS의 경우 추가적으로 Cocoapods 설치가 필요합니다.
cd ios && pod install
0.60
RN version < -
0.60
미만의 React Native를 사용중이시라면 Manual Linking Guide를 참고해주세요.
❗️ Important
추가 작업 - iOS 1. Launch Service Queries Schemes 추가
로그인 시에 네이버 앱을 실행시키기 위해 Launch Services Queries Schemes 를 등록해주어야 합니다.
Info.plist
파일안에 다음과 같은 항목을 추가합니다.
naversearchapp
naversearchthirdlogin
이미 LSApplicationQueriesSchemes
가 항목으로 추가되어 있다면, <array>
안에 두 가지만 더 추가해주세요.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>naversearchapp</string>
<string>naversearchthirdlogin</string>
</array>
2. custom URL scheme 추가
네이버 로그인이 완료된 뒤 다시 우리의 앱으로 돌아오기 위해 URL Scheme
를 Info.plist
에 정의해주어야 합니다.
아래 코드들에서 {{ CUSTOM URL SCHEME }}
는 커스텀하게 정의할 우리 앱에 사용될 URL scheme라고 생각하시면 됩니다.
주의할 점은 다음과 같습니다.
- 네이버 개발자 콘솔에 기입한
URL Scheme
와 동일해야 합니다. -
login
함수 호출시에serviceUrlScheme
로 동일하게 전달해주어야 합니다. - TODO 설명 및 이미지 추가
대략 다음과 같이 Info.plist
에 입력되게 됩니다.
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>naver</string>
<key>CFBundleURLSchemes</key>
<array>
<string>{{ CUSTOM URL SCHEME }}</string>
</array>
</dict>
...
</array>
AppDelegate
의 application:openURL:options
에서 URL 핸들링 로직 추가
3. 네이버 로그인이 성공한 후 우리앱으로 다시 돌아와 URL을 처리하기 위해 필요한 과정입니다.
#import <NaverThirdPartyLogin/NaverThirdPartyLoginConnection.h>
...
// 다른 URL 핸들링 로직이 없는경우
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
return [[NaverThirdPartyLoginConnection getSharedInstance] application:app openURL:url options:options];
}
// 다른 URL 핸들링 로직이 같이 있는 경우
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
// naver
if ([url.scheme isEqualToString:@"{{ CUSTOM URL SCHEME }}"]) {
return [[NaverThirdPartyLoginConnection getSharedInstance] application:application openURL:url options:options];
}
// kakao
if([RNKakaoLogins isKakaoTalkLoginUrl:url]) {
return [RNKakaoLogins handleOpenUrl: url];
}
...
}
❗️ Important
추가 작업 - Android 1. Proguard
만약 Release build에서 R8 컴파일러를 이용해 code obfuscating을 하신다면, app/build.gradle 설정에 minifyEnabled
이 true
로 설정이 되어있을 것입니다.
그 경우 다음과 같은 Proguard 규칙이 필요합니다.
만약 그렇지 않다면 별도의 설정이 필요하지 않습니다.
-keep public class com.nhn.android.naverlogin.** {
public protected *;
}
EXPO
- app.json 파일을 아래와 같이 수정합니다.
{
"expo": {
...
"plugins": [
...,
[
"@react-native-seoul/naver-login",
{
"urlScheme": "CUSTOM URL SCHEME" // 네이버 url scheme를 적어주세요.
}
]
],
...
}
}
- Bare workflow의 경우에는 expo prebuild를 이용합니다.
- Managed Workflow의 경우에는 EAS Build 이후 expo start --dev-client를 이용합니다.
- (Optional) Android에서 proguard rules 등을 적용하실 경우, Expo BuildProperties 를 참고하세요.
API
Func | Param | Return | Description |
---|---|---|---|
login | NaverLoginRequest |
Promise<NaverLoginResponse> |
로그인, 반환되는 Promise 는 항상 resolve된다. |
getProfile | String |
Promise<GetProfileResponse> |
프로필 불러오기 |
logout | Promise<void> |
로그아웃 | |
deleteToken | Promise<void> |
네이버 앱 연동 삭제 |
Type
NaverLoginRequest
export interface NaverLoginRequest {
consumerKey: string;
consumerSecret: string;
appName: string;
disableNaverAppAuth?: boolean;
/** Only for iOS */
serviceUrlScheme?: string;
}
NaverLoginResponse
export interface NaverLoginResponse {
isSuccess: boolean;
/** isSuccess가 true일 때 존재합니다. */
successResponse?: {
accessToken: string;
refreshToken: string;
expiresAtUnixSecondString: string;
tokenType: string;
};
/** isSuccess가 false일 때 존재합니다. */
failureResponse?: {
message: string;
isCancel: boolean;
/** Android Only */
lastErrorCodeFromNaverSDK?: string;
/** Android Only */
lastErrorDescriptionFromNaverSDK?: string;
};
}
GetProfileResponse
export interface GetProfileResponse {
resultcode: string;
message: string;
response: {
id: string;
profile_image: string | null;
email: string;
name: string;
birthday: string | null;
age: string | null;
birthyear: number | null;
gender: string | null;
mobile: string | null;
mobile_e164: string | null;
nickname: string | null;
};
}
Usage
- 자세한 예제는 예제 프로젝트를 참고해주세요
import React, {useState} from 'react';
import {SafeAreaView, Button, View, Text, ScrollView} from 'react-native';
import NaverLogin, {
NaverLoginResponse,
GetProfileResponse,
} from '@react-native-seoul/naver-login';
const consumerKey = '';
const consumerSecret = '';
const appName = 'Hello';
const serviceUrlScheme = 'navertest';
const App = () => {
const [success, setSuccessResponse] =
useState<NaverLoginResponse['successResponse']>();
const [failure, setFailureResponse] =
useState<NaverLoginResponse['failureResponse']>();
const [getProfileRes, setGetProfileRes] = useState<GetProfileResponse>();
const login = async () => {
const {failureResponse, successResponse} = await NaverLogin.login({
appName,
consumerKey,
consumerSecret,
serviceUrlScheme,
});
setSuccessResponse(successResponse);
setFailureResponse(failureResponse);
};
const logout = async () => {
try {
await NaverLogin.logout();
setSuccessResponse(undefined);
setFailureResponse(undefined);
setGetProfileRes(undefined);
} catch (e) {
console.error(e);
}
};
const getProfile = async () => {
try {
const profileResult = await NaverLogin.getProfile(success!.accessToken);
setGetProfileRes(profileResult);
} catch (e) {
setGetProfileRes(undefined);
}
};
const deleteToken = async () => {
try {
await NaverLogin.deleteToken();
setSuccessResponse(undefined);
setFailureResponse(undefined);
setGetProfileRes(undefined);
} catch (e) {
console.error(e);
}
};
return (
<SafeAreaView
style={{alignItems: 'center', justifyContent: 'center', flex: 1}}>
<ScrollView
style={{flex: 1}}
contentContainerStyle={{flexGrow: 1, padding: 24}}>
<Button title={'Login'} onPress={login} />
<Gap />
<Button title={'Logout'} onPress={logout} />
<Gap />
{success ? (
<>
<Button title="Get Profile" onPress={getProfile} />
<Gap />
</>
) : null}
{success ? (
<View>
<Button title="Delete Token" onPress={deleteToken} />
<Gap />
<ResponseJsonText name={'Success'} json={success} />
</View>
) : null}
<Gap />
{failure ? <ResponseJsonText name={'Failure'} json={failure} /> : null}
<Gap />
{getProfileRes ? (
<ResponseJsonText name={'GetProfile'} json={getProfileRes} />
) : null}
</ScrollView>
</SafeAreaView>
);
};
const Gap = () => <View style={{marginTop: 24}} />;
const ResponseJsonText = ({json = {}, name}: {json?: object; name: string}) => (
<View
style={{
padding: 12,
borderRadius: 16,
borderWidth: 1,
backgroundColor: '#242c3d',
}}>
<Text style={{fontSize: 20, fontWeight: 'bold', color: 'white'}}>
{name}
</Text>
<Text style={{color: 'white', fontSize: 13, lineHeight: 20}}>
{JSON.stringify(json, null, 4)}
</Text>
</View>
);
export default App;