본문 바로가기

TIL

[TIL] Kotlin Firebase Authentication [ 2 ] - 회원가입 시 추가로 회원정보 DB에 넣어주기.

Topic =  Realtime DB에 회원가입한 회원 uid와 회원정보 저장해주기

Auth
Realtime DB

 


 

회원가입 시 UID와 함께 회원정보를 저장하기

현재 프로젝트에서 사용할 기능 중 UID 를 통한 접근방식을 사용해야 하는 경우가 많다.

   회원가입 시 해당 유저 UID와 함께 회원정보를 DB에 저장함으로 DB의 UID 값을 통해서 회원정보 적용 및 수정을 할 수 있게 된다. 이 외에도 유저 DB 정보를 확인해서 게시글 정보, 좋아요 기능 구현이 가능하다. 현재 진행중인 프로젝트 뿐만 아니라 유저 로그인이 가능한 웹에서는 인증 정보 뿐만 아니라 유저별 UID에 따른 DB 구현은 반드시 필요한 기능인 것 같다.

   

 

Firebase Console

   Email, Password 를 통해 회원가입 할 수 있도록 Firebase Authentication 의 SIgn-In method 의 이메일 / 비밀번호 로그인 메소드를 사용 설정 체크해주도록 하자.

 

 

 

✔️ Realtime DB 규칙 수정하기

   Realtime DB의 프로젝트 적용 방법은 Firebase Authentication 과 동일하게 Firebase Console 좌측 메뉴에서 Realtime Database Service로 들어간 뒤 사용할 수 있도록 추가해주면 된다.

   프로젝트에 추가하였다면, Realtime DB 탭 클릭 ▶ 규칙에 들어와 rules 내부에 write를 true로 설정해주어서 앱 내부에서도 DB에 값을 추가할 수 있도록 해주자. 변경하고 나면 화면 상단에 게시 또는 취소할 수 있는 탭이 나오는데 ' 게시 '를 클릭해서 변경사항을 저장해주어야 한다.

 

적용 방법

Manifest, gradle 파일

 

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.INTERNET"/>
    <application
    
    ... >
   
    ...
    
    </application>
        <activity
        
        ...

        </activity>
</manifest>
  • Manifest에서 INTERNET Permossion을 부여해준다

 

build.gradle ( module : )

dependencies {

    implementation(platform("com.google.firebase:firebase-bom:32.3.1"))
    implementation("com.google.firebase:firebase-auth-ktx")
    implementation("com.google.firebase:firebase-database-ktx")
  • module 수준의 build.gradle에서 BoM 플랫폼을 이용하여 Auth와 Realtime db 의 버전을 관리할 수 있도록 해준다.

 

SignUpFragment.kt

 

[ 1 ] ViewBinding 적용 및 Firebase 객체 선언 및 초기화 

class SignUpFragment : Fragment() {
    private lateinit var auth : FirebaseAuth
    private lateinit var db: FirebaseDatabase
    
    private var _binding: FragmentSignupBinding? = null
    private val binding get() = _binding!!


    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentSignupBinding.inflate(inflater, container, false)

        db = Firebase.database
        auth = Firebase.auth

        return binding.root
    }
    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }
}
  • SignInActivity에서 회원가입 버튼 클릭 시 SignUp Fragment로 이동하여 SignUpFragment에서 회원가입을 진행하는 코드로 작성할 것이다.
  • ViewBinding 적용 및 Firebase Auth, Database 객체를 지연초기화로 선언 및 초기화 해준다.

 

[ 2 ] 회원가입 및 회원 정보 Realtime DB 저장 함수 정의

email과 password를 매개변수로 받는 createAccount 함수를 정의해주고 안에 다음과 같은 로직을 작성해준다.

 

[ 2 - 1 ] 사용자 생성 및 생성 된  사용자의 uid 와 정보를 Realtiem DB에 저장할 수 있도록 HashMap() 에 담아주기.

 

