졸업 프로젝트/React-Native

[React-Native] 카카오 로그인 구현 1)FE 파트

ansui 2025. 3. 3. 02:16

리액트로는 카카오 로그인을 구현해 본 경험이 있지만 리액트 네이티브는 처음이라 조금 헤맸다.

아직 완성은 못했지만 단계별로 차근차근 기록해보려 한다.

 

- npm & Expo Bare Workflow & android(window) 사용하였다.

- 사용한 라이브러리

https://github.com/crossplatformkorea/react-native-kakao-login?tab=readme-ov-file

 

GitHub - crossplatformkorea/react-native-kakao-login: react-native native module for Kakao sign in.

react-native native module for Kakao sign in. Contribute to crossplatformkorea/react-native-kakao-login development by creating an account on GitHub.

github.com

- 참고 블로그

https://mycodearchive.tistory.com/308

https://it-amin.tistory.com/131


0️⃣ Kakao developers 어플리케이션 생성

 

https://developers.kakao.com/console/app

위 링크로 들어가 카카오 로그인을 한 후 어플리케이션을 추가해준다.

앱이름은 drivemate, 회사명은 팀이름인 X10으로 설정하였다.

 

1️⃣ 안드로이드 플랫폼 등록

생성된 어플리케이션을 클릭한 후 앱 설정 > 플랫폼으로 이동한다.

나는 안드로이드로만 개발하기 때문에 안드로이드 플랫폼 등록만 진행하였다.

패키지명을 입력하고, 키 해시를 등록한다.

패키지명은 아무거나 설정해도 되는 것 같은데 혹시 몰라서 나는 app.json의 name으로 등록했다.

→ com.drivemate

키 해시는 기본 디버그 키 해시인 Xo8WBi6jzSxKDVR4drqm84yr9iU= 를 등록하였다.

위 라이브러리 readme에 아래와 같이 나와있어 나는 키 해시 등록은 따로 진행하지 않고 기본 디버그 키스토어의 키 해시를 복사해서 플랫폼에 등록하였다.

React Native 0.60.x 부터 템플릿에 기본적으로 디버그 키스토어가 포함되어 있습니다.
(project/android/app에 디버그 키스토어가 존재합니다.)
기본 디버그 키스토어의 key hash 는 Xo8WBi6jzSxKDVR4drqm84yr9iU= 를 사용하시면 됩니다.

 

2️⃣Kakao developers 카카오 로그인 활성화

 

3️⃣ 네이티브 앱 키 복사

앱 설정 > 앱 키로 들어가서 네이티브 앱 키를 복사한다.

 

4️⃣ Redirect URI 설정

android/app/src/main/AndroidManifest.xml에 아래 코드를 추가한다.

주의할 점은 네이티브 앱 키를 적을 때 kakao 뒤에 붙여서 바로 적어야 한다.

예를 들어, 네이티브 앱 키가 abc123일 경우, android:scheme="kakaoabc123" /> 이렇게 적어야한다.

// 생략

  <application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" android:supportsRtl="true">
    <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>

      <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="drivemate" />
      </intent-filter>
    </activity>

    // 추가된 부분
    <activity 
      android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
      android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <!-- Redirect URI: "kakao{NATIVE_APP_KEY}://oauth“ -->
        <data android:host="oauth"
          android:scheme="kakao + 네이티브앱키" />
      </intent-filter>
    </activity>
  </application>
</manifest>

 

다음으로 android/app/src/main/res/values/strings.xml에 아래 코드를 추가한다.

이 부분도 위 경우와 마찬가지로 네이티브 앱 키만 바로 적으면 된다.

예) <string name="kakao_app_key">abc123</string>

<resources>
    <string name="app_name">drivemate</string>
    <string name="kakao_app_key">네이티브 앱 키</string>
</resources>

 

5️⃣ android/build.gradle에 Maven 추가

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext {
        buildToolsVersion = findProperty('android.buildToolsVersion') ?: '35.0.0'
        minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '24')
        compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '35')
        targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34')
        kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.24'

        ndkVersion = "26.1.10909125"
    }
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath('com.android.tools.build:gradle')
        classpath('com.facebook.react:react-native-gradle-plugin')
        classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
    }
}

apply plugin: "com.facebook.react.rootproject"

allprojects {
    repositories {
        maven {
            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
            url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
        }
        maven {
            // Android JSC is installed from npm
            url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist'))
        }

        google()
        mavenCentral()
        maven { url 'https://www.jitpack.io' }
        // 아래 코드 추가
        maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }

    }
}

android/build.gradle에 가장 마지막 줄 하나만 추가해준다.

 

6️⃣ 카카오 로그인 테스트 코드 작성

