Android에서 Firebase 로그인 - 3(ViewModel)

2023. 5. 3. 23:20Mobile/Android

작성자알 수 없는 사용자

728x90
반응형

안녕하세요. 기깔나는 사람들에서 안드로이드를 맡고 있는 마플입니다.

이번 시간에는 저번에 이어서 로그인 부분을 구현하는데, ViewModel 부분을 알아 보죠.


1.ViewModel

1)LoginViewModel

로그인을 위한 ViewModel 입니다. FireAuth를 위한 Repository, 로그인 입력창에 입력되는 형식이 맞는지 확인하는 loginFormState, 로그인 결과를 저장하는 loginResult, 로그인이 성공했는지를 저장하는 isLoginSuccessful의 맴버들이 있습니다. 생성자에서 각 요소들을 초기화를 해주고 있어요.

public class LoginViewModel extends ViewModel {

    private final FirebaseAuthRepository mAuthRepository;
    private final MutableLiveData<LoginFormState> loginFormState;
    private final MutableLiveData<LoginResult> loginResult;
    private final MutableLiveData<Boolean> isLoginSuccessful;

    /**
     * no Args 생성자
     * Repository 가져 오기
     * LiveData 초기화
     */
    LoginViewModel() {
        this.mAuthRepository = FirebaseEmulatorAuth.getInstance();
        this.loginResult = new MutableLiveData<>();
        this.loginFormState = new MutableLiveData<>();
        this.isLoginSuccessful = new MutableLiveData<>();
    }
	...
}

1. 로그인

email과 password를 받아서 AuthRepository에서 로그인을 진행을 해요.
로그인에 성공하면 로그인 된 유저를 받아서 앱 내부에 저장을 합니다. 그리고 UI쪽에 전달할 내용으로 로그인이 성공했음을 전달하죠.  
실패를 하면 실패했음을 저장하고 실패한 이유 또한 저장합니다.

    /**
     * 로그인
     * username(email), password를 받아 로그인
     * 성공 하면 유저 정보를 저장하고, 로그인 성공 했음을 저장
     * 실패 하면 로그인 실패 했음을 저장
     * @param username - email
     * @param password - password (non encryption)
     */
    public void login(String username, String password) {
        mAuthRepository.login(username, password, new LoginListener() {
            @Override
            public void onSuccess(LoggedInUser user) {
                GlobalApplication.saveUser(user);
                isLoginSuccessful.postValue(true);
                loginResult.setValue(new LoginResult(new LoggedInUserView(user.getDisplayName())));
                Log.d("login", "success login");
            }

            @Override
            public void onError(Throwable t) {
                isLoginSuccessful.postValue(false);
                loginResult.setValue(new LoginResult(R.string.login_failed));
            }
        });
    }

2. 구글 계정을 통한 로그인

Auth에서는 구글 계정을 가지고 로그인이 가능하기에 구글 로그인이 통과한 계정 정보를 가지고 Auth쪽으로 전달하여 로그인을 진행합니다. 1. 로그인과 마찬가지로 성공 시와 실패 시를 처리합니다.

 /**
     * 구글 계정 서버 로그인
     * Google 로그인 한 정보를 가지고 서버 로그인
     * 성공 하면 유저 정보를 저장하고, 로그인 성공 했음을 저장
     * 실패 하면 로그인 실패 했음을 저장
     * @param account - 구글 로그인 성공 계정
     */
    public void googleLogin(GoogleSignInAccount account) {
        mAuthRepository.loginWithGoogle(account, new LoginWithGoogleListener() {
            @Override
            public void onSuccess(LoggedInUser user) {
                GlobalApplication.saveUser(user);
                isLoginSuccessful.postValue(true);
                loginResult.setValue(new LoginResult(new LoggedInUserView(user.getDisplayName())));
            }

            @Override
            public void onError() {
                isLoginSuccessful.postValue(false);
                loginResult.setValue(new LoginResult(R.string.login_failed));
            }
        });
    }

3. 네이버 로그인

