읽기 전

  • 불필요한 코드나 잘못 작성된 내용에 대한 지적은 언제나 환영합니다.
  • 개인적으로 사용해보면서 배운 점을 정리한 글입니다.
  • 설명을 위해 일부 코드는 잘라서 적고 있으며 전체 코드는 포스팅 하단에 적어두었으니 참고해주세요.

Android에서 Kakao 계정 연동하기

Android 사이드 프로젝트에 유저 계정관리를 로컬이 아닌 SNS 계정으로 사용하자는 의견이 있어 한국 유저들이 많이 이용하는 Kakao 계정을 third party 앱에 연동하는 방법을 정리하려 한다.

App key 발급 및 등록

카카오 로그인을 사용하기 위해 카카오 개발자 센터에 본인의 앱을 등록해야 한다.

개발자 센터 링크

카카오톡 계정으로 로그인 후 [내 애플리케이션] - [애플리케이션 추가하기]를 눌러 본인의 앱을 추가한다.

Android_SNS_Auth_001-01

앱을 추가한 뒤 클릭하여 세부정보로 들어가면 앱 키를 볼 수 있다. 프로젝트에는 [네이티브 앱 키]를 사용한다.

Android_SNS_Auth_001-02

지금은 등록만 했을 뿐 어떤 기능을 사용할 지 아무런 설정이 되어있지 않다. 카카오 로그인 기능이 필요하기 때문에 카카오 로그인 탭에 가서 기능을 켜준다.

Android_SNS_Auth_001-03

동의 항목에 가면 유저로부터 어떤 정보들을 동의를 받고 요청할 지 명세되어 있다. 프로젝트에서는 유저 식별 값만 필요해서 설정을 하지 않았지만 설명을 위해 프로필 정보 항목만 동의를 받아오기로 했다.

Android_SNS_Auth_001-04

플랫폼 탭으로 가서 해당 앱은 안드로이드에서 작동하기 때문에 Android 플랫폼 등록을 한다. 패키지 명은 AndroidManifest.xml을 참고하면 찾을 수 있다. 마켓 URL은 아직 출시를 안했으므로 없음을 체크한다. 키 해시는 앱이 갖는 고유 값이다. 해시 키를 얻는 방법은 Android | 해시 키 생성 및 릴리즈용 키 생성에 정리해두었다. 다른 개발자와 협업을 고려하여 릴리즈 키를 생성 후 입력하였다. 이제 개발자 센터에 앱 정보를 모두 등록하였다.

Android_SNS_Auth_001-05

프로젝트에 Kakao SDK import 하기

네이티브 앱 키 입력

발급받은 네이티브 앱 키를 strings.xml에 넣는다.

<resources>
    <string name="app_name">MySNSAccount</string>
    <string name="kakao_app_key">e489f82f0a6bf509a254d53c726ee5e5</string>
</resources>

build.gradle(Project)

SDK를 다운받게끔 다음과 같이 코드를 추가한다.

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://devrepo.kakao.com/nexus/content/groups/public/' }
        mavenCentral()
    }
}

build.gradle(Module)

먼저 gradle.properties에 업데이트할 버전을 명시하기 위해 다음을 추가한다.

KAKAO_SDK_GROUP=com.kakao.sdk
KAKAO_SDK_VERSION=1.27.0

그 후 build.gradle에서 해당 버전 값을 참조하여 사용하도록 코드를 입력한다.

dependencies {
    implementation group: project.KAKAO_SDK_GROUP, name: 'usermgmt', version: project.KAKAO_SDK_VERSION
}

Android 카카오톡 로그인 구조

Kakao SDK 초기화

Kakao SDK를 사용하기 전 초기화를 해야 한다. 따로 Application을 상속받는 GlobalHelper 클래스를 생성하여 앱 수준에서 관리하기로 한다.

GlobalHelper.class - SDK 초기화

public class GlobalHelper extends Application {
    private static volatile GlobalHelper mInstance = null;

    public static GlobalHelper getGlobalApplicationContext() {
        if (mInstance == null) {
            throw new IllegalStateException("This Application does not GlobalAuthHelper");
        }
        return mInstance;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
        KakaoSDK.init(new KakaoSDKAdapter());
    }