import { Pressable, ScrollView, StyleSheet, Text, View } from "react-native";
import React, { useState } from "react";
import {
  login,
  logout,
  getProfile as getKakaoProfile,
  shippingAddresses as getKakaoShippingAddresses,
  unlink,
} from "@react-native-seoul/kakao-login";

const Kakao = () => {
  const [result, setResult] = useState<string>("");

  const signInWithKakao = async (): Promise<void> => {
    try {
      const token = await login();
      setResult(JSON.stringify(token));
      console.log("login success ", token.accessToken);
    } catch (err) {
      console.error("login err", err);
    }
  };

  const signOutWithKakao = async (): Promise<void> => {
    try {
      const message = await logout();

      setResult(message);
    } catch (err) {
      console.error("signOut error", err);
    }
  };

  const getProfile = async (): Promise<void> => {
    try {
      const profile = await getKakaoProfile();

      setResult(JSON.stringify(profile));
    } catch (err) {
      console.error("signOut error", err);
    }
  };

  const getShippingAddresses = async (): Promise<void> => {
    try {
      const shippingAddresses = await getKakaoShippingAddresses();

      setResult(JSON.stringify(shippingAddresses));
    } catch (err) {
      console.error("signOut error", err);
    }
  };

  const unlinkKakao = async (): Promise<void> => {
    try {
      const message = await unlink();

      setResult(message);
    } catch (err) {
      console.error("signOut error", err);
    }
  };

  return (
    <View style={styles.container}>
      <View style={styles.resultContainer}>
        <ScrollView>
          <Text>{result}</Text>
        </ScrollView>
      </View>
      <Pressable
        style={styles.button}
        onPress={() => {
          signInWithKakao();
        }}
      >
        <Text style={styles.text}>카카오 로그인</Text>
      </Pressable>
      <Pressable style={styles.button} onPress={() => getProfile()}>
        <Text style={styles.text}>프로필 조회</Text>
      </Pressable>
      <Pressable style={styles.button} onPress={() => getShippingAddresses()}>
        <Text style={styles.text}>배송주소록 조회</Text>
      </Pressable>
      <Pressable style={styles.button} onPress={() => unlinkKakao()}>
        <Text style={styles.text}>링크 해제</Text>
      </Pressable>
      <Pressable style={styles.button} onPress={() => signOutWithKakao()}>
        <Text style={styles.text}>카카오 로그아웃</Text>
      </Pressable>
    </View>
  );
};

export default Kakao;

const styles = StyleSheet.create({
  container: {
    height: "100%",
    justifyContent: "flex-end",
    alignItems: "center",
    paddingBottom: 100,
  },
  resultContainer: {
    flexDirection: "column",
    width: "100%",
    padding: 24,
  },
  button: {
    backgroundColor: "#FEE500",
    borderRadius: 40,
    borderWidth: 1,
    width: 250,
    height: 40,
    paddingHorizontal: 20,
    paddingVertical: 10,
    marginTop: 10,
  },
  text: {
    textAlign: "center",
  },
});

 

우선 확인만 해보기 위해 위와 같이 코드를 작성했다.

아래와 같이 로그인 버튼을 누르면 링크가 잘 이동하고 로그인을 완료하면 accessToken과 refreshToken을 받은 것이 확인된다.

 

 

✅현재까지 구현한 부분

FE 파트) 카카오 로그인 구현 → 카카오에서 토큰(accessToken, refreshToken) 발급 받기

 

아래 그림은 다른 블로그에서 가져온 그림인데 리액트&스프링으로 카카오 로그인을 구현하는 과정이다.

전체 흐름을 앱과 비교하기 위해 아래 그림을 가져왔다.

 

현재 리액트 네이티브로 5번까지 진행이 된 상태이다.

하지만 BE로 인증 코드를 전송하지 않고 토큰을 카카오에서 BE이 아니라 FE이 받은 상태이다.

그래서 이 토큰을 kakao에서 BE로 넘기는게 아니라 FE에서 BE으로 넘기는 방식이다.

이후 BE이 6번부터 진행하면 될 것 같다.

https://2dowon.github.io/docs/react/social_login/

 

✅전체 흐름

FE) 카카오 로그인 구현 → 카카오에서 토큰(accessToken, refreshToken) 발급 받기 → 토큰 BE로 넘기기

BE) 사용자 인증 후 DB 저장 → 자체 토큰 FE로 발급

FE) 회원가입 여부에 따라 리다이렉트 + 토큰 앱 스토리지에 저장

 

아마 이렇게 진행될 것 같은데 아직 안해봐서 잘 모르겠다..

진행 중 변경되면 이 블로그 글도 업데이트할 예정이다 !