네이버 로그인은 네이버에서 제공하는 고유의 객체를 통해서 하는데 Activity가 필요하기 때문에 인자로 Activity를 받아 로그인을 진행해요. 콜백 리스너를 통해 성공 시와 실패 시를 1. 로그인 처리와 유사하게 합니다. token은 받아오지만 내부에서 따로 처리는 하지 않았습니다.

    /**
     * 네이버 로그인
     * 성공 하면 유저 정보를 저장하고, 로그인 성공 했음을 저장
     * 실패 하면 로그인 실패 했음을 저장
     */
    public void naverLogin(Activity activity) {
        try {
            NaverRepository.getInstance().login(activity, new NaverRepository.CheckTokenListener() {
                @Override
                public void onSuccess(String token) {
                    isLoginSuccessful.postValue(true);
                    loginResult.setValue(new LoginResult(new LoggedInUserView(token)));
                }

                @Override
                public void onFailure() {
                    isLoginSuccessful.postValue(false);
                    loginResult.setValue(new LoginResult(R.string.login_failed));
                }

                @Override
                public void onError() {
                    isLoginSuccessful.postValue(false);
                    loginResult.setValue(new LoginResult(R.string.login_failed));

                }
            });
        } catch (Throwable e) {
            e.printStackTrace();
            Log.d("tracking",e.toString());
            Log.d("naver login", "null context");
        }
    }

4. 카카오 로그인

카카오 또한 네이버처럼 고유의 객체를 이용해서 로그인을 진행하게 됩니다. 네이버처럼 Activity가 필요해서 인자로 받아옵니다. Repository로 작성이 안 되어 있어, 여기서 카카오 로그인 페이지로 이동하게 합니다. 그 결과를 가지고 로그인 성공 시 token을 받아옵니다. 1. 로그인 처럼 성공 시와 실패 시는 유사하게 처리해요.

/**
     * 카카오 로그인
     * 성공 하면 로그인 성공 했음을 저장
     * 실패 하면 로그인 실패 했음을 저장
     * @param activity - activity
     */
    public void kakaoLogin(Activity activity) {

        if (UserApiClient.getInstance().isKakaoTalkLoginAvailable(activity)) {
            // 카카오톡 어플로 로그인
            UserApiClient.getInstance().loginWithKakaoTalk(activity, (token, error) -> {
                if (error != null) {
                    Log.e("kakao", error.toString());

                } else if (token != null) {
                    isLoginSuccessful.postValue(true);
                    loginResult.setValue(new LoginResult(new LoggedInUserView(token.getAccessToken())));
                    Log.d("kakao", "success token: " + token.getAccessToken());
                }
                return null;
            });
        } else {
            UserApiClient.getInstance().loginWithKakaoAccount(activity, (token, error) -> {
                if (error != null) {
                    Log.e("kakao", error.toString());

                } else if (token != null) {
                    isLoginSuccessful.postValue(true);
                    loginResult.setValue(new LoginResult(new LoggedInUserView(token.getAccessToken())));
                    Log.d("kakao", "success token: " + token.getAccessToken());
                }
                return null;
            });
        }
    }

5. 회원가입

이메일과 비밀번호를 받아서 Auth에게 전달하여 회원가입을 진행합니다. 콜백 리스너를 통해 성공 시에는 바로 로그인 할 수 있도록 1. 로그인으로 이어서 진행하게 했어요.

    /**
     * 회원가입
     * 이메일과 패스워드를 받아 회원가입 후 로그인을 진행
     *
     * @param email    - 이메일
     * @param password - 비밀번호
     */
    public void registry(String email, String password) {
        mAuthRepository.registryAuthorization(email, password, new RegistryListener() {
            @Override
            public void onSuccess() {
                login(email, password);
            }

            @Override
            public void onError() {

            }
        });
    }

6. 로그인 입력 데이터 검증

로그인 입력 데이터 포맷이 맞는지 확인을 합니다. 이메일과 비밀번호를 각각 하고 그 결과를 LoginFormState에 저장합니다. 모든 과정을 통과야지 True 값을 저장하게 됩니다. LoginFormState는 UI측에서 처리하게 됩니다.

    /**
     * 로그인 데이터 변경 감지 검증
     * 로그인 화면의 username, paswrod 입력 부분이 변경 될 시 내용을 검증한다.
     * @param username - 이메일
     * @param password - 비밀 번호
     */
    public void loginDataChanged(String username, String password) {
        // 각 조건에 안 맞는지 확인하고 다 맞으면 valid(loginformstate - true)
        if (!isUserNameValid(username)) {
            loginFormState.setValue(new LoginFormState(R.string.invalid_username, null));
        } else if (!isPasswordValid(password)) {
            loginFormState.setValue(new LoginFormState(null, R.string.invalid_password));
        } else {
            loginFormState.setValue(new LoginFormState(true));
        }
    }

7. 이메일 검증 메소드

이메일이 올바른 형식인지 확인합니다. null인지, "@"을 포함한한 형식인지, 만약 공백문자로만 이루어졌는지도 확인합니다.

    /**
     * 이메일 검증 메소드
     * null 체크
     * "@" 포함한 형식인지
     * 비어었는지
     *
     * @param username - 이메일
     * @return 하나라도 틀리면 false, 아니면 true
     */
    private boolean isUserNameValid(String username) {
        if (username == null) {
            return false;
        }
        if (username.contains("@")) {
            return Patterns.EMAIL_ADDRESS.matcher(username).matches();
        } else {
            return !username.trim().isEmpty();
        }
    }

