마이페이지_구현하기 - boostcampwm-2024/and04-Nature-Album GitHub Wiki
✅ 진행한 기능
- 마이 페이지 UI 구현
- 로그인 시 유저 정보 가져와 이메일 / 프로필 이미지 표시
💡문제 해결 과정 기록
로그인하고 사용자가 로그인 여부를 확인할 수 있도록 간단하게 마이페이지 UI를 구현했다.
표시할 정보는 아래와 같다.
- 프로필 이미지
- 사용자 이메일
❓ 유저 정보는 어떻게 가져와야 할까?
처음에는 사용자가 로그인 하고난 후 다른 화면에서도 유저 정보에 접근하기 위하여 어떤 방식을 사용해야할까 고민이 들었다.
선택할 수 있는 선택지는 아래와 같았다.
- singletone object를 통해 앱 전역에서 사용한다.
- SharedPreferences를 이용한다.
- DataStore를 이용한다.
💡 여기서 Preferences DataStore를 사용하기로 결정했다.
DataStore는 데이터의 비동기 처리와 효율적인 데이터 관리가 가능하게 하는 코루틴과 플로우를 지원하고, 보안적인 측면에서 SharedPreferences 보다 강력한 옵션을 제공했다. 또한 공식문서에서도SharedPreferences를 사용하고 있다면 DataStore로 이전하는 것이 좋다고 권장했기에 선택했다.
❓ SharedPreferences 는 어떤 문제점이 있었을까?
우선 비동기 API를 제한적으로만 지원한다. 즉,기본적으로 동기적으로 작업한다는 뜻이다.
그렇기에 비동기 작업을 제대로 해주지 않는다면 ANR을 발생시킬 수 있고, 오류가 생겨도 확인이 불가능했다.
또한 런타임에 예외가 생기면 런타임 에러가 발생해 잘못 사용하면 앱이 강제 종료될 수 있다.
정리하자면,
- UI 스레드에서 호출할 수 있도록 API가 설계되었지만, UI 스레드를 블로킹 해 ANR을 발생시킬 수 있다.
- 여러 스레드에서 동시에 접근할 경우 데이터 일관성이 깨질 수 있다.
- Type-Safety를 제공하지 않는다.
💡 위와 같은 문제가 있기에 공식문서에서 조차 DataStore 사용을 권장하는 것 같다.
위에서 정한 것 처럼 DataStore를 통해 데이터를 가져오려고 했다.
그 전에, Firebase Storage 작업을 하고 있는 도윤님께 필요한 데이터를 확인하고 설계하려 했다.
하지만 도윤님께서는 로그인 시 별도로 데이터를 저장할 필요 없이 Firebase.auth
를 호출하여 현재 로그인된 사용자 정보를 가져오면 된다고 알려주셨다.
다른 화면에서 로그인 하고난 후 그 로그인 정보를 또 다른 화면에서 접근할 수 있는 것인지 테스트를 진행했고, 아주 잘 가져와졌다.
심지어 어떤 로직으로 동작하는지는 모르겠지만, 기기에서 한 번 로그인하면, 앱을 껐다 켜도 로그인 정보가 계속 남아 있는 것도 확인했다. (자동 로그인)
그 후 여러 화면에서 Firebase.auth
를 호출하여 사용자 정보를 가져올 것을 생각하여 싱글톤 객체로 UserManager를 생성하였다.
object UserManager {
private val auth = Firebase.auth
fun isSignIn() = auth.currentUser != null
fun getUser(): FirebaseUser? {
return auth.currentUser
}
}
뷰 모델에서는 로그인 성공 시 uiState를 Success
로 만들었고, MyPageScreen에서 UiState에 따라 다른 화면을 그리도록 만들었다.
//MyPageViewModel
fun signInWithGoogle(context: Context) {
authenticationManager.signInWithGoogle(context).onEach { response ->
when (response) {
is AuthResponse.Success -> {
_uiState.emit(UiState.Success)
}
}
}.launchIn(viewModelScope)
}
// MyPageScreen
when (uiState.value) {
is UiState.Success -> {
val user = UserManager.getUser()
val email = user?.email
val photoUri = user?.photoUrl
UserProfileContent(uri = photoUri, email = email)
}
else -> {
UserProfileContent()
}
}