    public class KakaoSDKAdapter extends KakaoAdapter {
        @Override
        public ISessionConfig getSessionConfig() {
            return new ISessionConfig() {
                @Override
                public AuthType[] getAuthTypes() {
                    return new AuthType[]{AuthType.KAKAO_LOGIN_ALL};
                }

                @Override
                public boolean isUsingWebviewTimer() {
                    return false;
                }

                @Override
                public boolean isSecureMode() {
                    return false;
                }

                @Override
                public ApprovalType getApprovalType() {
                    return ApprovalType.INDIVIDUAL;
                }

                @Override
                public boolean isSaveFormData() {
                    return true;
                }
            };
        }

        @Override
        public IApplicationConfig getApplicationConfig() {
            return new IApplicationConfig() {
                @Override
                public Context getApplicationContext() {
                    return GlobalHelper.getGlobalApplicationContext();
                }
            };
        }
    }
}

앱 수준에서 GlobalHelper.class를 인식하기 위해 AndroidManifest.xml을 수정해야 한다. 로그인을 위해 인터넷 권한을 부여하고 Strings.xml에 저장했던 kakao_app_key를 메타 데이터로 넣는다. 그리고 생성해둔 GlobalHelper 클래스를 application의 name 태그에 넣어준다.

    <application
        android:name=".GlobalHelper"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SecondActivity" />
        <meta-data
            android:name="com.kakao.sdk.AppKey"
            android:value="@string/kakao_app_key" />
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

로그인 버튼 및 자동 로그인 로직 구현

로그인 로직을 구현하기 위해 kakao에서 기본적으로 제공하는 버튼을 사용할 수 있지만 여러 SNS 로그인을 지원하려 할 경우 제공양식이 제각각이라 한 곳에 모아서 관리하려 할 경우 통일감이 없다. 이번 프로젝트에서 커스텀 버튼을 사용했기에 포스팅에서도 커스텀 버튼을 채택하려 한다.

로그인 레이아웃 정의

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/hg_main_70"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.70" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/hg_main_95"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.95" />

    <LinearLayout
        android:id="@+id/layout_login_btn_group"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toTopOf="@+id/hg_main_95"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/hg_main_70">

        <Button
            android:id="@+id/btn_kakao_login"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="카카오 로그인" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:visibility="gone"
        app:layout_constraintTop_toTopOf="parent"
        tools:visibility="gone">

        <com.kakao.usermgmt.LoginButton
            android:id="@+id/btn_kakao_login_basic"
            android:layout_width="0dp"
            android:layout_height="0dp" />

    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

임의의 버튼으로 로그인 로직이 동작하게끔 하기 위해 레이아웃을 정의한다. 카카오에서 자체적으로 제공하는 LoginButton 컴포넌트를 Invisible하게 배치하고 커스텀 버튼을 앞에 두는 방식이다.

로그인 버튼 선언 후 핸들러 설정

계정 인증 관련 내용은 별도 클래스로 구현을 해보았다. 다중 SNS 인증을 구현해야 하기 때문에 로그인 절차만 KakakoLogin에서 관리한다. 다만 별도의 레이아웃 없이 Activity만 상속받게끔 한다. 굳이 Activity를 상속시지 않고 클래스로만 구현해도 좋지만 네이버의 경우 쓰레드 관리가 필요해 Activity를 상속할 필요가 있어 카카오톡도 똑같이 작성하였다. 카카오톡 로그인 로직은 자체적으로 제공하는 버튼에 연걸되어 있으므로 커스텀 버튼 클릭 시 해당 컴포넌트에 클릭 신호를 전달하여 로그인 로직을 전달하는 방식으로 구현하였다. 계정 인증을 담당할 클래스에 로그인 성공/실패, 사용자 정보 요청에 대한 코드를 작성하였다.

KakaoLogin.class - 로그인 및 사용자 정보 요청

public class KakaoLogin extends Activity {
    public static class KakaoSessionCallback implements ISessionCallback {
        private Context mContext;
        private MainActivity mainActivity;

        public KakaoSessionCallback(Context context, MainActivity activity) {
            this.mContext = context;
            this.mainActivity = activity;
        }

        @Override
        public void onSessionOpened() {
            requestMe();
        }

        @Override
        public void onSessionOpenFailed(KakaoException e) {
            Toast.makeText(mContext, "KaKao 로그인 오류가 발생했습니다. " + e.toString(), Toast.LENGTH_SHORT).show();
        }

