Grusie 안드로이드 개발 기술 블로그

[Android] 파이어베이스 이메일 회원가입 - 1 (이메일 링크 인증) 본문

안드로이드 개발/파이어베이스

[Android] 파이어베이스 이메일 회원가입 - 1 (이메일 링크 인증)

grusie 2024. 3. 15. 10:03
728x90
반응형
SMALL

파이어베이스 설정을 완료 하였으니, 이제 회원가입과 로그인을 다뤄야 할 차례이다.

제일 먼저 이메일로 인증을 하는 방법을 먼저 알아보자.

만약 파이어베이스 설정을 아직 완료하지 않았다면, 이 전 글을 보고 오도록 하자.

2024.03.13 - [안드로이드 개발] - [Android] 안드로이드 프로젝트에 Firebase 추가하기

 

[Android] 안드로이드 프로젝트에 Firebase 추가하기

프로젝트를 진행하다 보면, 회원가입, 로그인이 필요하거나, 통신을 통해 불러올 DB를 직접 만들어서 사용해야 하는 경우들이 많이 생긴다. 그럴 때, 서버 개발자가 없는 개인프로젝트의 경우들

grusie.tistory.com

 

회원가입 플로우

  • 이메일 입력
  • 이메일을 보내 이메일 인증
  • 비밀번호 입력 후 회원가입 완료

 

회원가입 프로세스를 따라가면서 개발을 하던 도중 문제를 발견했다

 

회원가입 때 사용자에게 이메일 인증을 요청하고 요청 받은 것을 토대로 회원가입을 진행해야 하는데, 파이어베이스 이메일 인증은 회원가입이 되어있는 사용자들에게만 이메일 인증 요청을 할 수 있는 것이다.

인터넷에 검색을 해본 결과 나와 같은 문제로 고민한 글이 있어 첨부하려고 한다.

 

https://velog.io/@tpgus758/firebase%EB%A1%9C-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EC%9D%B8%EC%A6%9D%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%98%EB%A9%B4%EC%84%9C-%EB%8A%90%EB%82%80-%ED%95%9C%EA%B3%84

 

firebase로 이메일 로그인, 인증을 구현하면서 맞닥뜨린 문제들

개인 프로젝트를 진행하며 로그인을 구현해야 했다.로그인을 구현하려면 인증을 담당하는 서버가 필요한데 나에겐 따로 서버를 구현할 여유가 없었다.이전에 팀 프로젝트를 진행하며 서버 코

velog.io

 

해당 블로그에 나와있는 방법과 동일한 방법대로 진행할 것이다.

 

요약

  1. 사용자가 이메일을 입력한다.
  2. 사용자가 인증 버튼을 클릭한다.
  3. 인증 버튼 클릭시, 임의의 비밀번호를 난수로 생성하고 이를 암호화하여 입력된 이메일, 암호화된 비밀번호를 통해 임의로 회원가입을 시킨다.
  4. firebase는 회원 가입시 자동으로 로그인 된다. 따라서 로그인된 사용자 정보를 얻을 수 있다.(아래의 공식문서)이렇게 얻게된 사용자 정보를 통해 sendEmailVerification를 호출하며 사용자 정보를 전달한다.
  5. 입력된 이메일로 인증 메일이 전송된다.
  6. 이메일 인증이 완료된다면 미리 가입시킨 임의의 계정을 삭제시킨다.
  7. 이메일 인증 이후 사용자가 최종적으로 회원가입버튼을 눌렀을 때 실제 가입이 완료된다.

 

구현하면서 발생할 수 있을 문제점

  • 사용자 계정을 임의로 만들었다가 삭제하기에, 중간에 실패할 경우 회원가입이 제대로 진행이 안 될 수 있다.
  • 인증버튼을 누르고, 인증을 진행하지 않을 경우, 어떻게 처리해야 하는가?

이렇게 두 가지 문제점을 안고 진행해 보려고 한다.

 

의존성

모듈(앱 수준)에 다음 의존성들을 추가한다.

dependencies {
    // Import the BoM for the Firebase platform
    implementation(platform("com.google.firebase:firebase-bom:32.3.1"))

    // Add the dependency for the Firebase Authentication library
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation("com.google.firebase:firebase-auth-ktx")

    // Also add the dependency for the Google Play services library and specify its version
    implementation("com.google.android.gms:play-services-auth:20.7.0")
}

 

1. 파이어베이스 프로젝트에 이메일 인증 로그인 설정을 해준다.

 

이메일 인증을 보내기 위해서는 actionCodeSettings를 구현해주어야 한다.

