플러터 개발일지 2 : 파이어베이스를 이용한 카카오 로그인

Adam·2023년 6월 22일
2

Flutter

목록 보기
2/3
post-thumbnail

아직 애플 로그인을 위한 애플 개발자 계정의 심사가 진행 중이고, 파이어베이스를 이용한 카카오 로그인을 구현하였기 때문에 해당 작업에 대한 개발일지를 작성해보려 한다.

1. 패키지 다운로드

우선 카카오에서 제공하는 sdk를 플러터 프로젝트에서 다운로드 받아야 한다.
커맨드는 아래와 같다

flutter pub add kakao_flutter_sdk

그리고 카카오는 애플이나 구글 로그인과 다르게 현재 공식적으로 파이어베이스에서 로그인을 지원해주지는 않는다.
그렇기 때문에 핸드폰에서 카카오로 인증 요청 -> 카카오에서 인증 토큰 발급 -> 플러터 프런트앤드에서 카카오 인증 토큰을 백엔드 서버에 보내 파이어베이스 토큰을 반환 -> 파이어베이스 토큰을 통해 파이어베이스 서버에서 인증 진행 후 FCM 토큰 발급을 진행해야 한다.

이 과정을 위해서 내가 받은 카카오 토큰이 서버에서 제대로 된 토큰인지 인증하는 과정이 필요한데 이를 위해 필자는 nestJs를 백엔드 서버로 구축하였기 때문에 passport-kakao를 패키지를 설치해준다.

npm i passport-kakao

2. 카카오 개발자 계정 설정

카카오 로그인을 구현하기 위해선 카카오 개발자 계정에 어플리케이션을 생성하고 ios와 안드로이드 등록 작업을 해주어야 한다.

서버 설정

앞서 설명한것과 같이 서버에서 카카오 토큰을 인증하고 파이어베이스 토큰을 발급해주는 작업을 해주어야 한다.
이를 위해서 카카오 개발자 페이지에서 callback url을 설정해주어야 하는데, 로컬에서 먼저 실행해줄 것이기 때문에 로컬 서버에서 받을 주소를 설정해 준다.

그 후 서버에서 kakao auth strategy를 설정해주어야 한다.

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-kakao';

@Injectable()
export class KakaoStrategy extends PassportStrategy(Strategy, 'kakao') {
  constructor() {
    super({
      clientID: "카카오 어플리케이션에서 보이는 rest api 키를 사용하면 된다"
      callbackURL: 'http://localhost:3000/auth/kakao/callback',
    });
  }

  async validate(
    accessToken: string,
    refreshToken: string,
    profile: any,
    done: Function,
  ) {
    // You can save user information to your database here
    const user = {
      uid: profile.id,
      email: profile._json && profile._json.kaccount_email,
      // ... more user info
    };
    done(null, user);
  }
}

그 후 해당 strategy를 authmoudle에서 사용할 수 있도록 provider에 추가해준다.

providers: [AuthService, JwtStrategy, PrismaService, KakaoStrategy],

다음은 해당 콜백을 받을 컨트롤러를 설정해준다.

@Post('kakao/callback')
  async handleKakaoCallback(@Body() body: { uid: string}) {
    const { uid } = body;
    // Create custom Firebase token
    const firebaseToken = await this.authService.createFirebaseToken(uid);

    console.log("firebase token created");
    // Return the Firebase token
    return { firebaseToken };
  }

연결 된 서비스는 간단한데, 전에 설정해준 파이어베이스 어드민으로 토큰을 발급해주는 작업이다.

async createFirebaseToken(uid: string): Promise<string> {
    return admin.auth().createCustomToken(uid);
  }

안드로이드 설정

카카오 개발자 계정에서 나온대로 AndroidManifest.xml에 나온 값을 등록을 해준 후 아래 커맨드를 통해 해시 키를 생성하고 등록해준다.

keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -storepass android -keypass android | openssl sha1 -binary | openssl base64 

그 후 AndroiMaifest.xml에서 아래 정보를 추가해준다

<activity
            android:name="com.kakao.sdk.flutter.AuthCodeCustomTabsActivity"
            android:exported="true">
            <intent-filter android:label="flutter_web_auth">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <!-- "kakao${YOUR_NATIVE_APP_KEY}://oauth" 형식의 앱 실행 스킴 설정 -->
                <!-- 카카오 로그인 Redirect URI -->
                <data
                    android:host="oauth"
                    android:scheme="kakao+해쉬키 />
            </intent-filter>
        </activity>

ios 설정

우선 Runner 디렉토리 밑에 있는 Info.plist에 아래 항목을 추가해준다.

<key>LSApplicationQueriesSchemes</key>
  <array>
      <!-- 카카오톡으로 로그인 -->
      <string>kakaokompassauth</string>
      <!-- 카카오톡 공유 -->
      <string>kakaolink</string>
  </array>

