[안드로이드 스튜디오] Firebase를 활용한 인증 구현 및 계정설정 기능 만들기

2020. 11. 24. 02:31졸업프로젝트/안드로이드 스튜디오

전체 project 설명:

1. 대형 폐기물 처리를 도와주는 어플리케이션

2. 이미지 인식 기능을 통한 품목 분석 및 AR ruler를 통한 품목 크기 측정 기능

3. 그 외로 촬영한 이미지를 기반으로 한 신고필증 작성 기능 도입

앱 흐름:

1차 흐름도

 

 

이번 포스팅에서는 주황색 박스로 쳐놓은 흐름에 대해서 인증을 구현하고 계정을 관리하는 기능을 구현하고자 한다.

 

 

 

<로그인 화면>

1. 로그인이 되어 있다면 메인 홈으로 이동

2. 로그인이 되어 있지 않다면 로그인 페이지에서 로그인 진행

 

<계정설정>

1. 로그아웃 -> 로그아웃 진행 후 다시 로그인 화면

2. 계정탈퇴 -> 탈퇴하시겠습니까? 질문 후에 yes면 탈퇴 완료 처리 후 다시 로그인 화면

 


1. 연결한 Firebase를 활용한 인증 구현

설명 문서에 나와있는 로그인 인텐트를 만드는 코드를 활용할 예정.

-> 여기에는 지금 arrayLishOf 내용을 조면 email, phone, google, facebook, twitter에 대한 인증 정보를 받아오고 있다. 전 포스터에서 언급한 것과 같이 현재 나는 email, google 인증 정보만을 대상으로 진행하고 있어서 email, google에 해당하는 줄만 남기고 다른 줄은 삭제.

 

 

또한, SNS 로그인 자체가 인터넷 사용이므로 "AndroidManifest.xml"에 인터넷 권한 추가하기


로그인

로그인 화면의 flow는 로그인이 되어있는지를 먼저 확인한 후(MainActivity에서 진행), 로그인이 되어 있는 경우 메인 홈으로 이동한다. 로그인이 되어 있지 않은 경우 로그인 페이지를 호출하여 계정을 생성하거나 로그인을 진행하여야 한다.

 

1. checkPreviousLogin() 함수를 호출하여 로그인 여부를 확인한다.

FirebaseAuth를 통해서 받아오는 user가 null인지 비교하여 로그인 여부를 확인한다.

 private val RC_SIGN_IN = 1000

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        checkPreviousLogin()

    }
    
    private fun checkPreviousLogin(){
        val user = FirebaseAuth.getInstance().currentUser
        
        //login이 되어있지 않으면 로그인 페이지 호출
        if(user == null)showLoginWindow()

        //login이 되어있는 경우 바로 MainHomeActivity로 이동
        else moveToMainHome()
    }

2. 로그인이 되어있지 않다면 로그인 페이지 호출

해당 코드는 앞전 포스터의 설명문서에서 이어지는 로그인 코드 부분을 활용한다. 위에서 언급했듯이 현재는 구글과 이메일을 활용한 로그인만을 사용할 예정이라서 두가지만 남기고 다른 providers는 삭제했다.

private fun showLoginWindow(){
        // Choose authentication providers
        val providers = arrayListOf(
                AuthUI.IdpConfig.EmailBuilder().build(),
                AuthUI.IdpConfig.GoogleBuilder().build())

        // Create and launch sign-in intent
        startActivityForResult(
                AuthUI.getInstance()
                        .createSignInIntentBuilder()
                        .setAvailableProviders(providers)
                        .build(),
                RC_SIGN_IN
        )
    }

 

로그인 과정이 완료되면 onActivityResult로 결과가 수신된다. onActivityResult의 parameter에서 "data: Intent?"의 '?'는 'null'일수도 있다는 것을 의미한다.

'Activity.RESULT_OK'를 통해서 결과를 확인한다.

 

○ 로그인 성공시 FirebaseAuth를 통해서 user를 받아온다(val user = FirebaseAuth.getInstance().currentUser).

-> user를 통해서 isAnonymous, email, displayName, photoUrl 등 다양한 user 정보를 가져올 수 있다. UI에서 user 정보가 필요할 경우 활용할 것이므로 추후 활용을 위해서 남겨두도록 하겠음.

로그인이 성공하고 나면 메인화면페이지인 MainHomeActivity로 이동시켜준다.

 

 로그인 실패 시, 로그인 실패를 알려주는 toast message를 띄어준다.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == RC_SIGN_IN) {
            val response = IdpResponse.fromResultIntent(data)

            if (resultCode == Activity.RESULT_OK) {
                // Successfully signed in
                val user = FirebaseAuth.getInstance().currentUser
                startActivity(Intent(this, MainHomeActivity::class.java))
            } else {
                Toast.makeText(this, "로그인 실패, 로그인을 다시 시도해주세요", Toast.LENGTH_LONG).show()
            }
        }
    }

 

