Introduction to RxSocialLogin :: Provides SocialLogin feature with RxJava

도입

약 9개월 전, 소셜 로그인 기능을 통합한 SocialLogin 라이브러리(https://github.com/WindSekirun/SocialLogin)를 만들어서 배포한 적이 있다. 이제까지는 무리 없이 사용했었지만, 최근 MVVM에 RxJava 를 도입하면서 콜백 방식이나 여러 가지가 마음에 안 드는 점이 있었다. 또한, 카카오 유저 v2 지원에 대한 요청(https://github.com/WindSekirun/SocialLogin/issues/13)이 들어와 처리하는 도중 코드를 관리하는 도중에도 자바로 작성한 것이라 조금 아쉬운 점이 있었다.

따라서 날 잡아서 SocialLogin 라이브러리를 다시 만드는 작업을 하기로 결정했는데, 적용될 작업은 다음과 같다.

  • 모든 메서드는 Kotlin 으로 재 작성
  • 가능하면 콜백 받는 방식을 RxJava 로 변경
  • Naver 로그인 요청 방식 변경
  • logout() 메서드 정리
  • README 문서 정리

이 5개 작업을 비롯해 재정비 작업을 완료했기에, 금일 시간 기준으로 0.5.0을 배포하였고, 이름을 RxSocialLogin(https://github.com/WindSekirun/RxSocialLogin) 으로 명명했다. 이 글에서는 간단한 소개 및 개발하면서 느꼈던 제약사항 (주로 RxJava) 에 대해 정리해보려고 한다.

소개

RxSocialLogin(https://github.com/WindSekirun/RxSocialLogin) 은 통합적인 소셜 로그인 기능을 제공하는 라이브러리로 페이스북, 카카오, 네이버, 라인, 트위터, 구글을 지원하는데, 기존 SocialLogin 과 차이는 없다. 또한 기존 라이브러리에도 있었던 장점인 모든 서비스에 대응하는 xxxLogin 클래스가 서로 같은 메서드를 가져 큰 차이점 없이 도입할 수 있다는 점도 변함이 없다.

하지만 내부적으로 RxJava, RxAndroid 를 도입하면서 기존에 사용하던 OnResponseListener 가 아닌 RxSocialLogin.kakao 로 결과값을 받을 수 있게 하여, 소셜 로그인 결과로 다른 작업을 Reactive 하게 진행할 수 있다.

RxSocialLogin 설계는 RxBinding(https://github.com/JakeWharton/RxBinding) 라이브러리의 설계를 많이 참고했는데, 기존 Listener 는 그대로 지원하는 동시에 Listener 를 포함하는 RxKakaoLogin 등의 클래스를 만들어 기존 구조를 크게 바꾸지 않으면서도 Callback 방식에서 Reactive 방식으로 업그레이드 하는 목표를 달성했다.

val kakaoDisposable = RxSocialLogin.kakao(kakaoLogin)
            .subscribe(Consumer<LoginResultItem> {
                Log.d(MainActivity::class.java.simpleName, "onNext: typeStr: ${it.toString()}")
            }, Consumer<Throwable> {
                Log.d(MainActivity::class.java.simpleName, "onError: ${it.message}")
            })

지금까지 각 소셜 서비스에서 제공하는 값을 받기 위해 사용했던 Map<UserInfoType, String> 또한 크게 변경되었는데, 기존에 있던 이 Map 객체에서 값을 꺼내서 사용해야 했던 단점을 회피하고자 새로운 결과 객체인 LoginResultItem 을 만들어 그대로 활용이 가능하게 만들었다. 따라서 Observable 가 LoginResultItem 만을 가지고 발행할 수 있게 했다.

그 외에도 구글 로그인 방법을 Google Sign-In API(https://developers.google.com/identity/sign-in/android/sign-in) 을 사용하는 것이 아닌 Firebase 인증(https://firebase.google.com/docs/auth/?hl=ko) 을 사용하여 Firebase 인증이 제공하는 다른 서비스 제공자들에 대해서도 구현할 수 있게 되었다.

제약 사항

현재 시점에서 RxJava 로 설계를 변경한 라이브러리는 이 라이브러리가 처음이었기에, 고민한 사항도  많았고, 아쉬운 부분도 많았다.

첫 번째로, 각 서비스를 호출하는 부분을 메인 스레드에서 호출할 수 있도록 강제한 점이다. 일반적으로 subscribe 하는 데에는 크게 문제는 없지만, 소셜 로그인의 결과값으로 서버 API 를 호출해야 할 경우에는 문제가 생긴다. 이는 원본 Observable, 즉 RxSocialLogin 가 요구하는 Thread 는 메인 스레드 이나, 서버 API 가 요구하는 Thread 는 메인 스레드가 아닌 백그라운드 스레드 ,정확히는 io 스레드이다. RxJava2 의 특성상 .subscribeOn 은 한 번만 적용되기 때문에 각 Observable 에 맞춘 스레드 변경이 불가능하다는 점이 있다.

만일 소셜 로그인 > 페이스북 친구 목록 가져오기 > 서버에 친구 목록 보내기 > ViewModel 갱신 이라는 비지니스 로직이 있을 경우에는 아래 방법으로 처리를 해야 한다.

// due to limitation, divide chain into two group
// see https://github.com/WindSekirun/RxSocialLogin#limitations
Disposable disposable = RxSocialLogin.facebook(facebookLogin)
        .subscribeOn(AndroidSchedulers.mainThread())
        .subscribe(resultItem -> FacebookUtils.newMyFriendsRequest(resultItem)
                .flatMap(data -> mSettingRepository.updateFriendsList(memberNumber, data))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(data -> loadData(), EMPTY_ERROR), EMPTY_ERROR);

addDisposable(disposable);

두 번째로, 소셜 로그인 결과를 가져오기를 실패했을 때 UndeliverableException 라는 오류가 가끔 나오는데, 이에 대한 해결책으로 Observer 에서 onNext (또는 onError) 하기 전 Observer.isDispose 를 체크하여 dispose 가 되지 않은 상태에서만 발행하게 했는데도 발생이 된다.

단, 이 문제는 onError 를 발행할 때 에만 문제가 되는 사항이고, 일반적인 경우에는 onError  가 반환된 일이 적어 아주 치명적인 문제는 아니지만 사용자 (= 개발자) 가 각 소셜 서비스의 프로젝트 설정을 잘못 했을 경우에는 자주 반환되는데, 그럴 때 마다 해당 문제가 발생하여 앱이 죽는 것이다.

당장은 RxJavaPlugin 의 setErrorHandler 메서드를 통하여 해당 예외가 발생하지 않게는 처리했지만, 이 클래스는 앱의 RxJava 로직 전반에 적용되므로 다소 위험하다는 단점이 있다.

그리고 사소하지만 개인 개발환경 (Macbook Pro 15′) 의 안드로이드 스튜디오가 build.gradle 를 변경할 때 마다 ‘Please select Android SDK’ 라는 문구가 뜨며 빌드가 안 되는 문제가 있었는데, 결국에는 임시 해결방법으로 최근 프로젝트 목록에서 삭제 후 재 임포트 라는 방식으로 해결했다. 해당 방법에 대한 자세한 사항은 https://stackoverflow.com/a/50000408 에서 볼 수 있다.

위 글에서 한 댓글에 아주 공감되었는데, 그 댓글은 다음과 같다.

To everyone that skipped over this…. I did not think this would fix my issue and I tried everything under this answer first. This is the only thing that worked. Don’t know why. Yeah it is super weird that this is the fix, but it is. Try this one

이 답변을 넘긴 모두에게, 나는 이 방법이 이슈를 해결할 것이라 생각하지 않아 아래에 있는 모든 답변을 시도했다. 이 방법 만이 작동했다. 왜 인지는 모르겠고 왜 작동하는지도 모르겠지만 어찌되었든 작동되니 이 방법을 사용해라

약 40분동안 삽질한 입장에서 매우 공감이 될 수 밖에 없었다 <(._.)>

마무리

개선할 사항은 분명하게도 남아있지만, 첫 RxJava-related 라이브러리가 생긴 것 만으로도 충분히 성공했다고 생각한다. Callback 을 쓰는 대신 Observable 나 Single 를 사용하는 것도 익숙해졌고, 앞으로도 계속 공부하면서 익혀 나가야 겠다고 생각했다.

마지막으로, 기존에 있던 SocialLogin 라이브러리는 7월 11일 기준으로 Deprecated 처리를 했고, 앞으로의 모든 업데이트는 RxSocialLogin 에서만 이루어질 계획이다.


특성 이미지는 https://www.tutorialrepublic.com/snippets/preview.php?topic=bootstrap&file=sign-in-from-with-social-login-button 에 있는 화면입니다.