그 후 Xcode를 연 후 Runner> TARGETS> Runner> Info> URL Types에서 Url Type을 추가해주어야 하는데 Url Scheme을 AndroidManifest.xml에서 입력했던것처럼 "kakao+해쉬키" 값으로 입력을 해주면 된다.

플러터 코드 설정

main.dart에서 카카오sdk를 초기화 시켜줘야 하는데 이는 void main 매써드에서 한다.
초기화 방법은 아래 코드를 runApp전에 실행 시켜주면 된다.

KakaoSdk.init(nativeAppKey: "네이티브 키");

3. 코드 작성

전에 구글 로그인 매써드를 작성한 auth.dart파일에 마찬가지로 작성을 해보도록 하겠다.

Future<User?> signInWithKakao() async {
    if (await k.isKakaoTalkInstalled()) {
      try {
        final response = await k.UserApi.instance.loginWithKakaoTalk();
      } catch (error) {
        print('카카오톡으로 로그인 실패 $error');

        // 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우,
        // 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기)
        if (error is PlatformException && error.code == 'CANCELED') {
          print('카카오톡으로 로그인 실패 $error');
        }
        // 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인
        try {
          final response = await k.UserApi.instance.loginWithKakaoAccount();
        } catch (error) {
          print('카카오계정으로 로그인 실패 $error');
        }
      }
    } else {
      try {
        final response = await k.UserApi.instance.loginWithKakaoAccount();
      } catch (error) {
        print('카카오계정으로 로그인 실패 $error');
      }
    }
    k.User? kakaoUser = await k.UserApi.instance.me();
    final kakaoToken = await createCustomToken({'uid': kakaoUser!.id.toString(),});
    final userCredential = await _firebaseAuth.signInWithCustomToken(kakaoToken);
    final user = userCredential.user;
    final idToken = await user!.getIdToken();

    // Optionally get FCM token for push notifications
    FirebaseMessaging messaging = FirebaseMessaging.instance;
    String? token = await messaging.getToken();
    await _storeUserInfoInPrefs(user, token);
    String? nickname = kakaoUser.kakaoAccount!.profile!.nickname;
    SharedPreferences prefs = await SharedPreferences.getInstance();
    prefs.setString("displayName", nickname!);
    prefs.setString("email", kakaoUser!.kakaoAccount!.email!);
    int userCheck = await this.isRegistered(kakaoUser!.kakaoAccount!.email!);
    if(userCheck != 0){
      updateMessagingToken(userCheck, token!);
      authenticateFromServer(idToken, user!.email!);
      Get.to(HomeScreen());
    }else{
      Get.to(SignupScreen());
    }

    return user;
  }

  Future<String> createCustomToken(Map<String, dynamic> user) async {
    final customTokenResponse = await http
        .post(Uri.parse('$baseUrl/auth/kakao/callback'), body: user);

    final responseJson = jsonDecode(customTokenResponse.body);
    print('Received Token from server: ${responseJson['firebaseToken']}');

    return responseJson['firebaseToken'];
  }

코드 설명은 다음과 같다.
우선 파이어베이스에서 제공하는 User 변수와 카카오에서 제공하는 User 변수가 이름이 같기 때문에 충돌 방지를 위해 카카오 sdk를 k로 import 해주었다.
위에 로그인을 진행하고 예외 처리를 하는 부분은 카카오 공식 문서에 나온 부분으로 상황별로 예외 처리가 잘 돼있기 때문에 그대로 사용하기로 하였다.
그 후 k.UserApi.instance.me()가 카카오의 user객체라고 생각하면 되기 때문에 이 객체의 id를 전에 만들었던 nestJs의 파이어베이스 토큰을 생성하는 함수에 전달을 해주고 그 뒷부분은 구글 로그인과 동일하다.

이때, 카카오에서 가져올 수 있는 정보들을 설정을 해줄 수 있는데, 아래와 같이 설정을 하여 이메일과 닉네임을 가져와 SharedPreferences에 담아주었다.

그 후 로그인 화면에서 카카오 로그인 Inkwell 위젯에서 signInWithKakao 매써드를 호출해준다.

		InkWell(
                  onTap: () async {
                    await authService.signInWithKakao();
                  },
                  child: Image.asset(
                    "images/kakao.png",
                    width: 331,
                    height: 58,
                    fit: BoxFit.cover,
                  ),
                ),

느낀점

파이어베이스에서 구글 로그인은 공식적으로 지원을 해주었기 때문에 정말 순조롭게 진행하였는데, 카카오 로그인은 구동 방법을 이해하는 방법을 이해하는데 조금 시간이 걸렸다.
또한 서버쪽에서 어떤식으로 파이어베이스 토큰을 발급 받아야 되는지에 대한 레퍼런스가 좀 부족했기 때문에 시간이 더 걸렸던것 같다.
카카오 로그인을 구현해야 되는 개발자 분들이 많을탠데, 이 것이 조금이나마 레퍼런스가 될 수 있었으면 좋겠다.

profile
Keep going하는 개발자

0개의 댓글