3. 로그인이 되어있는 경우 바로 메인홈으로 이동

메인홈 페이지를 위한 클래스로 MainHomeActivity class를 만들었고, 로그인이 되어있는 경우 메인홈 activity를 시작하기 위하여 startActivity를 이용한다.

    private fun moveToMainHome(){
        startActivity(Intent(this, MainHomeActivity::class.java))
    }

 


로그아웃, 계정설정

로그인이 되면 MainHomeActivity로 넘어가게 되는데, 해당 페이지 layout에 "계정설정" textview를 추가한 후, "계정설정"을 선택하면 로그아웃 및 계정을 관리할 수 있는 페이지로 이동하고자 한다.

해당 textview의 id는 account_setting으로 설정하였고, MainHomeActivity class에서 account_setting.setOnClickListener를 활용하여 "계정설정"이 클릭되면 AccountSettingActivity를 시작한다.

각각의 수치 및 string 설정은 res-values-dimense.xml, res-values- strings.xml에서 아래와 같이 미리 설정하여 활용하였다.

res-values- strings.xml 에 미리 추가한 '계정 설정' string

    <TextView
        android:id="@+id/account_setting"
        android:layout_width="match_parent"
        android:layout_height="@dimen/normal_height"
        android:gravity="end"
        android:paddingRight="@dimen/text_size"
        android:text="@string/about_account"
        android:textColor="@color/text_color"
        android:textSize="@dimen/title_size"
        android:textStyle="normal" />

 

MainHomeActivity class에서 account_setting.setOnClickListener를 활용하여 "계정설정"이 클릭되면 AccountSettingActivity를 시작한다.

account_setting.setOnClickListener {
            startActivity(Intent(this, AccountingSettingActivity::class.java))
        }

 

 

계정설정 창을 만들어서 로그아웃 기능과 계정 삭제 기능을 추가하고자 AccountingSettingActivity class를 생성하였다.

class AccountingSettingActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_accounting_setting)

        setUpListener()
    }

 


계정설정 페이지의 레이아웃은 우선 심플하게 아래 그림과 같이 구성하였다.

 

해당 페이지에서

1. back_arrow(왼쪽 상단 이미지) 클릭 시 -> 이전 화면으로 이동

2. 로그아웃 클릭 시 -> 로그아웃 진행

3. 계정 탈퇴 클릭 시 -> 계정 탈퇴를 진행할 것인지 확인하는 dialog

 

1. back_arrow(왼쪽 상단 이미지) 클릭

Imageview의 id는 account_setting_back으로 설정했다.

해당 이미지는 res - drawble에 추가한 "back_arrow.jpg"이다. 이미지 추가 방법에 대해서는 추후 다른 포스팅에서 다루도록 하겠다.

        <ImageView
            android:id="@+id/account_setting_back"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:paddingLeft="15dp"
            android:src="@drawable/back_arrow" />

2. 로그아웃 클릭

Textview의 id는 account_setting_logout으로 설정했다.

    <TextView
        android:id="@+id/account_setting_logout"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:background="@drawable/button_background"
        android:gravity="center"
        android:text="로그아웃"
        android:textSize="16dp"
        android:textStyle="bold" />

3. 계정 탈퇴 클릭

Textview의 id는 account_setting_delete로 설정했다.

    <TextView
        android:id="@+id/account_setting_delete"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="16dp"
        android:background="@drawable/button_background"
        android:gravity="center"
        android:text="계정 탈퇴"
        android:textSize="16dp"
        android:textStyle="bold" />

 

onCreate는 Acitivity가 처음 실행 되는 상태에서 제일 먼저 호출되는 메소드다. 여기에서 레이아웃을 생성하고 초기화 컴포넌트를 불러오는 작업을 해준다. 현재 AccountingSettingActivity class의 OnCreate 메소드에는 setContentView로 화면 디자인을 정의해놓은 "activity_accounting_setting.xml" 파일을 불러온다. 해당 메소드 내에서 호출한 setUpListener()에 대해서 살펴보려고 한다.

 

위에서 언급한 각각의 경우에

1. back_arrow(왼쪽 상단 이미지) 클릭 시 -> account_setting_back.setOnClickListener 메소드 호출한 후 뒤로 가기 버튼을 눌렀을 때 처리해주는 함수 onBackPressed() 호출.

2. 로그아웃 클릭 시 -> account_setting_logout.setOnClickListener 메소드 호출한 후 signoutAccount() 호출

3. 계정 탈퇴 클릭 시 -> account_setting_delete.setOnClickListener 메소드 호출한 후 showDeleteDialgo() 호출

