[Flutter] iOS 카카오 로그인

ShinDasom·2023년 7월 21일

준비단계

  • 카카오 개발자 페이지에서 로그인 후 내 어플리케이션을 눌러줍니다.

  • 어플리케이션 추가를 클릭하여 앱이름, 사업자명을 작성 후 생성합니다.

  • 만들어 준 어플리케이션을 선택한 후 플랫폼을 누릅니다.

  • iOS 등록을 눌러서 번들ID만 입력해주고 '저장'을 누릅니다

    • 인텔리제이에서 번들ID 찾기 : ios>Runner.xcodeproj>project.pbxproj 문서에서 PRODUCT_BUNDLE_IDENTIFIER 검색
  • 카카오 로그인 탭에서 활성화 상태를 ON으로 설정해줍니다.

  • 동의항목 탭에서 받아야하는 모든 데이터에 대해 '설정'합니다.
    업로드중..

KaKao SDK 추가

최신 SDK 버전 확인하기

  • pubspec.yaml 파일에 라이브러리 추가합니다. 아래 코드 중 하나를 선택하여 추가하면 됩니다. 들여 쓰기에 유의하시고 작성 후 'Pub get'을 클릭합니다.
dependencies:
  kakao_flutter_sdk: ^1.4.1 # 전체 추가
  kakao_flutter_sdk_user: ^1.4.1 # 카카오 로그인
  • main.dart에 KakaoSdk.init(nativeAppKey: '본인의 네이티브 앱 키') 를 넣어주세요. 네이티브 앱 키는 내 '에플리케이션>앱설정>요약정보'순으로 들어간 후 본인의 네이티브 앱 키를 확인하면 됩니다.
void main() {
  KakaoSdk.init(nativeAppKey: '본인의 네이티브 앱 키');
  runApp(const MyApp());
}

KaKao 로그인

공식문서 확인하기

1. 로그인 기능 예제

login.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart';

class Login extends StatefulWidget {
  const Login({super.key});

  
  State<Login> createState() => _LoginState();
}

class _LoginState extends State<Login> {
  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('kakao 로그인'),
      ),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(onPressed: ()async{
              if (await isKakaoTalkInstalled()) {
              try {
              await UserApi.instance.loginWithKakaoTalk();
              print('카카오톡으로 로그인 성공');
              } catch (error) {
              print('카카오톡으로 로그인 실패 $error');

              // 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우,
              // 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기)
              if (error is PlatformException && error.code == 'CANCELED') {
              return;
              }
              // 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인
              try {
              await UserApi.instance.loginWithKakaoAccount();
              print('카카오계정으로 로그인 성공');
              } catch (error) {
              print('카카오계정으로 로그인 실패 $error');
              }
              }
              } else {
              try {
              await UserApi.instance.loginWithKakaoAccount();
              print('카카오계정으로 로그인 성공');
              } catch (error) {
              print('카카오계정으로 로그인 실패 $error');
              }
              }

            }, child: Text('로그인'))
          ],
        ),
      ),
    );
  }
}

2. 로그인 및 사용자 동의 예제

loginTest.dart

import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart';

class LoginTest extends StatefulWidget {
  const LoginTest({super.key});

  
  State<LoginTest> createState() => _LoginTestState();
}

class _LoginTestState extends State<LoginTest> {
  Future<dynamic> fn_loginWithKakaoAccount() async{
    try {
      OAuthToken token = await UserApi.instance.loginWithKakaoAccount();
      fn_getAdditionalKakaoAccount();
      print("token : "+token.toString());
      return token;
    } catch (e) {
    print("로그인 실패 "+e.toString());

      return null;
    }
  }

