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)
}
}
'질문 정리' 카테고리의 다른 글
State와 StateFlow 차이 (0) | 2024.05.30 |
---|---|
PDF 파일 다운로드를 사용자에게 알리기 (0) | 2024.05.29 |
위임하는 방식(By)과 직접 할당하는 방식(=)의 차이는 무엇일까? (0) | 2024.05.24 |
안드로이드 컴포즈 구글 로그인 연동 정리 (0) | 2024.04.28 |
연속된 버튼 클릭, api 요청 처리법 (1) | 2024.04.24 |