private fun createAccount(email: String, password: String) {
        if (email.isNotEmpty() && password.isNotEmpty()) {
            auth.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(requireActivity()) { task ->
                    if (task.isSuccessful){
                        val signUpEmail = binding.editEmail.text.toString()
                        val signUpPassword = binding.editPassword.text.toString()
                        val signUpNickname = binding.editNickname.text.toString()

                        val firebaseUser: FirebaseUser? = task.result?.user
                        if (firebaseUser != null){
                            Log.d("xxxx", " firebaseUser != null /  ${ task.result?.user?.uid}")
                            val userId = firebaseUser?.uid
                            val reference = db.reference.child("Users").child(userId!!)
                            val hashMap: HashMap<String, Any> = HashMap()
                            hashMap["id"] = userId
                            hashMap["nickname"] = signUpNickname
                            hashMap["email"] = signUpEmail
                            hashMap["pw"] = signUpPassword
                            hashMap["profile"] =
                                "https://source.android.com/static/docs/setup/images/Android_symbol_green_RGB.png?hl=ko"

auth.createUserWithEmailAndPassword(email, password)

  • createAccount 함수의 파라미터로 받은 email, password을 통해 Firebase auth 서비스의 새로운 계정을 생성하는 함수로, Firebase Authentication에 해당 계정에 대한 인증 정보를 저장한다.

 

addOnCompleteListener(requireActivity()) { task -> if (task.isSuccessful) { } 

  • 사용자 생성이 successful로 처리가 되었을 경우 { } 안에 정의한 로직이 실행되도록 한다.
  • addOnFailureListener, addOnCanceledListener, addOnCompleteListener 와 같은 Firebase에서 제공하는 SDK의  Listener를 사용해 Firebase 함수가 성공적으로 처리 되었을 경우, 실패했을 경우, 취소 했을 경우, 완료 되었을 경우의 로직을 각각 정의할 수 있다.

 

 

 val firebaseUser: FirebaseUser? = task.result?.user
                        if (firebaseUser != null){
                            Log.d("xxxx", " firebaseUser != null /  ${ task.result?.user?.uid}")
                            val userId = firebaseUser?.uid
                            val reference = db.reference.child("Users").child(userId!!)
                            val hashMap: HashMap<String, Any> = HashMap()
                            hashMap["id"] = userId
                            hashMap["nickname"] = signUpNickname
                            hashMap["email"] = signUpEmail
                            hashMap["pw"] = signUpPassword
                            hashMap["profile"] =
                                "https://source.android.com/static/docs/setup/images/Android_symbol_green_RGB.png?hl=ko"
  • auth.createUserWithEmailAndPassword(email, password) 메소드의 실행 결과인 task.result의 user 값을 받아 해당하는 user의 uid 값을 회원가입 시 함께 작성했던 유저 정보와 함께 DB에 저장할 수 있도록 HashMap()에 추가해준다.
  • Realtime DB에서 UID 별로 유저를 나누기 위해 userId로 child 노드를 나누어서 저장될 수 있도록 한다.

 

[ 2 - 2 ] 위에 작성하던 코드에 이어서 Realtime DB에 유저 정보 data를 추가해주고 mainActivity로 이동.

  • hashMap ~ 부분 중복되는 이유는 위에 코드에 이어서 작성하면 된다는 의미로 중복해서 적지 않는다.
  • reference.setValue()를 통해 UID 에 해당 하는 데이터 값을 지정해준다. UID는 겹칠 수 없는 독립적인 값이므로 push()를 넣어줄 필요가 없다 ▶ 값을 여러개 넣어야 하는 DB는 setValue 앞에 .push() 메서드를 통해 별도의 Key를 생성 하여 1이 현재 데이터일 때 2의 값을 push().setValue 하면 해당 child 노드는 각각 다른 Key의 1, 2 값을 갖게 된다.
                            hashMap["id"] = userId
                            hashMap["nickname"] = signUpNickname
                            hashMap["email"] = signUpEmail
                            hashMap["pw"] = signUpPassword
                            hashMap["profile"] =
                                "https://source.android.com/static/docs/setup/images/Android_symbol_green_RGB.png?hl=ko"

                            reference.setValue(hashMap).addOnCompleteListener {
                                if (it.isSuccessful) {
                                    val intent = Intent(activity, MainActivity::class.java)
                                    startActivity(intent)
                                    requireActivity().finish()
                                    Log.d("xxxx", " referce addOnComplete Successful ")
                                } else {
                                    Toast.makeText(requireContext(), "계정 생성 실패", Toast.LENGTH_SHORT).show()
                                    Log.d("xxxx", "referce addOnComplete Fail")
                                }
                            }
                        }
                    }
                }
            Log.d("xxxx", "createAccount Successful")
        }
    }
  • Firebase DB에도 정상적으로 데이터가 들어가면, MainActivity를 시작하고 현재 SignIn Acitivty를 종료한다.

 

 

전체 코드

private fun createAccount(email: String, password: String) {
        if (email.isNotEmpty() && password.isNotEmpty()) {
            auth.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(requireActivity()) { task ->
                    if (task.isSuccessful){
                        val signUpEmail = binding.editEmail.text.toString()
                        val signUpPassword = binding.editPassword.text.toString()
                        val signUpNickname = binding.editNickname.text.toString()

                        val firebaseUser: FirebaseUser? = task.result?.user
                        if (firebaseUser != null){
                            Log.d("xxxx", " firebaseUser != null /  ${ task.result?.user?.uid}")
                            val userId = firebaseUser?.uid
                            val reference = db.reference.child("Users").child(userId!!)
                            val hashMap: HashMap<String, Any> = HashMap()
                            hashMap["id"] = userId
                            hashMap["nickname"] = signUpNickname
                            hashMap["email"] = signUpEmail
                            hashMap["pw"] = signUpPassword
                            hashMap["profile"] =
                                "https://source.android.com/static/docs/setup/images/Android_symbol_green_RGB.png?hl=ko"

                            reference.setValue(hashMap).addOnCompleteListener {
                                if (it.isSuccessful) {
                                    val intent = Intent(activity, MainActivity::class.java)
                                    startActivity(intent)
                                    requireActivity().finish()
                                    Log.d("xxxx", " referce addOnComplete Successful ")
                                } else {
                                    Log.d("xxxx", "referce addOnComplete Failure")
                                }
                            }
                        }
                    }
                }
            Log.d("xxxx", "createAccount Successful")
        }
    }

 

 


[ A. 오늘 복습한 내용 / B. 다음에 학습할 내용 ]

A. Realtime DB 및 Firebase Authentication 복습

 

B. Listener등 Firebase SDK를 활용하면 조금 더 깔끔하게 저장할 수 있을 것 같다 / Firebase SDK 추가 학습하기.

 

B. 생명주기, Intent 추가 학습하기. 

 

 


 

[오류,에러 등등]

회원가입이 완료되면 Realtime DB에 uid 와 회원 정보들이 저장 되어야 하는데 저장되지 않는 오류

 

오류가 되게 많았어서 순서대로 나열해야겠다.

 

1. Realtime DB 의 Rule의 write 값이 false로 되어 있었다. Firebase Console ▶ Realtime DB  ▶ Rule에서 write를 true로 변경하여 해결.

 

2. Realtime DB에 데이터를 저장함과 Firebase auth를 함께 사용하는데, auth에 회원가입은 정상적으로 되나, DB는 저장이 되지 않아서 DB 저장을 auth의 CompleteListener 외부에서 시도 해보았다. 

 

   private fun으로 따로 DB저장 함수로 회원가입 함수와 분리해서 회원가입 함수 이후에 DB 저장을 시도 해보았는데 정상적으로 DB가 저장되는 것 처럼 보였지만 이번에 생성한 user가 10번 user라면 9번 user의 uid로 DB가 저장이 되는 것이다 기존에 로그인 해둔 User의 데이터가 남아있고, 그렇다고 해서 Sign Out을 하면 uid가 null 값이 들어가게 되는 것.

 

   해당 오류가 생겨서 코드 순서도 바꿔서 계속 시도 해보고, FirebaseDatabase의 객체 선언 방식도 바꾸어 보고, 초기화 방법도 여러가지로 시도 해보았는데 계속 해결이 안되어서 Log 하나하나 파보면서 진행해보니 회원가입 Fragment 내부에서 DestoryView에 방금 회원가입 한 user를 currentUser로 만들 수 있는 방법은 도저히 찾을 수 없어 currentUser 대신  task.result를 통해 user에 접근하여 해결하였다.

 

 

 

오류 코드 

private fun createAccount(email: String, password: String) {
    if (email.isNotEmpty() && password.isNotEmpty()) {
        auth.createUserWithEmailAndPassword(email, password)
            .addOnCompleteListener(
                requireActivity()
            ) {
                val signUpEmail = binding.editEmail.text.toString()
                val signUpPassword = binding.editPassword.text.toString()
                val signUpNickname = binding.editNickname.text.toString()

                auth = Firebase.auth
                val firebaseUser: FirebaseUser? = auth.currentUser
                val userId = firebaseUser?.uid
                val reference = db.reference.child("Users").child(userId!!)
                val hashMap: HashMap<String, Any> = HashMap()
                hashMap["id"] = userId
                hashMap["nickname"] = signUpNickname
                hashMap["email"] = signUpEmail
                hashMap["pw"] = signUpPassword
                hashMap["profile"] =
                    "https://source.android.com/static/docs/setup/images/Android_symbol_green_RGB.png?hl=ko"

                reference.setValue(hashMap).addOnCompleteListener {
                    if (it.isSuccessful) {
                        Log.d("xxxx", " referce addOnComplete Successful ")
                    } else {
                        Toast.makeText(requireContext(), "계정 생성 실패", Toast.LENGTH_SHORT).show()
                        Log.d("xxxx", "referce addOnComplete Fail")
                    }
                }

            }

        Log.d("xxxx", "createAccount Successful")
    }


}

 

 

해결 코드

    private fun createAccount(email: String, password: String) {
        if (email.isNotEmpty() && password.isNotEmpty()) {
            auth.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(requireActivity()) { task ->
                    if (task.isSuccessful){
                        Log.d("xxxx", " tast.isSuccessful ")
                        val signUpEmail = binding.editEmail.text.toString()
                        val signUpPassword = binding.editPassword.text.toString()
                        val signUpNickname = binding.editNickname.text.toString()

                        val firebaseUser: FirebaseUser? = task.result?.user
                        if (firebaseUser != null){
                            Log.d("xxxx", " firebaseUser != null /  ${ task.result?.user?.uid}")
                            val userId = firebaseUser?.uid
                            val reference = db.reference.child("Users").child(userId!!)
                            val hashMap: HashMap<String, Any> = HashMap()
                            hashMap["id"] = userId
                            hashMap["nickname"] = signUpNickname
                            hashMap["email"] = signUpEmail
                            hashMap["pw"] = signUpPassword
                            hashMap["profile"] =
                                "https://source.android.com/static/docs/setup/images/Android_symbol_green_RGB.png?hl=ko"

                            reference.setValue(hashMap).addOnCompleteListener {
                                if (it.isSuccessful) {
                                    Log.d("xxxx", " referce addOnComplete Successful ")
                                    val intent = Intent(activity, MainActivity::class.java)
                                    startActivity(intent)
                                    requireActivity().finish()
                                    Log.d("xxxx", " referce addOnComplete Successful ")
                                } else {
                                    Toast.makeText(requireContext(), "계정 생성 실패", Toast.LENGTH_SHORT).show()
                                    Log.d("xxxx", "referce addOnComplete Fail")
                                }
                            }
                        }
                    }
                }
            Log.d("xxxx", "createAccount Successful")
        }
    }

 

저장한 Data 가져오는 방법 별도로 작성할 예정

➕링크 추가

 

[TIL] Kotlin Firebase - Realtime Database [ 2 ] 저장된 데이터Listener로 가져오기

Topic = Firebase Realtime DB에 저장된 데이터를 여러가지 방식으로 가져오는 방법 학습 Why - DB를 가져오기 위해 사용하는 Listener에 대해서 학습해보고 차이점을 확인하기 Realtime DB의 데이터를 가져올

junes-daily.tistory.com


 

[느낀 점]

1. 디버깅이 매우 중요한 것 같다.

 

2. Firebase 자료는 참 많은데 다 비슷비슷한 느낌이다

 

3. 키워드를 잘 정해서 검색을 잘 하는것이 중요한 것 같다.

 

 


[Reference]

 

// Firebase 

https://tazozzangzzang.tistory.com/8

https://firebase.google.com/docs/database/android/start?hl=ko#kotlin+ktx_1

https://firebase.google.com/docs/auth/android/start?hl=ko