이메일 링크를 만드는 방법에 대한 안내를 제공하는 객체라고한다.

  • url: 삽입할 딥 링크 및 함께 전달할 추가 상태입니다. 승인된 도메인의 Firebase Console 목록에서 링크의 도메인을 허용 목록에 추가해야 하며 로그인 방법 탭(인증 -> 로그인 방법)으로 이동하여 확인할 수 있다. 사용자 기기에 앱이 설치되어 있지 않고 앱을 설치할 수 없는 경우에 인증 링크는 사용자를 이 URL로 리디렉션한다.
  • androidPackageName  IOSBundleId: Android 또는 Apple 기기에서 로그인 링크를 열 때 사용하는 앱이다. 모바일 앱을 통해 이메일 작업 링크를 열기 위해 사용할 수 있다.
  • handleCodeInApp: true로 설정한다. 다른 대역 외 이메일 작업(비밀번호 재설정 및 이메일 인증)과 달리 이 로그인 작업은 항상 앱에서 완료해야 한다. 그 이유는 인증 과정 마지막에 사용자가 로그인하고 사용자의 인증 상태를 앱에서 유지해야 하기 때문이다.
  • dynamicLinkDomain: 프로젝트 하나에 커스텀 동적 링크 도메인이 여러 개 정의된 경우 지정된 모바일 앱을 통해 링크를 열 때 사용할 도메인을 지정한다(예: example.page.link). 지정하지 않으면 첫 번째 도메인이 자동으로 선택된다.

여기서 주의깊게 봐야 할 것은 동적 링크를 구현해서, 메일의 링크를 클릭하면, 모바일 페이지로 이동 할 수 있다는 것이다.

이메일 인증을 위해서 받은 메일에 동적 링크를 첨부하여, 사용자가 클릭 했을 시 인증 페이지로 넘어와 사용자가 인증을 완료 할 수 있도록 처리하는 것이 중요하다.

 

하지만 이번엔 시간 관계 상 이메일을 보내는 것 까지만 진행 하고, 다음 글부터 동적 링크를 구현해서 실제로 인증을 완료하는 것 까지 작성 할 예정이다.

 

UseCase

class EmailSendUseCase(private val repository: EmailAuthRepository) {
    suspend operator fun invoke(email: String) {
        val actionCodeSettings = actionCodeSettings {
            url = "https://www.example.com/finishSignUp?cartId=1234"
            // This must be true
            handleCodeInApp = true
            setAndroidPackageName(
                "com.grusie.policyInfoApp",
                true, // installIfNotAvailable
                "24", // minimumVersion
            )
        }

        try {
            Firebase.auth.sendSignInLinkToEmail(email, actionCodeSettings).await()
        } catch (e:Exception) {
            throw e
        }
    }
}

 

이메일을 보내기 위한 actionCodeSettings를 구현한 UseCase이다.

비즈니스 로직은 UseCase에서 구현하는 게 올바르기에 이렇게 처리하였고, sendSignInLinkToEmail을 통해 이메일을 보낸다.

콜백 지옥을 벗어나게 하기 위해, Task의 await()를 사용하였고, 함수 자체가 suspend함수이기에 try catch로 묶어서 처리하였다.

 

ViewModel

fun sendEmail() {
    if(_idText.value.trim().isEmpty()){
        setSignUpUiState(SignUpUiState.Alert(Constant.ERROR_CODE_EMPTY))
    }
    else if(!isValidEmail()){
        setSignUpUiState(SignUpUiState.Alert(Constant.ERROR_CODE_FORMAT))
    } else {
        viewModelScope.launch {
            setSignUpUiState(SignUpUiState.Loading)
            try {
                useCases.emailSendUseCase(_idText.value)
                changeVerifyChecked(false)
                setSignUpUiState(SignUpUiState.SuccessSendEmail)
            } catch (e: Exception) {
                setSignUpUiState(SignUpUiState.Error(e))
            }
        }
    }
}

인증 메일을 보내기 위해 사용하는 함수로서 인증 버튼을 클릭 했을 때 동작하며, 처음엔 Loading상태였다가, 이메일을 성공적으로 보냈으면 VerifyChecked를 false로 바꾼뒤 uiState를 변경해준다.

VerifyChecked 변수는 처음엔 null이여서 사용자에게 보여지지 않다가, 값이 변경되면 false로 변경되어 보이게 되는 것.

인증이 전부 완료 되면 true로 바꾸어 주면 된다.

인증 메일을 전송하기 위해서는 이메일이 필요하기에, 이메일이 비어있거나, 이메일 형식에 맞지 않으면, 처리를 하지 않도록 구현하였다.

 

private fun isValidEmail(): Boolean {
    val emailRegex = Regex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
    return emailRegex.matches(_idText.value)
}

이메일 형식인지 아닌지를 판별해주는 정규표현식이다.

 

UI