8. 비밀번호 검증

비밀번호 형식을 검증합니다. null 체크와, 6글자 이상으로 됬는지만 확인합니다. 

    /**
     * 비밀번호 검증 메소드
     * null 체크
     * 6글자 이상인지
     *
     * @param password - 비밀번호
     * @return 하나라도 틀리면 false, 아니면 true
     */
    private boolean isPasswordValid(String password) {
        return password != null && password.trim().length() > 5;
    }

    LiveData<LoginFormState> getLoginFormState() {
        return loginFormState;
    }

    LiveData<LoginResult> getLoginResult() {
        return loginResult;
    }

2)LoginFormState

로그인 입력 형식이 맞는지에 대한 정보를 저장하는 클래스입니다. Integer로 저장된 것은 (int)R.String.?의 형식을 가져오기 때문에 지정이 Integer로 저장이 됩니다.

usernameError는 이메일이 형식에 맞지 않을 경우 띄울 String의 id 값을 저장합니다.

passwordError는 비밀번호가 형식에 맞지 않을 경우 띄울 String의 id 값을 저장합니다.

isDataVaild는 형식이 맞는지 최종적으로 판단하는 맴버입니다. 하나라도 틀리면 false로 저장이 되어서 모두 만족해야만 True의 값을 가집니다.

/**
 * 로그인 입력 폼에 대한 검증 상태 데이터
 */
class LoginFormState {
    @Nullable
    private final Integer usernameError;
    @Nullable
    private final Integer passwordError;
    private final boolean isDataValid;

    LoginFormState(@Nullable Integer usernameError, @Nullable Integer passwordError) {
        this.usernameError = usernameError;
        this.passwordError = passwordError;
        this.isDataValid = false;
    }

    LoginFormState(boolean isDataValid) {
        this.usernameError = null;
        this.passwordError = null;
        this.isDataValid = isDataValid;
    }

    @Nullable
    Integer getUsernameError() {
        return usernameError;
    }

    @Nullable
    Integer getPasswordError() {
        return passwordError;
    }

    boolean isDataValid() {
        return isDataValid;
    }
}

3)LoginResult

로그인 결과를 저장합니다. view에 띄워줄 LoggedInUserView도 저장합니다.

success는 성공 시 View에서 쓸 객체를 저장합니다.

error는 로그인 실패시 띄워 줄 String의 id값을 저장합니다.

/**
 * 인증 결과 : success (유저 내용) 이나 에러 메시지
 */
class LoginResult {
    @Nullable
    private LoggedInUserView success;
    @Nullable
    private Integer error;

    LoginResult(@Nullable Integer error) {
        this.error = error;
    }

    LoginResult(@Nullable LoggedInUserView success) {
        this.success = success;
    }

    @Nullable
    LoggedInUserView getSuccess() {
        return success;
    }

    @Nullable
    Integer getError() {
        return error;
    }
}

4)LoggedInUser

로그인 된 유저 클래스

이메일, 표시되는 이름, 토큰, 역할이 저장됩니다.

/**
 * 로그인 된 유저
 * - 이메일, 표시명, 토큰, 역할
 */
public class LoggedInUser {

    @NonNull
    private String email;
    @Nullable
    private String displayName;
    @Nullable
    private String token;
    @NonNull
    private String role;

    public LoggedInUser(@NonNull String email, String displayName, @Nullable String token,@NonNull String role) {
        this.email = email;
        this.displayName = displayName;
        this.token = token;
        this.role = role;
    }

    public String getDisplayName() {
        return displayName;
    }

    public String getToken() {
        return token;
    }

    public String getRole() {
        return role;
    }

    public String getEmail() {
        return email;
    }

    public void setRole(@Nullable String role) {
        this.role = role;
    }
}

5)LoggedInUserView

UI를 위해 쓸 사용자 정보를 담을 클래스. 

/**
 * UI를 위한 인증 된 유저의 정보 클래스
 */
class LoggedInUserView {
    private final String displayName;
    //... other data fields that may be accessible to the UI

    LoggedInUserView(String displayName) {
        this.displayName = displayName;
    }

    String getDisplayName() {
        return displayName;
    }
}

이상으로 ViewModel과 그와 연관된 클래스들에 대해 알아 봤습니다. 다음 글에서는 Activity에 대해 알아보도록 하죠.

728x90
반응형