Android에서 Firebase 로그인 - 4(Activity)

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

작성자알 수 없는 사용자

728x90
반응형

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

이번에는 로그인을 위한 Activity를 다뤄 볼겁니다.


Activity

1)LoginActivity

1. 필드

RC_SIGN_IN는 google 로그인을 실행하고 받기위해서 google login에 부여할 코드입니다.

구글 로그인을 위해서 mGoogleSignInClient는 구글 로그인을 위한 고유의 객체를 가리키는 맴버가 있습니다.

ViewModel와 Layout을 bind한 맴버가 선언 되었습니다.

public class LoginActivity extends AppCompatActivity {

    private static final int RC_SIGN_IN = 9001;
    private GoogleSignInClient mGoogleSignInClient;

    private LoginViewModel mLoginViewModel;
    private ActivityLoginBinding mMainBinding;

    ...
}

2. onCreate()

activity_Login.xml과 Activity를 연결하고, 사용자 이벤트에 반응을 위한 리스너 추가 및 viewModel과 소통하는 부분이 있습니다.

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // view binding
        mMainBinding = ActivityLoginBinding.inflate(getLayoutInflater());
        setContentView(mMainBinding.getRoot());
        // google 로그인을 위한 intent 생성
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.default_web_client_id))
                .requestEmail()
                .build();
        mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
        // viewModel 생성
        mLoginViewModel = new ViewModelProvider(this).get(LoginViewModel.class);
        // view 각 요소 binding
        final EditText usernameEditText = mMainBinding.username;
        final EditText passwordEditText = mMainBinding.password;
        final Button loginButton = mMainBinding.login;
        final ProgressBar loadingProgressBar = mMainBinding.loading;
        final com.navercorp.nid.oauth.view.NidOAuthLoginButton naverButton = mMainBinding.naverAuthLoginButton;
        final Button registryButton = mMainBinding.registry;
        final ImageButton kakaoLoginButton = mMainBinding.kakaoLoginButton;
        final com.google.android.gms.common.SignInButton googleButton = mMainBinding.googleSignInButton;

        // login valid 데이터 감지하는 Obsever
        // login form이 변경 되고 그거에 따른 valid 값이 변동 시
        mLoginViewModel.getLoginFormState().observe(this, loginFormState -> {
            if (loginFormState == null) {
                return;
            }
            // 로그인 버튼 활성화
            loginButton.setEnabled(loginFormState.isDataValid());
            // 회원가입 버튼 활성화
            assert registryButton != null;
            registryButton.setEnabled(loginFormState.isDataValid());
            // username(email) 부분이 규칙에 맞지 않을 경우 띄워 주기
            if (loginFormState.getUsernameError() != null) {
                usernameEditText.setError(getString(loginFormState.getUsernameError()));
            }
            // password 부분이 규칙에 맞지 않을 경우 띄워 주기
            if (loginFormState.getPasswordError() != null) {
                passwordEditText.setError(getString(loginFormState.getPasswordError()));
            }
        });

        // loginResult를 감시할 처리 내용을 담긴 옵저버 추가
        mLoginViewModel.getLoginResult().observe(this, loginResult -> {
            if (loginResult == null) {
                return;
            }
            // 로딩 진행 바 제거
            progressbarDone();
            // 로그인 에러 시
            if (loginResult.getError() != null) {
                showLoginFailed(loginResult.getError());
                return;
            }
            // 로그인 성공 시
            if (loginResult.getSuccess() != null) {
                updateUiWithUser(loginResult.getSuccess());
            }
            setResult(Activity.RESULT_OK);
            Intent intent = new Intent(LoginActivity.this, MainActivity.class);
            startActivity(intent);
            finish();
        });

        // 로그인 Edit Text 감지를 위한 TextWatcher
        TextWatcher afterTextChangedListener = new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // ignore
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // ignore
            }

            @Override
            public void afterTextChanged(Editable s) {
                // 입력 값이 형식에 맞는지 검사 처리
                mLoginViewModel.loginDataChanged(usernameEditText.getText().toString(),
                        passwordEditText.getText().toString());
            }
        };

        // editText 내의 텍스트 변경 감지
        usernameEditText.addTextChangedListener(afterTextChangedListener);
        passwordEditText.addTextChangedListener(afterTextChangedListener);

        // 패스워드 입력에서 끝났다는 행위 시 처리 -ex) 완료 버튼, enter
        passwordEditText.setOnEditorActionListener((v, actionId, event) -> {
            if (actionId == EditorInfo.IME_ACTION_DONE) {
                mLoginViewModel.login(usernameEditText.getText().toString(),
                        passwordEditText.getText().toString());
            }
            return false;
        });

        // 로그인 버튼 클릭 시 처리 내용
        loginButton.setOnClickListener(v -> {
            // 진행 바 보이게 하기
            loadingProgressBar.setVisibility(View.VISIBLE);
            // 로그인 process 진행
            mLoginViewModel.login(usernameEditText.getText().toString(),
                    passwordEditText.getText().toString());
        });

        // 회원 가입 버튼 클릭 시 처리 내용
        assert registryButton != null;
        registryButton.setOnClickListener(view -> {
            // 진행 바 보이게 하기
            loadingProgressBar.setVisibility(View.VISIBLE);
            // 회원 가입 process 진행
            mLoginViewModel.registry(usernameEditText.getText().toString(),
                    passwordEditText.getText().toString());
        });

        // naver 로그인 버튼 클릭 시 처리 내용 추가
        assert naverButton != null: "Naver Button null";
        naverButton.setOnClickListener(view -> mLoginViewModel.naverLogin(this));

        // kakao 로그인 버튼 클릭 시 처리 내용 추가
        assert kakaoLoginButton != null : "kakao button null";
        kakaoLoginButton.setOnClickListener(view -> mLoginViewModel.kakaoLogin(LoginActivity.this));

        // google 로그인 버튼 클릭 시 처리 내용 추가
        assert googleButton != null : "google button null";
        googleButton.setOnClickListener(view -> googleSignIn());
    }