@Composable
fun EmailSignUp(
    idText: String,
    pwText: String,
    changeIdText: (String) -> Unit = {},
    changePwText: (String) -> Unit = {},
    authBtnOnClick: () -> Unit = {},
    signUpBtnOnClick: () -> Unit = {},
    verifyMsgFlag: Boolean = false,
    verifyChecked: Boolean = false
) {
    val interactionSource = remember { MutableInteractionSource() }
    IdTextField(
        idText = idText,
        changeIdText = changeIdText,
        verifyFlag = true,
        verifyOnClick = { authBtnOnClick() })

    if (verifyMsgFlag) {
        Text(
            text = if (verifyChecked) stringResource(id = R.string.str_verified_checked) else stringResource(
                id = R.string.str_send_email
            ), fontSize = 12.sp,
            color = if (verifyChecked) Blue500 else Color.Gray
        )
    }

    Spacer(modifier = Modifier.padding(top = dimensionResource(id = R.dimen.margin_default)))

    PasswordTextField(pwText = pwText, changePwText = changePwText)

    Spacer(modifier = Modifier.padding(dimensionResource(id = R.dimen.margin_default)))

    Button(
        onClick = { signUpBtnOnClick() },
        modifier = Modifier
            .background(Color.Black)
            .fillMaxWidth(),
        colors = ButtonDefaults.buttonColors(
            containerColor = Color.Black,
            contentColor = Color.White,
            disabledContainerColor = Color.Gray,
            disabledContentColor = Color.White
        ),
        interactionSource = interactionSource
    ) {
        Text(text = stringResource(id = R.string.str_signup), color = Color.White)
    }
}

회원가입 화면을 나타내는 곳으로 아이디(이메일)을 입력받는 텍스트필드와, 비밀번호를 입력받는 텍스트필드가 존재한다. 추가로 인증에 관한 텍스트를 넣도록 처리하였다.

 

이메일은 이렇게 보내면 되는 것 같은데, 테스트를 해보았더니 에러가 발생한다.

The configured custom domain is not allowlisted. Please allowlist the domain in the Firebase console -> Authentication -> Settings -> Authorized domains tab. [ Domain not whitelisted by project ]

 

도메인이 승인이 안 되어 있다는데, 적혀있는대로 들어가보면 이렇게 되어있어서, 찾아보니 동적 링크를 생성 후 도메인에 등록을 해야 사용이 가능하다는 것이다.

 

참고

Please activate Dynamic Links in the Firebase Console and agree to the terms and conditions. [ FDL domain is not configured ]

동적링크를 활성화하지 않으면 이런 에러가 발생한다.

도메인 추가하는 김에 동적링크를 만들어서 추가하는 게 좋을 것 같다.

파이어베이스 콘솔에서 Dynamic Links를 사용설정 해주자.

동적링크 사용설정

파이어베이스 콘솔에 들어가 다이나믹 링크를 생성해준다.

동적링크가 deprecated되었다고 한다. 다른 대안이 나올 때 까지 우선 사용하도록 하자

 

프리픽스와 동적 링크를 생성한다.

인증에 관한 링크이기에, verify를 하위에 붙혔다.

 

Authentication 승인 도메인

authentication의 설정에 들어가 도메인을 추가한다.

 

아까 만들어둔, actionCodeSettingsurl도 마찬가지로 수정해주어야 한다.

val actionCodeSettings = actionCodeSettings {
    url = "https://policyinfo.page.link"
    // This must be true
    handleCodeInApp = true
    setAndroidPackageName(
        "com.grusie.policyInfoApp",
        true, // installIfNotAvailable
        "24", // minimumVersion
    )
}

 

인증된 사용자만 가능하다고 하였었기에, 임시로 사용자로 추가한 뒤, 이메일을 보내보자

임시 인증 사용

This project's quota for this operation has been exceeded. [ Exceeded daily quota for email sign-in. ]

할당량을 다 채워서 그만하라는 메세지가 떴다. 볼 수 있는 에러는 다 보는 것 같다.

 

만들면서 생긴 의문점

이메일 전송 한도

이메일 링크 로그인 이메일은 하루에 5개가 한계라고 한다...

주소 확인 이메일로도 충분하지 않은가? 싶어서 다음에 다시 해보려고 한다.

 

참고

https://firebase.google.com/docs/auth/android/email-link-auth?hl=ko&_gl=1*16g88r*_up*MQ..*_ga*ODI0OTg2MjQ2LjE3MTAzOTI2NDY.*_ga_CW55HF8NVT*MTcxMDM5MjY0Ni4xLjAuMTcxMDM5MjY0Ni4wLjAuMA..

 

Android에서 이메일 링크를 사용하여 Firebase에 인증  |  Firebase Authentication

Google I/O 2023에서 Firebase의 주요 소식을 확인하세요. 자세히 알아보기 의견 보내기 Android에서 이메일 링크를 사용하여 Firebase에 인증 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를

firebase.google.com

 

후기

파이어베이스를 제대로 공부하면서 사용해보기는 처음인 것 같아 시간이 오래 걸렸다.

이메일 인증에 대한 고민을 많이 하게 되는 것 같다. 이메일 링크 로그인 이메일 한도를 5개로 제한했다는 게 상심이 컸다.

여태까지 했던 수고가 아까워서 기록을 남긴다.

다음 게시글에서 마저 만들도록 하자.

반응형
LIST