private fun setUpListener() {
        account_setting_back.setOnClickListener { onBackPressed() }

        account_setting_logout.setOnClickListener { signoutAccount() }

        account_setting_delete.setOnClickListener { showDeleteDialog() }
    }

로그아웃, 계정 탈퇴 코드는 설명 문서에서 제공하는 메소드를 활용한다.


로그인 페이지(MainActivity)로 이동하기 위한 moveToMainActivity().

private fun moveToMainActivity() {
    startActivity(Intent(this, MainActivity::class.java))
}

로그아웃을 위한 signoutAccount()

-> 로그아웃을 하게 되면 moveToMainActivity()를 통해서 로그인 페이지로 돌아가게 되고, 로그아웃 했다는 toast message를 띄워준다.

private fun signoutAccount() {
        AuthUI.getInstance()
            .signOut(this)
            .addOnCompleteListener {
                moveToMainActivity()
                Toast.makeText(this, "로그아웃 하셨습니다", Toast.LENGTH_SHORT).show()
            }
    }

계정탈퇴를 위한 showDeleteDialog()

진짜로 탈퇴를 할 것인지 확인하는 dialog를 생성하기 위해 AccountDeleteDialog를 생성하고, DialogFragment()를 상속받는다.

https://developer.android.com/guide/topics/ui/dialogs?hl=ko

 

 

 

 

 

 

 

왼쪽의 참고 이미지와 같이 DialogFragment 클래스는 Dialog 객체의 메서드를 호출하는 대신 대화상자를 만들고 외형을 관리하는 데 필요한 모든 컨트롤을 제공한다. 관련 상세 설명은 해당 페이지에 들어가면 확인할 수 있다.

 

 

 

 

그 다음 onActivityCreated와 화면을 그려주는 onCreateView 메소드 2개를 override한다.

해당 dialog에서 사용할 "account_delete_dialog.xml" 생성 후 사용.

   <!-- account_delete_dialog.xml -->
   <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:orientation="horizontal"
        android:paddingLeft="16dp"
        android:paddingRight="16dp">

        <TextView
            android:id="@+id/delete_no"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginRight="16dp"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:gravity="center"
            android:padding="12dp"
            android:text="아니요"
            android:textColor="#141518"
            android:textSize="16dp"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/delete_yes"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@drawable/button_background"
            android:gravity="center"
            android:padding="12dp"
            android:text="탈퇴 하겠습니다"
            android:textColor="#141518"
            android:textSize="16dp"
            android:textStyle="bold" />
//class AccountDeleteDialog : DialogFragment()
override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.account_delete_dialog, container, false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        setupListener()
    }

 

interface를 사용해서 dialog에서 사용자 input이 들어왔을 때 원하는 위치로 이동시키도록 한다.

delete() 함수와 cancleDelete() 함수 2개를 만든다.

    interface AccountDeleteDialogInterface{
        fun delete()
        fun cancleDelete()
    }

    private var accountDeleteDialogInterface: AccountDeleteDialogInterface? = null
 fun addAccountDeleteDialogInterface(listener: AccountDeleteDialogInterface){
        accountDeleteDialogInterface = listener
    }

    private fun setupListener(){
        delete_no.setOnClickListener {
            accountDeleteDialogInterface?.cancleDelete() //?로 null 체크
            dismiss() //선택하면 dialog 닫기
        }
        delete_yes.setOnClickListener {
            accountDeleteDialogInterface?.delete() //?로 null 체크
            dismiss() //선택하면 dialog 닫기
        }
    }

 

다시 AccountingSettingActivity에서 위의 dialog를 활용하여 showDeleteDialog() 메소드 작성한 코드.

private fun showDeleteDialog() {
        AccountDeleteDialog().apply {
            addAccountDeleteDialogInterface(object :
                AccountDeleteDialog.AccountDeleteDialogInterface {
                override fun delete() {
                    deleteAccount()
                }

                override fun cancleDelete() {

                }
            })
        }.show(supportFragmentManager, "")

//        위와 같은 코드
//        val accountDeleteDialog = AccountDeleteDialog()
//        accountDeleteDialog.addAccountDeleteDialogInterface(object :
//            AccountDeleteDialog.AccountDeleteDialogInterface {
//            override fun delete() {
//                  deleteAccount()
//            }
//
//            override fun cancleDelete() {
//                
//            }
//        })
//        accountDeleteDialog.show(supportFragmentManager, "")

    }
   private fun deleteAccount() {
        AuthUI.getInstance()
            .delete(this)
            .addOnCompleteListener {
                moveToMainActivity()
                Toast.makeText(this, "탈퇴 하셨습니다", Toast.LENGTH_SHORT).show()

            }
    }

 


여기까지 완료하면 firebase를 이용한 로그인 기능, 로그아웃 및 계정탈퇴 기능까지 구현할 수 있다.