        protected void requestMe() {
            UserManagement.getInstance().me(new MeV2ResponseCallback() {
                @Override
                public void onSessionClosed(ErrorResult errorResult) {
                    mainActivity.directToSecondActivity(false);
                }

                @Override
                public void onSuccess(MeV2Response result) {
                    List userInfo = new ArrayList<>();
                    userInfo.add(String.valueOf(result.getId()));
                    userInfo.add(result.getKakaoAccount().getProfile().getNickname());
                    GlobalHelper mGlobalHelper = new GlobalHelper();
                    mGlobalHelper.setGlobalUserLoginInfo(userInfo);

                    mainActivity.directToSecondActivity(true);
                }
            });
        }
    }
}

로그인 성공 시 requestMe() 메소드를 호출하여 계정정보를 전역 변수에 저장하는 코드가 있다. 전역 변수에 값을 저장하고 반환하는 코드를 앱 단위에서 관리해야 하므로 GlobalHelper.class 에 메소드를 추가해준다. checkAndImplicitOpen()과 세션이 유효하면 카카오 로그인 페이지가 아니라 바로 회원정보 요청한 뒤 다음 Activity로 direct해준다. 다른 분들이 정리한 포스팅에서는 위 과정을 모두 로그인 Activity에 넣는데 이 방식이 제일 깔끔해보인다.

GlobalHelper.class - 전역변수에 값 입력 및 반환

    public static List<String> getGlobalUserLoginInfo() {
        return mGlobalUserLoginInfo;
    }

    public static void setGlobalUserLoginInfo(List<String> userLoginInfo) {
        mGlobalUserLoginInfo = userLoginInfo;
    }

레이아웃에 있는 kakao 로그인을 연결할 버튼을 선언한 뒤 클릭 리스너를 생성한다. 리스너 내에 자체 제공 로그인 버튼으로 클릭 신호를 전달하면 커스텀 버튼 클릭 시 카카오톡 로그인 화면으로 넘어간다. 이후 카카오톡 로그인 세션 여부를 체크하는 메소드를 정의하고 세션이 있다면 재로그인 없이 즉시 이전에 정의했던 KakaoSessionCallback 클래스가 작동되게끔 코드를 작성한다. 그리고 로그인 성공 시 다음 Activity로 넘어가는 메소드도 추가해준다.

MainActivity - 버튼 리스너 / 자동 로그인 활성화

public class MainActivity extends AppCompatActivity {
    private Button mKakaoLoginBtn;
    private LoginButton mKakaoLoginBtnBasic;
    private KakaoSessionCallback sessionCallback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mKakaoLoginBtn = findViewById(R.id.btn_kakao_login);
        mKakaoLoginBtnBasic = findViewById(R.id.btn_kakao_login_basic);

        mKakaoLoginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mKakaoLoginBtnBasic.performClick();
            }
        });

        if (!HasKakaoSession()) {
            sessionCallback = new KakaoSessionCallback(getApplicationContext(), MainActivity.this);
            Session.getCurrentSession().addCallback(sessionCallback);
        } else if (HasKakaoSession()) {
            sessionCallback = new KakaoSessionCallback(getApplicationContext(), MainActivity.this);
            Session.getCurrentSession().addCallback(sessionCallback);
            Session.getCurrentSession().checkAndImplicitOpen();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        // 세션 콜백 삭제
        Session.getCurrentSession().removeCallback(sessionCallback);
    }

    private boolean HasKakaoSession() {
        if (!Session.getCurrentSession().checkAndImplicitOpen()) {
            return false;
        }
        return true;
    }

    public void directToSecondActivity(Boolean result) {
        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        if (result) {
            Toast.makeText(getApplicationContext(), "로그인 성공!", Toast.LENGTH_SHORT).show();
            startActivity(intent);;
            finish();
        }
    }
}

Activity 전환 후 계정 정보 출력

로그인에 성공하면 저장했던 전역변수에 접근하여 Id 값과 Nickname을 Textview에 출력한다.

전환 이후 보여줄 Activity 정의

