이 Codelab은 Kotlin 기반 Android 고급 교육 과정의 일부입니다. Codelab을 순서대로 진행하는 경우 학습 효과를 극대화할 수 있지만 순서를 바꿔 진행해도 괜찮습니다. 모든 과정 Codelab은 Kotlin Codelab의 고급 Android Codelab 방문 페이지에 나열되어 있습니다.
소개
Android 앱을 빌드하면 사용자의 로그인 지원을 통해 얻을 수 있는 여러 가지 이점이 있습니다. 사용자가 앱 내에서 ID를 만들 수 있도록 하면 더 많은 앱 참여 방법을 제공할 수 있습니다.
사용자는 개인 맞춤 계정을 사용해 인앱 환경을 맞춤설정하고 다른 사용자와 소통하며 다른 기기 (예: 웹 또는 새 휴대전화)에서 앱을 사용하는 동안 데이터를 유지하고 전송할 수 있습니다.
이 Codelab에서는 FirebaseUI 라이브러리를 사용하여 앱의 로그인을 지원하는 기본적인 방법을 알아봅니다. 무엇보다도 FirebaseUI 라이브러리를 사용하면 로그인 과정을 구축하고자 하는 개발자를 위한 간단한 도구가 마련되며 사용자 계정 관리 작업이 처리됩니다.
기본 요건
- Android 앱 빌드 기초
- LiveData 및 ViewModel
학습할 내용
- 프로젝트에 Firebase를 추가하는 방법
- Android 앱의 로그인을 지원하는 방법
- 앱의 현재 인증 상태를 관찰하는 방법
- 사용자를 로그아웃시키는 방법
실습할 내용
- Firebase Console을 사용하여 Firebase를 앱에 통합합니다.
- 로그인 기능을 구현합니다.
- 로그인한 사용자에게 앱에서 맞춤설정 추가
- 사용자 로그아웃 시키기
LiveData 및 ViewModel에 관해 자세히 알아보기
이 Codelab의 앱에는 LiveData 및 ViewModel에 관한 기본적인 이해가 필요합니다. 이러한 개념에 대한 간략한 개요를 원하는 경우 LiveData 및 ViewModel 개요를 읽어보세요.
Kotlin으로 Android 앱 개발하기 과정을 살펴보면 이 Codelab의 일부로 알아야 할 기본적인 Android 주제에 관해 알아볼 수 있습니다. 이 교육 과정은 Udacity 과정과 Codelab 과정으로 모두 제공됩니다.
이 Codelab에서는 재미있는 Android 사실을 보여주는 앱을 빌드해 봅니다. 무엇보다도 앱에 Login/Logout 버튼이 있습니다. 사용자가 앱에 로그인한 경우, Android에 표시되는 모든 사실 정보에는 맞춤 인사말을 추가하는 방법으로 사용자의 인사말이 포함됩니다.
샘플 앱을 다운로드한 후 다음 중 하나를 수행합니다.
또는 다음 명령어를 사용하여 명령줄에서 GitHub 저장소를 클론하고 저장소의 start
분기로 전환합니다.
$ git clone https://github.com/googlecodelabs/android-kotlin-login
중요: Firebase를 사용하기 위해 앱을 통합하므로 시작 앱에서 빌드 및 실행을 위해서는 몇 가지 설정이 필요합니다. Codelab의 다음 단계에서 이를 수행합니다.
1단계: Firebase 프로젝트 만들기
Firebase를 Android 앱에 추가하려면 먼저 Android 앱에 연결할 Firebase 프로젝트를 만들어야 합니다.
- Firebase Console에서 프로젝트 추가를 클릭합니다.
- 프로젝트 이름을 선택하거나 입력합니다. 프로젝트의 이름은 무엇이든 지정할 수 있지만 빌드 중인 앱과 관련된 이름을 선택해 보세요.
- 계속을 클릭합니다.
- Google 애널리틱스 설정을 건너뛰고 나중에 옵션을 선택할 수 있습니다.
- 프로젝트 만들기를 클릭하여 Firebase 프로젝트 설정을 완료합니다.
2단계: Firebase에 앱 등록
이제 Firebase 프로젝트가 준비되었으므로 Android 앱을 프로젝트에 추가할 수 있습니다.
- Firebase Console의 프로젝트 개요 페이지 중앙에 있는 Android 아이콘을 클릭하여 설정 워크플로를 시작합니다.
- Android 패키지 이름 필드에 앱의 애플리케이션 ID를 입력합니다. Firebase 프로젝트에 앱을 등록한 후에는 이 값을 추가하거나 수정할 수 없으므로 앱에서 사용 중인 ID를 입력해야 합니다.
- 애플리케이션 ID를 패키지 이름이라고도 합니다.
- 모듈(앱 수준) Gradle 파일(일반적으로
app/build.gradle
)에서 이 애플리케이션 ID(예:com.yourcompany.yourproject
)를 찾습니다. - 디버그 서명 인증서 SHA-1을 입력합니다. 명령줄 터미널에 다음 명령어를 입력하여 이 키를 생성할 수 있습니다.
keytool -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android
- Register app(앱 등록)을 클릭합니다.
3단계: 프로젝트에 Firebase 구성 파일 추가
앱에 Firebase Android 구성 파일을 추가합니다.
- google-services.json 다운로드를 클릭하여 Firebase Android 구성 파일(
google-services.json
)을 가져옵니다.
- 언제든지 다시 Firebase Android 구성 파일을 다운로드할 수 있습니다.
- 구성 파일에 문자가 추가되지 않았는지 확인하고 파일 이름을
google-services.json
로만 지정해야 합니다.
- 구성 파일을 앱의 모듈 (앱 수준) 디렉터리로 이동합니다.
4단계: Firebase 제품을 사용하도록 Android 프로젝트 구성
- 앱에서 Firebase 제품을 사용할 수 있도록 google-services 플러그인을 Gradle 파일에 추가합니다.
- 루트 수준 (프로젝트 수준) Gradle 파일(
build.gradle
)에서 Google 서비스 플러그인을 포함하는 규칙을 추가합니다. Google의 Maven 저장소도 있는지 확인합니다.
build.gradle
buildscript {
repositories {
// Check that you have the following line (if not, add it):
google() // Google's Maven repository
}
dependencies {
// ...
// Add the following line:
classpath 'com.google.gms:google-services:4.3.0' // Google Services plugin
}
}
allprojects {
// ...
repositories {
// Check that you have the following line (if not, add it):
google() // Google's Maven repository
// ...
}
}
- 모듈 (앱 수준) Gradle 파일 (일반적으로
app/build.gradle
)에서 파일 하단에 줄을 추가합니다.
app/build.gradle
apply plugin: 'com.android.application'
android {
// ...
}
// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services' // Google Play services Gradle plugin
4단계: Firebase 종속 항목 추가
이 Codelab에서 Firebase를 통합하는 주된 이유는 사용자를 만들고 관리하는 방법이기 때문입니다. 이 경우 로그인을 구현할 수 있는 Firebase 라이브러리를 추가해야 합니다.
- 앱에서 SDK를 사용할 수 있도록
build.gradle (Module:app)
파일에 다음 종속 항목을 추가합니다.firebase-auth
SDK를 사용하면 인증된 앱 사용자를 관리할 수 있습니다.
app/build.gradle:
implementation 'com.firebaseui:firebase-ui-auth:5.0.0'
- 프로젝트를 Gradle 파일과 동기화하여 앱에서 모든 종속 항목을 사용할 수 있는지 확인하세요. 메시지가 표시되지 않으면 Android 스튜디오에서 또는 툴바에서 File > Sync Project with Gradle Files를 선택합니다.
5단계: 앱 실행 및 코드 검사
- 에뮬레이터 또는 실제 기기에서 앱을 실행하여 개발이 시작되도록 환경이 올바르게 설정되었는지 확인합니다.
성공하면 홈 화면에 재미있는 Android 사실과 왼쪽 상단의 로그인 버튼이 표시됩니다. 로그인 버튼을 탭해도 아직 아무 작업도 실행되지 않습니다.
상위 수준에서 이는 여러 프래그먼트가 있는 단일 활동 앱입니다. MainFragment
는 아래 화면에 표시되는 모든 UI를 포함합니다. 후속 Codelab에서 LoginFragment
및 SettingsFragment
을 사용합니다.
- 코드를 숙지합니다. 특히 다음 사항을 참고하세요.
FirebaseUserLiveData
는 앱과 연결된 현재 Firebase 사용자를 관찰하기 위해 구현할 클래스입니다. 이후 단계에서 이 사용자 정보를 가져오기 위해FirebaseAuth
인스턴스를 진입점으로 사용합니다.MainFragment
는LoginViewModel
에 연결됩니다.LoginViewModel
는FirebaseUserLiveData
를 활용하여authenticationState
변수를 만들기 위해 구현할 클래스입니다. 그런 다음 이authenticationState
변수를 사용하여MainFragment
는 값을 관찰하여 적절하게 UI를 업데이트할 수 있습니다.
이 단계에서는 Firebase Console을 사용하여 앱에서 지원할 인증 방법을 설정합니다. 이 Codelab에서는 사용자가 제공한 이메일 주소 또는 Google 계정을 사용하여 로그인하도록 허용하는 데 중점을 둡니다.
- Firebase Console로 이동합니다. 참고: Firebase 추가 워크플로가 계속 표시된다면 왼쪽 상단에서 X를 클릭하여 Console로 돌아갑니다.
- 아직 프로젝트가 없다면 프로젝트를 선택합니다.
- 왼쪽 탐색 메뉴를 열고 Develop > Authentication을 선택합니다.
- 상단의 탐색 메뉴에서 로그인 방법 탭을 선택합니다.
- 이메일/비밀번호 행을 클릭합니다.
- 팝업에서 사용 설정됨 스위치를 전환하고 저장을 클릭합니다.
- 마찬가지로 Google 행을 클릭합니다.
- 사용 설정됨 스위치를 전환하여 프로젝트 지원 이메일을 입력하고 저장을 클릭합니다.
이 작업에서는 사용자를 위한 로그인 기능을 구현합니다.
MainFragment.kt
를 엽니다.MainFragment
의 레이아웃에서auth_button
를 확인합니다. 현재 사용자 입력을 처리하도록 설정되어 있지 않습니다.onViewCreated(),
에서onClickListener
를auth_button
에 추가하여launchSignInFlow()
를 호출합니다.
MainFragment.kt
binding.authButton.setOnClickListener { launchSignInFlow() }
MainFragment.kt
에서launchSignInFlow()
메서드를 찾습니다. 현재TODO
가 포함되어 있습니다.- 아래와 같이
launchSignInFlow()
함수를 완료합니다.
MainFragment.kt
private fun launchSignInFlow() {
// Give users the option to sign in / register with their email or Google account.
// If users choose to register with their email,
// they will need to create a password as well.
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build(), AuthUI.IdpConfig.GoogleBuilder().build()
// This is where you can provide more ways for users to register and
// sign in.
)
// Create and launch sign-in intent.
// We listen to the response of this activity with the
// SIGN_IN_REQUEST_CODE
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.build(),
MainFragment.SIGN_IN_REQUEST_CODE
)
}
이렇게 하면 사용자가 이메일 주소 또는 Google 계정으로 등록하고 로그인할 수 있습니다. 사용자가 이메일 주소로 등록하기로 선택한 경우, 생성한 이메일 및 비밀번호 조합은 앱마다 고유합니다. 즉, 이메일 주소 및 비밀번호 조합을 사용하여 앱에 로그인할 수 있지만 동일한 사용자 인증 정보를 사용해 다른 Firebase 지원 앱에도 로그인할 수 있습니다.
MainFragment.kt
에서 아래와 같이onActivityResult()
메서드를 구현하여 로그인 프로세스 결과를 수신 대기할 수 있습니다.SIGN_IN_REQUEST_CODE
로 로그인 프로세스를 시작했으므로SIGN_IN_REQUEST_CODE
가onActivityResult()
로 다시 전달될 때 필터링하여 로그인 프로세스 결과를 수신 대기할 수도 있습니다. 먼저 로그 구문으로 사용자가 성공적으로 로그인되었는지 확인합니다.
MainFragment.kt
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == SIGN_IN_REQUEST_CODE) {
val response = IdpResponse.fromResultIntent(data)
if (resultCode == Activity.RESULT_OK) {
// User successfully signed in
Log.i(TAG, "Successfully signed in user ${FirebaseAuth.getInstance().currentUser?.displayName}!")
} else {
// Sign in failed. If response is null the user canceled the
// sign-in flow using the back button. Otherwise check
// response.getError().getErrorCode() and handle the error.
Log.i(TAG, "Sign in unsuccessful ${response?.error?.errorCode}")
}
}
}
이제 앱에서 사용자 등록과 로그인을 처리할 수 있습니다.
- 앱을 실행하고 Login 버튼을 탭하면 로그인 화면이 표시되는지 확인합니다.
- 이제 이메일 주소와 비밀번호 또는 Google 계정으로 로그인할 수 있습니다.
- 로그인 후 UI에 변경되는 내용은 없지만 (다음 단계에서 UI 업데이트를 구현함) 모든 기능이 올바르게 작동하는 경우 등록 과정을 거친 로그 메시지
Successfully signed in user ${your name}!
가 표시됩니다. - 또한 Firebase Console로 이동하여 Develop > Authentication > Users로 이동하여 앱에 등록된 사용자가 1명 있는지 확인할 수 있습니다.
- 사용자가 앱 계정을 만들면 이 계정은 앱에만 연결되며 로그인 기능에 Firebase를 사용하는 앱에는 연결되지 않습니다.
이 작업에서는 인증 상태에 따라 UI 업데이트를 구현합니다. 사용자가 로그인하면 사용자 이름을 표시하여 홈 화면을 맞춤설정할 수 있습니다. 또한 사용자가 로그인할 때 Login 버튼이 Logout 버튼으로 업데이트됩니다.
- 이미 생성된
FirebaseUserLiveData.kt
클래스를 엽니다. 가장 먼저 해야 할 일은 앱의 다른 클래스에서 사용자가 로그인 또는 로그아웃했을 때를 알 수 있는 방법을 제공하는 것입니다. 하지만LiveData
값이 업데이트되지 않으므로 클래스가 아직 아무것도 하지 않습니다. FirebaseAuth
라이브러리를 사용 중이므로 FirebaseUI 라이브러리의 일부로 구현된FirebaseUser.AuthStateListener
콜백을 사용하여 로그인한 사용자의 변경사항을 수신 대기할 수 있습니다. 이 콜백은 사용자가 앱에서 로그인하거나 로그아웃할 때마다 트리거됩니다.FirebaseUserLiveData.kt
는authStateListener
변수를 정의합니다. 이 변수를 사용하여LiveData
의 값을 저장합니다.authStateListener
변수가 생성되었으므로 애플리케이션 상태에 따라 인증 상태 변경사항을 제대로 수신 대기하고 중지할 수 있습니다. 예를 들어 사용자가 앱을 백그라운드로 전환하면 앱은 인증 상태 변경사항 수신을 중지해야 하며 메모리 누수가 발생할 수 있도록 합니다.FirebaseUserLiveData
값이 현재 Firebase 사용자에 해당하도록authStateListener
를 업데이트합니다.
FirebaseUserLiveData.kt
private val authStateListener = FirebaseAuth.AuthStateListener { firebaseAuth ->
value = firebaseAuth.currentUser
}
LoginViewModel.kt
를 엽니다.LoginViewModel.kt
에서 방금 구현한FirebaseUserLiveData
객체를 기반으로authenticationState
변수를 만듭니다. 이authenticationState
변수를 만들면 다른 클래스에서 사용자가LoginViewModel
를 통해 로그인했는지 여부를 쿼리할 수 있습니다.
LoginViewModel.kt를 참조하세요.
val authenticationState = FirebaseUserLiveData().map { user ->
if (user != null) {
AuthenticationState.AUTHENTICATED
} else {
AuthenticationState.UNAUTHENTICATED
}
}
MainFragment.kt.
을 엽니다.MainFragment.kt
의observeAuthenticationState()
에서 방금LoginViewModel
에 추가한authenticationState
변수를 사용하고 적절하게 UI를 변경할 수 있습니다. 로그인한 사용자가 있으면authButton
에 Logout이 표시됩니다.
MainFragment.kt
private fun observeAuthenticationState() {
val factToDisplay = viewModel.getFactToDisplay(requireContext())
viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
when (authenticationState) {
LoginViewModel.AuthenticationState.AUTHENTICATED -> {
binding.authButton.text = getString(R.string.logout_button_text)
binding.authButton.setOnClickListener {
// TODO implement logging out user in next step
}
// TODO 2. If the user is logged in,
// you can customize the welcome message they see by
// utilizing the getFactWithPersonalization() function provided
}
else -> {
// TODO 3. Lastly, if there is no logged-in user,
// auth_button should display Login and
// launch the sign in screen when clicked.
}
}
})
}
- 사용자가 로그인한 경우
MainFragment
에서 제공하는getFactWithPersonalization()
함수를 활용하여 사용자에게 표시되는 환영 메시지를 맞춤설정할 수도 있습니다.
MainFragment.kt
binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
- 마지막으로, 로그인한 사용자가 없는 경우 (
authenticationState
이LoginViewModel.AuthenticationState.AUTHENTICATED
이외의 경우)auth_button
는 Login을 표시하고 클릭할 때 로그인 화면을 시작해야 합니다. 표시된 메시지 맞춤설정도 없어야 합니다.
MainFragment.kt
binding.authButton.text = getString(R.string.login_button_text)
binding.authButton.setOnClickListener { launchSignInFlow() }
binding.welcomeText.text = factToDisplay
모든 단계가 완료되면 최종 observeAuthenticationState()
메서드는 아래 코드와 비슷하게 표시됩니다.
MainFragment.kt
private fun observeAuthenticationState() {
val factToDisplay = viewModel.getFactToDisplay(requireContext())
viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
// TODO 1. Use the authenticationState variable you just added
// in LoginViewModel and change the UI accordingly.
when (authenticationState) {
// TODO 2. If the user is logged in,
// you can customize the welcome message they see by
// utilizing the getFactWithPersonalization() function provided
LoginViewModel.AuthenticationState.AUTHENTICATED -> {
binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
binding.authButton.text = getString(R.string.logout_button_text)
binding.authButton.setOnClickListener {
// TODO implement logging out user in next step
}
}
else -> {
// TODO 3. Lastly, if there is no logged-in user,
// auth_button should display Login and
// launch the sign in screen when clicked.
binding.welcomeText.text = factToDisplay
binding.authButton.text = getString(R.string.login_button_text)
binding.authButton.setOnClickListener {
launchSignInFlow()
}
}
}
})
}
- 앱을 실행합니다. 사용자의 로그인 여부에 따라 UI가 업데이트되어야 합니다. 모든 것이 제대로 작동하고 로그인하면 로그인 시 Android 화면을 표시하는 것 외에도 홈 화면에서 이름을 확인할 수 있습니다. 이제 Login 버튼에 Logout도 표시됩니다.
이 작업에서는 로그아웃 기능을 구현합니다.
앱에서 사용자가 로그인할 수 있으므로 사용자에게 로그아웃 방법을 제공해야 합니다. 다음은 코드 한 줄만으로 사용자를 로그아웃시키는 방법의 예입니다.
AuthUI.getInstance().signOut(requireContext())
MainFragment.kt
를 엽니다.MainFragment.kt
의observeAuthenticationState()
에 로그인한 사용자가 있을 때auth_button
이 올바르게 작동하도록 로그아웃 로직을 추가하세요. 메서드의 최종 결과는 아래 코드와 같습니다.
MainFragment.kt
private fun observeAuthenticationState() {
val factToDisplay = viewModel.getFactToDisplay(requireContext())
viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
when (authenticationState) {
LoginViewModel.AuthenticationState.AUTHENTICATED -> {
binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
binding.authButton.text = getString(R.string.logout_button_text)
binding.authButton.setOnClickListener {
AuthUI.getInstance().signOut(requireContext())
}
}
else -> {
binding.welcomeText.text = factToDisplay
binding.authButton.text = getString(R.string.login_button_text)
binding.authButton.setOnClickListener {
launchSignInFlow()
}
}
}
})
}
- 앱을 실행합니다.
- Logout(로그아웃) 버튼을 탭하고 사용자가 로그아웃되어 있는지 확인합니다. 버튼의 상태가 Login(로그인)으로 변경됩니다.
완성된 앱의 최종 버전은 https://github.com/googlecodelabs/android-kotlin-login에서 찾을 수 있습니다.
이 Codelab에서 배운 내용은 다음과 같습니다.
- gradle 파일에 필요한 종속 항목을 추가하고 Firebase Console에서 프로젝트를 설정하여 프로젝트에 Firebase를 추가하는 방법
- FirebaseUI 라이브러리를 사용하여 앱의 로그인을 구현하고 사용자의 로그인 허용 방법을 지정하는 방법 앱에서 사용자가 만든 계정은 해당 앱에만 적용되며 로그인 기능에 Firebase를 활용하는 모든 앱과 공유되지 않습니다.
LiveData
를 사용하여 앱의 현재 인증 상태를 관찰하는 방법- 사용자를 로그아웃시키는 방법
이 Codelab에서는 Android 앱의 로그인을 지원하는 기본적인 방법을 다루었습니다.
이 Codelab에서는 사용자가 이메일 주소로 등록하고 로그인할 수 있도록 허용했습니다. 하지만 FirebaseUI 라이브러리를 사용하면 전화번호로 로그인하는 등의 다른 인증 방법도 지원할 수 있습니다. FirebaseUI 라이브러리의 기능 및 제공되는 다른 기능을 활용하는 방법은 다음 리소스를 참고하세요.
로그인에 관한 권장사항을 자세히 알아보려면 다음 리소스를 확인하세요.
Codelab:
Android 개발자 문서:
동영상:
이 과정의 다른 Codelab에 관한 링크는 Kotlin Codelab의 고급 Android 방문 페이지를 참고하세요.