3. 구글 로그인 실행

구글은 구글 로그인을 진행하는 Activity를 Intent를 통해 제공합니다. GoogleSignInClient에서 해당 Intent를 가져와서 구글 로그인 Activity에 RC_SIGN_IN를 붙혀서 실행합니다.

    /**
     * Google 로그인을 위해 해당 Activity를 실행
     */
    private void googleSignIn() {
        // 구글 로그인 클라이언트 정보를 담은 intent 생성
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        // 구글 로그인 액티비티를 실행
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }

4. 구글 로그인 결과 처리

Activity 끼리의 통신은 onActivityResult를 통해 할 수 있습니다. 다른 Activity에서 해당 Activity로 데이터를 보냅니다. 이 경우는 GoogleLogin이 requestCode로 RC_SIGN_IN을 보낼 것이기 때문에 그에 대한 처리를 합니다. 
성공을 하면 ViewModel에서 구글 로그인을 진행합니다. 
실패했으면 실패한 내용을 화면에 띄웁니다.

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        // 구글 로그인 액티비티 처리 코드 라면
        if (requestCode == RC_SIGN_IN) {
            // 처리가 성공 했으면
            if (resultCode == RESULT_OK) {
                // 처리 했던 정보를 가져 옴
                Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
                try {
                    // 계정을 꺼냄
                    GoogleSignInAccount account = task.getResult(ApiException.class);
                    // 서버 로그인
                    mLoginViewModel.googleLogin(account);
                } catch (ApiException e) {
                    // 실패 했음을 알리도록 메시지 띄우기
                    showLoginFailed();
                    Log.w("google", "signInResult:failed code=" + e.getStatusCode());
                }
            } else {
                // 실패 했음을 알리도록 메시지 띄우기
                showLoginFailed();
            }
            // 진행 중임을 나타 내는 뷰를 끄기
            progressbarDone();
        }
    }

5. 실패 메시지 띄우기

실패  메시지를 띄우는 것을 메소드로 묶어서 따로 빼놨습니다. 

Toast를 통해 화면에 보여주게 됩니다. message는 변수로 전달하거나 R.String의 id값으로 전달했습니다.

진행 중임을 알리는 로딩바를 끄는 메소드도 있습니다.

    /**
     * 로그인 실패한 것을 알리는 메시지를 띄웁니다.
     *
     * @param errorString - 띄울 메시지 (R.string.(?))
     */
    private void showLoginFailed(@StringRes Integer errorString) {
        Toast.makeText(getApplicationContext(), errorString, Toast.LENGTH_SHORT).show();
    }

    /**
     * 로딩 바를 끕니다.
     */
    private void progressbarDone() {
        mMainBinding.loading.setVisibility(View.GONE);
    }

    /**
     * 로그인 실패한 것을 알리는 메시지를 띄웁니다.
     * failString : 띄울 메시지
     */
    private void showLoginFailed() {
        String failString = "로그인에 실패 했습니다.";
        Toast.makeText(getApplicationContext(), failString, Toast.LENGTH_SHORT).show();
    }

6. onDestroy()

자원이 원활하게 반환이 되도록 할당을 해제 해줬습니다.

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 할당 해제
        mMainBinding = null;
        mGoogleSignInClient = null;
        mLoginViewModel = null;
    }

이상으로 안드로이드에서 로그인 구현을 해보았습니다. 시작은  Firebase 중심으로 되었지만, 구글, 네이버, 카카오까지 로그인을 해보는 상황이 만들어졌네요. 
역할 분리가 잘 안된 부분도 있었지만, 그런 부분을 다음에는 좀 더 신경써서 해보도록하죠. ㅎㅎ

728x90
반응형