activity_second.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/hg_second_20"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.20" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/hg_second_30"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.30" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/hg_second_70"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.70" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/hg_second_90"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.90" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toTopOf="@+id/hg_second_30"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/hg_second_20">


        <TextView
            android:id="@+id/tv_second_userid"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text=" "
            android:textSize="18sp" />

        <TextView
            android:id="@+id/tv_second_nickname"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text=" "
            android:textSize="18sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toTopOf="@+id/hg_second_90"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/hg_second_70">

        <Button
            android:id="@+id/btn_second_logout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="로그아웃하기" />

        <Button
            android:id="@+id/btn_second_resign"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="탈퇴하기" />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

로그인에 성공하고서 넘어간 Activity에는 계정정보를 출력할 Textview와 카카오 로그아웃, 앱 연동 해제(회원탈퇴)를 수행할 버튼을 정의한다.

SecondActivity - 계정정보 출력

    private void initView() {
        GlobalHelper mGlobalHelper = new GlobalHelper();
        userInfo = mGlobalHelper.getGlobalUserLoginInfo();
        tvSecondUserID.setText("UserId : " + userInfo.get(0));
        tvSecondNickname.setText("Nickname : " + userInfo.get(1));
    }

전역변수로 List에 [Id, Nickname]을 저장했으므로 그에 맞게 조회해서 출력한다.

카카오톡 로그아웃 / 앱 연동 해제 (회원 탈퇴)

카카오톡 로그아웃, 계정연동은 다른 SNS 계정과 통합하여 동작하기 때문에 KakaoLogin이 아닌 GlobalAuthHelper.class에 메소드 정의 후 버튼 클릭 시 호출하는 방식으로 구현하였다. 로그아웃, 탈퇴하기 로직 작동 후 MainActivity로 보낸다.

SecondActivity - 로그아웃, 회원탈퇴

public class SecondActivity extends AppCompatActivity {
    private TextView tvSecondUserID, tvSecondNickname;
    private Button btnSecondLogout, btnSecondResign;
    private List userInfo = new ArrayList<>();

    Button.OnClickListener mLogoutListener = new Button.OnClickListener() {
        @Override
        public void onClick(View v) {
            GlobalAuthHelper.accountLogout(getApplicationContext(),SecondActivity.this);
        }
    };

    Button.OnClickListener mResignListener = new Button.OnClickListener() {
        @Override
        public void onClick(View v) {
            GlobalAuthHelper.accountResign(getApplicationContext(), SecondActivity.this);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        tvSecondUserID = findViewById(R.id.tv_second_userid);
        tvSecondNickname = findViewById(R.id.tv_second_nickname);
        btnSecondLogout = findViewById(R.id.btn_second_logout);
        btnSecondResign = findViewById(R.id.btn_second_resign);

        initView();

        btnSecondLogout.setOnClickListener(mLogoutListener);
        btnSecondResign.setOnClickListener(mResignListener);

    }

    private void initView() {
        GlobalHelper mGlobalHelper = new GlobalHelper();
        userInfo = mGlobalHelper.getGlobalUserLoginInfo();
        tvSecondUserID.setText("UserId : " + userInfo.get(0));
        tvSecondNickname.setText("Nickname : " + userInfo.get(1));
    }

    public void directToMainActivity(Boolean result) {
        Intent intent = new Intent(SecondActivity.this, MainActivity.class);
        if (result) {
            Toast.makeText(getApplicationContext(), "성공!", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(getApplicationContext(), "실패! 다시 로그인해주세요.", Toast.LENGTH_SHORT).show();
        }
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intent);
        finish();
    }
}

전체 코드

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.mysnsaccount">
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name=".GlobalHelper"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SecondActivity" />
        <activity android:name=".KakaoLogin" />
        <activity android:name=".NaverLogin" />
        <meta-data
            android:name="com.kakao.sdk.AppKey"
            android:value="@string/kakao_app_key" />
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

GlobalHelper.class

package com.example.mysnsaccount;

import android.app.Application;
import android.content.Context;

import com.kakao.auth.ApprovalType;
import com.kakao.auth.AuthType;
import com.kakao.auth.IApplicationConfig;
import com.kakao.auth.ISessionConfig;
import com.kakao.auth.KakaoAdapter;
import com.kakao.auth.KakaoSDK;

import java.util.ArrayList;
import java.util.List;

public class GlobalHelper extends Application {
    private static volatile GlobalHelper mInstance = null;
    private static List<String> mGlobalUserLoginInfo = new ArrayList<>();

    public static GlobalHelper getGlobalApplicationContext() {
        if (mInstance == null) {
            throw new IllegalStateException("This Application does not GlobalAuthHelper");
        }
        return mInstance;
    }

    public static List<String> getGlobalUserLoginInfo() {
        return mGlobalUserLoginInfo;
    }

    public static void setGlobalUserLoginInfo(List<String> userLoginInfo) {
        mGlobalUserLoginInfo = userLoginInfo;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
        KakaoSDK.init(new KakaoSDKAdapter());
    }

    public class KakaoSDKAdapter extends KakaoAdapter {
        @Override
        public ISessionConfig getSessionConfig() {
            return new ISessionConfig() {
                @Override
                public AuthType[] getAuthTypes() {
                    return new AuthType[]{AuthType.KAKAO_LOGIN_ALL};
                }

                @Override
                public boolean isUsingWebviewTimer() {
                    return false;
                }

                @Override
                public boolean isSecureMode() {
                    return false;
                }

                @Override
                public ApprovalType getApprovalType() {
                    return ApprovalType.INDIVIDUAL;
                }

                @Override
                public boolean isSaveFormData() {
                    return true;
                }
            };
        }

        @Override
        public IApplicationConfig getApplicationConfig() {
            return new IApplicationConfig() {
                @Override
                public Context getApplicationContext() {
                    return GlobalHelper.getGlobalApplicationContext();
                }
            };
        }
    }
}

MainActivity

package com.example.mysnsaccount;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import com.kakao.auth.Session;
import com.kakao.usermgmt.LoginButton;
import com.example.mysnsaccount.KakaoLogin.KakaoSessionCallback;

public class MainActivity extends AppCompatActivity {
    private Button mKakaoLoginBtn;
    private LoginButton mKakaoLoginBtnBasic;
    private KakaoSessionCallback sessionCallback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mKakaoLoginBtn = findViewById(R.id.btn_kakao_login);
        mKakaoLoginBtnBasic = findViewById(R.id.btn_kakao_login_basic);

        mKakaoLoginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mKakaoLoginBtnBasic.performClick();
            }
        });

        if (!HasKakaoSession()) {
            sessionCallback = new KakaoSessionCallback(getApplicationContext(), MainActivity.this);
            Session.getCurrentSession().addCallback(sessionCallback);
        } else if (HasKakaoSession()) {
            sessionCallback = new KakaoSessionCallback(getApplicationContext(), MainActivity.this);
            Session.getCurrentSession().addCallback(sessionCallback);
            Session.getCurrentSession().checkAndImplicitOpen();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 세션 콜백 삭제
        Session.getCurrentSession().removeCallback(sessionCallback);
    }

    private boolean HasKakaoSession() {
        if (!Session.getCurrentSession().checkAndImplicitOpen()) {
            return false;
        }
        return true;
    }

    public void directToSecondActivity(Boolean result) {
        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        if (result) {
            Toast.makeText(getApplicationContext(), "로그인 성공!", Toast.LENGTH_SHORT).show();
            startActivity(intent);;
            finish();
        }
    }
}

KakaoLogin.class

package com.example.mysnsaccount;

import android.app.Activity;
import android.content.Context;
import android.widget.Toast;

import com.kakao.auth.ISessionCallback;
import com.kakao.network.ErrorResult;
import com.kakao.usermgmt.UserManagement;
import com.kakao.usermgmt.callback.MeV2ResponseCallback;
import com.kakao.usermgmt.response.MeV2Response;
import com.kakao.util.exception.KakaoException;

import java.util.ArrayList;
import java.util.List;

public class KakaoLogin extends Activity {
    public static class KakaoSessionCallback implements ISessionCallback {
        private Context mContext;
        private MainActivity mainActivity;

        public KakaoSessionCallback(Context context, MainActivity activity) {
            this.mContext = context;
            this.mainActivity = activity;
        }

        @Override
        public void onSessionOpened() {
            requestMe();
        }

        @Override
        public void onSessionOpenFailed(KakaoException e) {
            Toast.makeText(mContext, "KaKao 로그인 오류가 발생했습니다. " + e.toString(), Toast.LENGTH_SHORT).show();
        }

