crossplatformkorea / react-native-naver-login

리엑트 네이티브 네이버 로그인 라이브러리
MIT License
164 stars 90 forks source link

@react-native-seoul/naver-login

npm version Android SDK - 5.9.1 iOS SDK - 4.2.3

downloads license

React Native 네이버 로그인 라이브러리 입니다.

Supported platforms

Android iOS

Supported typing

screenshots

Installation

❗️ 2.x 버전은 2.x branch 의 설치 가이드와 사용법을 따라주세요.

# npm
npm install @react-native-seoul/naver-login --save

# yarn
yarn add @react-native-seoul/naver-login

RN version >= 0.60

cd ios && pod install

RN version < 0.60

Configuration

initialize 함수 호출

다음과 같이 앱의 index.js나 로그인이 필요한 시점 전에 초기화 함수를 호출합니다.

NaverLogin.initialize({
  appName,
  consumerKey,
  consumerSecret,
  serviceUrlSchemeIOS,
  disableNaverAppAuthIOS: true,
});

추가 작업 - iOS 🍎

1. Launch Service Queries Schemes 추가

로그인 시에 네이버 앱을 실행시키기 위해 Launch Services Queries Schemes 를 등록해주어야 합니다.

Info.plist 파일안에 다음과 같은 항목을 추가합니다.

이미 LSApplicationQueriesSchemes 가 항목으로 추가되어 있다면, <array> 안에 두 가지만 더 추가해주세요.

<key>LSApplicationQueriesSchemes</key>
<array>
  <string>naversearchapp</string>
  <string>naversearchthirdlogin</string>
</array>

image

2. custom URL scheme 추가

네이버 로그인이 완료된 뒤 다시 우리의 앱으로 돌아오기 위해 URL SchemeInfo.plist 에 정의해주어야 합니다.

아래 코드들에서 {{ CUSTOM URL SCHEME }}는 커스텀하게 정의할 우리 앱에 사용될 URL scheme라고 생각하시면 됩니다.

주의할 점은 다음과 같습니다.

대략 다음과 같이 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>

image

3. AppDelegateapplication:openURL:options 에서 URL 핸들링 로직 추가

네이버 로그인이 성공한 후 우리앱으로 다시 돌아와 URL을 처리하기 위해 필요한 과정입니다.

#import <NaverThirdPartyLogin/NaverThirdPartyLoginConnection.h>
...
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
   return [[NaverThirdPartyLoginConnection getSharedInstance] application:app openURL:url options:options];
}
- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  // naver
  if ([url.scheme isEqualToString:@"{{ CUSTOM URL SCHEME }}"]) {
    return [[NaverThirdPartyLoginConnection getSharedInstance] application:app openURL:url options:options];
  }

  // kakao
  if([RNKakaoLogins isKakaoTalkLoginUrl:url]) {
    return [RNKakaoLogins handleOpenUrl: url];
  }
  ...
}

추가 작업 - Android 🤖

1. Proguard

만약 Release build에서 R8 컴파일러를 이용해 code obfuscating을 하신다면, app/build.gradle 설정에 minifyEnabledtrue로 설정이 되어있을 것입니다.

그 경우 다음과 같은 Proguard 규칙이 필요합니다.

만약 그렇지 않다면 별도의 설정이 필요하지 않습니다.

-keep public class com.navercorp.nid.** { *; }

[!NOTE] 이 규칙은 확실하지 않으나 Android Native SDK 코드에서 proguard consumer rules가 정의되어있지 않아 삽입된 구문입니다.

이것을 추가해주었음에도 난독화로 인해 에러가 나는 상황에서는 OKHttp, Retrofit 라이브러리들의 Proguard Rule들을 직접 같이 추가해주는 것을 추천드립니다.

R8 컴파일러로 안드로이드 프로젝트를 빌드하면 Okhtttp, Retrofit과 같은 라이브러리들은 내부적으로 JAR, AAR에 rule을 포함시켜두었기 때문에 문제가 되지 않아야 정상입니다.

추가 작업 - EXPO

  1. app.json 파일을 아래와 같이 수정합니다.
{
  "expo": {
    ...
    "plugins": [
      ...,
      [
        "@react-native-seoul/naver-login",
        {
          "urlScheme": "CUSTOM URL SCHEME" // 네이버 url scheme를 적어주세요.
        }
      ]
    ],
    ...
  }
}
  1. (Optional) Android에서 proguard rules 등을 적용하실 경우, Expo BuildProperties 를 참고하세요.

API

Func Param Return Description
initialize NaverLoginInitParams void 네이버 SDK 초기화
login Promise<NaverLoginResponse> 로그인
getProfile String Promise<GetProfileResponse> 프로필 불러오기
logout Promise<void> 로그아웃
deleteToken Promise<void> 네이버 계정 연동 해제

Type

NaverLoginInitParams

export interface NaverLoginInitParams {
  consumerKey: string;
  consumerSecret: string;
  appName: string;
  /** (iOS) 네이버앱을 사용하는 인증을 비활성화 한다. (default: false) */
  disableNaverAppAuthIOS?: boolean;
  /** (iOS) */
  serviceUrlSchemeIOS?: 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

/** Fill your keys */
const consumerKey = '';
const consumerSecret = '';
const appName = 'testapp';

/** This key is setup in iOS. So don't touch it */
const serviceUrlSchemeIOS = 'navertest';

const App = (): ReactElement => {
  useEffect(() => {
    NaverLogin.initialize({
      appName,
      consumerKey,
      consumerSecret,
      serviceUrlSchemeIOS,
      disableNaverAppAuthIOS: true,
    });
  }, []);

  const [success, setSuccessResponse] =
    useState<NaverLoginResponse['successResponse']>();

  const [failure, setFailureResponse] =
    useState<NaverLoginResponse['failureResponse']>();
  const [getProfileRes, setGetProfileRes] = useState<GetProfileResponse>();

  const login = async (): Promise<void> => {
    const { failureResponse, successResponse } = await NaverLogin.login();
    setSuccessResponse(successResponse);
    setFailureResponse(failureResponse);
  };

  const logout = async (): Promise<void> => {
    try {
      await NaverLogin.logout();
      setSuccessResponse(undefined);
      setFailureResponse(undefined);
      setGetProfileRes(undefined);
    } catch (e) {
      console.error(e);
    }
  };

  const getProfile = async (): Promise<void> => {
    try {
      const profileResult = await NaverLogin.getProfile(success!.accessToken);
      setGetProfileRes(profileResult);
    } catch (e) {
      setGetProfileRes(undefined);
    }
  };

  const deleteToken = async (): Promise<void> => {
    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>
  );
};

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT