질문 정리

안드로이드 컴포즈 페이스북 로그인 연동 정리

five2week 2024. 5. 26. 15:24

1. 필요한 의존성 추가

프로젝트의 build.gradle 파일에 필요한 의존성을 추가합니다.

plugins {
    id("com.android.application") version "8.1.1" apply false
    id("org.jetbrains.kotlin.android") version "1.8.10" apply false
    id("com.google.dagger.hilt.android") version "2.45" apply false
    id("com.google.gms.google-services") version "4.4.0" apply false
    id("com.google.firebase.crashlytics") version "2.9.9" apply false
}
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("kotlin-kapt")
    id("com.google.dagger.hilt.android")
    id("com.google.gms.google-services")
}

dependencies {
    // Firebase Authentication
    implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
    implementation("com.google.firebase:firebase-auth-ktx")
    implementation("com.google.android.gms:play-services-auth:20.7.0")
    // Facebook Android SDK
    implementation("com.facebook.android:facebook-android-sdk:17.0.0")
}

2. FaceBook 및 Firebase 프로젝트 설정

페이스북 콘솔에서 프로젝트를 생성하고, 빠른 시작을 통해 설정을 완료합니다.

  • 개발자 계정 생성 및 로그인
  • 새 앱 만들기
  • 앱 아이디 생성
  • 해시키 구하기
  • 안드로이드 플랫폼 추가 및 앱 인증 설정 허용
  • 앱 아이디 및 시크릿 키 확인
// main activity에서 hashkey 구하는 코드
val information =
    packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)
val signatures = information.signingInfo.apkContentsSigners
val md = MessageDigest.getInstance("SHA")
for (signature in signatures) {
    val md: MessageDigest
    md = MessageDigest.getInstance("SHA")
    md.update(signature.toByteArray())
    var hashcode = String(Base64.encode(md.digest(), 0))
    Log.d("hashcode", "" + hashcode)
}

 

 

Firebase 콘솔에서 프로젝트를 설정하고, 페이스북 로그인을 위한 설정을 완료합니다. 이 과정은 다음을 포함합니다:

  • Firebase 프로젝트 생성
  • 앱 등록 및 google-services.json 파일을 앱에 추가
  • Authentication 섹션에서 페이스북 로그인 방법 활성화

  • 페이스북 앱 ID와 앱 시크릿 입력

Meta for Developers > App > 앱 설정 > 기본설정 > 앱ID, 앱 시크릿코드를 입력합니다.

3. Facebook SDK 초기화

앱의 Application 클래스에서 Facebook SDK를 초기화하고 Manifest에 다음의 코드를 추가합니다.

@HiltAndroidApp
class ApplicationClass : Application() {
    override fun onCreate() {
        super.onCreate()
        FirebaseApp.initializeApp(this)
        FacebookSdk.setClientToken(getString(R.string.fb_client_token))
        FacebookSdk.setApplicationId(getString(R.string.facebook_app_id))
        FacebookSdk.sdkInitialize(applicationContext)
        AppEventsLogger.activateApp(this)
    }
}
// manifest > application
<meta-data android:name="com.facebook.sdk.ApplicationClientToken" android:value="@string/fb_client_token"/>
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id"/>

<activity
    android:name="com.facebook.FacebookActivity"
    android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|smallestScreenSize|orientation"
    android:label="@string/app_name"
    tools:replace="android:configChanges"/>

<activity
    android:name="com.facebook.CustomTabActivity"
    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" />
        <data android:scheme="@string/fb_login_protocol_scheme"/>
    </intent-filter>
</activity>

4. 로그인 로직 구현

Compose UI에서 사용자가 로그인 버튼을 누를 때 호출될 로그인 로직을 구현합니다.

@Composable
fun SocialLoginElement(modifier: Modifier = Modifier, callbackManager: CallbackManager) {
    val context = LocalContext.current
    val viewModel: LoginFormViewModel = hiltViewModel()

    val callback = object : FacebookCallback<LoginResult> {
        override fun onSuccess(result: LoginResult) {
            viewModel.loginFacebook(result)
        }

        override fun onCancel() {
            LogUtils.e("Facebook login onCancel")
        }

        override fun onError(error: FacebookException) {
            LogUtils.e(error.message.toString())
        }
    }

    val loginManager = remember { LoginManager.getInstance() }

    Row(
        modifier = modifier
            .wrapContentWidth()
    ) {
        ImageButton(R.drawable.facebook_icon, "Facebook") {
            loginManager.logInWithReadPermissions(
                context as Activity,
                listOf("email", "public_profile")
            )
        }

				...

        DisposableEffect(Unit) {
            loginManager.registerCallback(callbackManager, callback)
            onDispose {
                loginManager.unregisterCallback(callbackManager)
            }
        }
    }
}

5. Firebase 인증 연동

Facebook 로그인 성공 후, Firebase 인증을 통해 로그인을 완료합니다.

fun loginFacebook(result: LoginResult) {
    val firebaseAuth = FirebaseAuth.getInstance()
    val accessToken = result.accessToken

    try {
        val credential = FacebookAuthProvider.getCredential(accessToken.token)
        firebaseAuth.signInWithCredential(credential).addOnCompleteListener { authTask ->
            if (authTask.isSuccessful) {
                handleFirebaseAuthentication(accessToken, firebaseAuth)
            } else {
                LogUtils.e("Firebase authentication failed: ${authTask.exception?.message}")
            }
        }
    } catch (e: Exception) {
        LogUtils.e("Facebook sign in failed: ${e.stackTraceToString()}")
    }
}

private fun handleFirebaseAuthentication(accessToken: AccessToken, firebaseAuth: FirebaseAuth) {
    firebaseAuth.currentUser?.getIdToken(true)?.addOnCompleteListener { tokenTask ->
        if (tokenTask.isSuccessful) {
            val idToken = tokenTask.result.token ?: ""
            fetchFacebookEmail(accessToken, idToken)
        } else {
            LogUtils.e("Firebase ID token retrieval failed: ${tokenTask.exception?.message}")
        }
    }
}

private fun fetchFacebookEmail(accessToken: AccessToken, idToken: String) {
    GraphRequest.newMeRequest(accessToken) { jsonObject, response ->
        if (response?.error != null) {
            LogUtils.e("Graph Request failed: ${response.error?.errorMessage}")
            return@newMeRequest
        }

        val email = jsonObject?.optString("email", "No email found")
        _userType.value = UserType(SignUpType.FACEBOOK, email ?: "", idToken)
        performLoginSocial(SignUpType.FACEBOOK.name.lowercase(), idToken, accessToken.token)
    }.executeAsync()
}

6. 콜백 처리

페이스북 SDK를 사용할 때 LoginManager의 logInWithReadPermissions 메소드는 Intent를 반환하지 않습니다. 그래서 onActivityResult을 오버라이드하여 로직을 처리했습니다.

@AndroidEntryPoint
class TabBarActivity : ComponentActivity() {
    private lateinit var callbackManager: CallbackManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        callbackManager = CallbackManager.Factory.create()

        setContent {
	        ...
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        LogUtils.e("onActivityResult", facebook1)
        callbackManager.onActivityResult(requestCode, resultCode, data)
    }
}