        protected void requestMe() {
            UserManagement.getInstance().me(new MeV2ResponseCallback() {
                @Override
                public void onSessionClosed(ErrorResult errorResult) {
                    mainActivity.directToSecondActivity(false);
                }

                @Override
                public void onSuccess(MeV2Response result) {
                    List<String> userInfo = new ArrayList<>();
                    userInfo.add(String.valueOf(result.getId()));
                    userInfo.add(result.getKakaoAccount().getProfile().getNickname());
                    GlobalHelper mGlobalHelper = new GlobalHelper();
                    mGlobalHelper.setGlobalUserLoginInfo(userInfo);

                    mainActivity.directToSecondActivity(true);
                }
            });
        }
    }
}

SecondActivity

package com.example.mysnsaccount;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import java.util.ArrayList;
import java.util.List;

public class SecondActivity extends AppCompatActivity {
    private TextView tvSecondUserID, tvSecondNickname;
    private Button btnSecondLogout, btnSecondResign;
    private List<String> userInfo = new ArrayList<>();

    Button.OnClickListener mLogoutListener = new Button.OnClickListener() {
        @Override
        public void onClick(View v) {
            GlobalAuthHelper.accountLogout(SecondActivity.this);
        }
    };

    Button.OnClickListener mResignListener = new Button.OnClickListener() {
        @Override
        public void onClick(View v) {
            GlobalAuthHelper.accountResign(getApplicationContext(), SecondActivity.this);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        tvSecondUserID = findViewById(R.id.tv_second_userid);
        tvSecondNickname = findViewById(R.id.tv_second_nickname);
        btnSecondLogout = findViewById(R.id.btn_second_logout);
        btnSecondResign = findViewById(R.id.btn_second_resign);

        initView();

        btnSecondLogout.setOnClickListener(mLogoutListener);
        btnSecondResign.setOnClickListener(mResignListener);

    }

    private void initView() {
        GlobalHelper mGlobalHelper = new GlobalHelper();
        userInfo = mGlobalHelper.getGlobalUserLoginInfo();
        tvSecondUserID.setText("UserId : " + userInfo.get(0));
        tvSecondNickname.setText("Nickname : " + userInfo.get(1));
    }

    public void directToMainActivity() {
        Intent intent = new Intent(SecondActivity.this, MainActivity.class);
        startActivity(intent);
        finish();
    }
}

GlobalAuthHelper.class

package com.example.mysnsaccount;

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import com.kakao.auth.ApiErrorCode;
import com.kakao.auth.ISessionCallback;
import com.kakao.auth.Session;
import com.kakao.network.ErrorResult;
import com.kakao.usermgmt.UserManagement;
import com.kakao.usermgmt.callback.LogoutResponseCallback;
import com.kakao.usermgmt.callback.UnLinkResponseCallback;

public class GlobalAuthHelper {

    public static void accountLogout(Context context, SecondActivity activity) {
        if (Session.getCurrentSession().checkAndImplicitOpen()) {
            UserManagement.getInstance().requestLogout(new LogoutResponseCallback() {
                @Override
                public void onCompleteLogout() {
                    activity.directToMainActivity(true);
                }
            });
        }
    }

    public static void accountResign(final Context context, final SecondActivity activity) {
        if (Session.getCurrentSession().checkAndImplicitOpen()) {
            // 카카오 연동 해제
            UserManagement.getInstance().requestUnlink(new UnLinkResponseCallback() {
                @Override
                public void onFailure(ErrorResult errorResult) {
                    if (errorResult.getErrorCode() == ApiErrorCode.CLIENT_ERROR_CODE) {
                        Toast.makeText(context, "네트워크가 불안정합니다.", Toast.LENGTH_SHORT).show();
                        activity.directToMainActivity(false);
                    }
                }

                @Override
                public void onSessionClosed(ErrorResult errorResult) {
                    Toast.makeText(context, "세션이 닫혀있습니다.", Toast.LENGTH_SHORT).show();
                    activity.directToMainActivity(false);
                }

                @Override
                public void onSuccess(Long result) {
                    Toast.makeText(context, "카카오톡 연동 해제", Toast.LENGTH_SHORT).show();
                    activity.directToMainActivity(true);
                }
            });
        }
    }
}

참고자료

+ Recent posts