  Future<void> fn_getAdditionalKakaoAccount() async{
    User user;
    try {
      user = await UserApi.instance.me();
    } catch (error) {
      print('사용자 정보 요청 실패 $error');
      return;
    }

    List<String> scopes = [];

    if (user.kakaoAccount?.emailNeedsAgreement == true) {
      scopes.add('account_email');
    }
    if (user.kakaoAccount?.birthdayNeedsAgreement == true) {
      scopes.add("birthday");
    }
    if (user.kakaoAccount?.birthyearNeedsAgreement == true) {
      scopes.add("birthyear");
    }
    if (user.kakaoAccount?.ciNeedsAgreement == true) {
      scopes.add("account_ci");
    }
    if (user.kakaoAccount?.phoneNumberNeedsAgreement == true) {
      scopes.add("phone_number");
    }
    if (user.kakaoAccount?.profileNeedsAgreement == true) {
      scopes.add("profile");
    }
    if (user.kakaoAccount?.ageRangeNeedsAgreement == true) {
      scopes.add("age_range");
    }

    if (scopes.length > 0) {
      print('사용자에게 추가 동의 받아야 하는 항목이 있습니다');

      // OpenID Connect 사용 시
      // scope 목록에 "openid" 문자열을 추가하고 요청해야 함
      // 해당 문자열을 포함하지 않은 경우, ID 토큰이 재발급되지 않음
      // scopes.add("openid")

      //scope 목록을 전달하여 카카오 로그인 요청
      OAuthToken token;
      try {
        token = await UserApi.instance.loginWithNewScopes(scopes);
        print('현재 사용자가 동의한 동의 항목: ${token.scopes}');
      } catch (error) {
        print('추가 동의 요청 실패 $error');
        return;
      }

      // 사용자 정보 재요청
      try {
        User user = await UserApi.instance.me();
        print('사용자 정보 요청 성공'
            '\n회원번호: ${user.id}'
            '\n닉네임: ${user.kakaoAccount?.profile?.nickname}'
            '\n이메일: ${user.kakaoAccount?.email}');
      } catch (error) {
        print('사용자 정보 요청 실패 $error');
      }
    }
  }

  Future<void> fn_kakaoLogin() async{
    // 카카오 로그인 구현 예제
    print("button click");
// 카카오톡 설치 여부 확인
// 카카오톡이 설치되어 있으면 카카오톡으로 로그인, 아니면 카카오계정으로 로그인
    if (await isKakaoTalkInstalled()) {
      try {
        await UserApi.instance.loginWithKakaoTalk();
        print('카카오톡으로 로그인 성공');
      } catch (error) {
        print('카카오톡으로 로그인 실패 $error');

        // 사용자가 카카오톡 설치 후 디바이스 권한 요청 화면에서 로그인을 취소한 경우,
        // 의도적인 로그인 취소로 보고 카카오계정으로 로그인 시도 없이 로그인 취소로 처리 (예: 뒤로 가기)
        if (error is PlatformException && error.code == 'CANCELED') {
          return;
        }
        // 카카오톡에 연결된 카카오계정이 없는 경우, 카카오계정으로 로그인
        try {
          await UserApi.instance.loginWithKakaoAccount();
          print('카카오계정으로 로그인 성공');
        } catch (error) {
          print('카카오계정으로 로그인 실패 $error');
        }
      }
    } else {
      try {
        await UserApi.instance.loginWithKakaoAccount();
        print('카카오계정으로 로그인 성공');
      } catch (error) {
        print('카카오계정으로 로그인 실패 $error');
      }
    }
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          SizedBox(
            height: 150,
          ),
          Container(
              child: ElevatedButton(
                style: ElevatedButton.styleFrom(
                    padding: EdgeInsets.all(10)),
                child: const Text('kakao 로그인'),
                onPressed: () async{
                  try {

                    AccessTokenInfo tokenInfo = await UserApi.instance.accessTokenInfo();
                    print('이미 액세스 토큰이 존재하므로 로그인을 시도하지 않습니다.');

                    User user = await UserApi.instance.me();

                    print('사용자 정보 요청 성공'
                        '\n회원번호: ${user.id}'
                        '\n닉네임: ${user.kakaoAccount?.profile?.nickname}'
                        '\n이메일: ${user.kakaoAccount?.email}');
                  } catch (error) {

                    print('액세스 토큰이 존재하지 않습니다. 로그인을 시도합니다.');
                    OAuthToken token = await fn_loginWithKakaoAccount();
                    fn_getAdditionalKakaoAccount();

                    User user = await UserApi.instance.me();
                    if(token != null) {
                      print('사용자 정보 요청 성공'
                          '\n회원번호: ${user.id}'
                          '\n닉네임: ${user.kakaoAccount?.profile?.nickname}'
                          '\n이메일: ${user.kakaoAccount?.email}');
                    }
                  }

                },
              )),
          GestureDetector(
            onTap: ()async{
              try {
                await UserApi.instance.logout();
                print('로그아웃 성공, SDK에서 토큰 삭제');
              } catch (error) {
                print('로그아웃 실패, SDK에서 토큰 삭제 $error');
              }
            },
              child: Text('kakao 로그아웃'))
        ],
      ),
    );
  }


}

0개의 댓글