이전에 구현에 포커스를 맞추고 Activity에 모든 것을 위임한 MVC 구조에서 MVVM-Repository 패턴으로 전반적인 구조 개선을 함
그렇게 함으로써 관심사 분리가 되어, Fragment & Activity에선 View의 변화만을 Action등의 UI Controller를 주로 하고 ViewModel에선 UI에 필요한 데이터를 가지고 있어서 UI에 변화를 observe 할 수 있게 LiveData를 활용하여 해당 UI에 있는 데이터를 관리함
직접적인 네트워크 통신과 API 처리를 위한 데이터 계층을 Repository로 만들어 해당 계층에서 API 통신을 하고 그 결과값을 ViewModel에 처리해주는 앞서 본 AAC-Repository대로 관심사 분리를 하여 적용함
추가로 Data Binding을 활용하여서 양방향 바인딩을 통해 EditText를 바로 반영하고 ViewModel에서 받거나 처리한 데이터에 대해서 바로 적용을 하는 등 기존이라면 직접 View를 불러오고 데이터를 처리하는 등 스파게티 코드나 한 클래스 방대한 코드가 늘어서는 부분을 한결 더 간소화 시키고 효율성을 증대시킴
어쨌든 전반적인 구조 개선을 통해 관심사 분리 및 각각 UI별 처리에 대해서 좀 더 효율적이게 관리할 수 있었음
앞서 개선사항 중 네트워크 통신에 대해서 썼는데 이 부분의 경우 기존의 Retrofit을 활용해서 콜백 형식을 받아도 되지만, 앞으로 진행하고 받을 API에 대해서 그에 따른 콜백량 증가와 현재에도 Repository에서 할 부분이 많아져 보일 것으로 보임
데이터 흐름과 전달에 관한 프로그래밍 패러다임인데, 데이터 흐름을 먼저 정의하고 변경되었을 때 함수나 수식이 업데이트 되는 방식으로 활용함
모델의 값에 변화가 생겼을 때 뷰를 자동으로 업데이트 해주는 목적으로 사용하고 시스템에 어떤 이벤트가 발생했을 때 처리하는 것으로 현재 서버 다수와 통신하고 API가 많아질 수 있는 상황에서 각각 콜백 처리만 하는것은 콜백 지옥이 될 수 있어보였음
그래서 RxJava를 통해서 좀 더 쉽게 데이터 모음으로써 비동기 처리를 하기 위해 RxJava를 도입함
이 때 기존 Retrofit가 다르게 클라이언트 요청을 처리할 때 다수의 비동기 실행 흐름을 생성하고 그것의 결과를 취합하여 최종 리턴하는 방식으로 내부 로직이 바뀜
그래서 콜백을 사용하지 않고 처리할 수 있는 것임
이외에 Reactive Programming에 대해선 데이터를 구독하고 발행하는 처리 등 매우 다양하고 방대한 내용이 담고 있기 때문에 그 부분은 생략하고 실제 이 부분을 어떻게 Android에 적용해서 개선했는지 알아볼 것
앞서 위에서 본 바와 같이 Retrofit을 활용해서 해당 API에 대해서 콜백 형태로 그 값을 받아서 처리했음, 이를 RxJava를 적용해서 진행할 것
그러기 앞서 RxJava에 대한 라이브러리를 추가해주고 RetrofitBuilder에 Adapter 역시 추가 해줘야함
new Retrofit.Builder()
.baseUrl("http://10.0.2.2:8080")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
그 다음 기존에 Retrofit 사용시 API 인터페이스에서 리턴 타입을 Call로 하여서 받았지만 이제 RxJava를 적용하기 때문에 다른 리턴으로 받아줘야함
여기서 리턴에 대해서 Observable, Completable 등 적용할 수 있지만 여기선 Single을 처리할 것 왜냐하면 현재 로그인 & 토큰 처리 등 API 설계 자체가 success, error로 명확하게 구분하게 받아왔기 때문에 Single로 처리함
이 Single로 받았다는 것은 오직 데이터 1개를 받아서 처리할 수 있는 것인데 아래와 같이 API 선언을 할 수 있음
@POST("/auth/login")
Single<TokenDto> getSignIn(@Body AccountLoginDto accountLoginDto);
로그인 & 회원가입으로 데이터 처리가 명확하기 때문에 Single로 처리해서 진행함
그 다음 Repository에서 아래와 같이 RxJava를 적용할 수 있음
그 전에 Disposable 먼저 정의해서 썼는데 이를 쓴 것은 데이터를 구독하고 처리하는 형태인데, 이에 대해서 데이터가 더 이상 쓰이지 않을 상황에 대해서 구독해제 처리를 위해서 진행을 함
그리고 여기서 여러 API를 호출할 수 있기 때문에 RxJava를 도입한 것인데 이 부분에 대해서 CompositeDisposable을 적용, 여러개를 처리할 수 있도록 정의함
private final CompositeDisposable disposable = new CompositeDisposable();
이 Disposable을 통해서 데이터를 발행하고 구독 해제를 할 수 있음 즉, Android에서 로그인이나 회원가입 등의 API 호출 처리를 통해 데이터를 Disposable로 발행을 한 뒤 해당 데이터가 다 쓰이고 다음 화면으로 넘어가거나 처리가 끝난 경우 구독 해제 처리를 할 수 있게됨
이제 세부적으로 위에서 사용한 로그인을 RxJava를 활용한 것을 보면 아래와 같음
public void getToken(AccountLoginDto accountLoginDto) {
disposable.add(userApi.getSignIn(accountLoginDto)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
tokenDto -> {
Log.e("완료", "응답값: " + tokenDto.getAccessToken());
Preferences.setAccessToken(tokenDto.getAccessToken());
Preferences.setRefreshToken(tokenDto.getRefreshToken());
}, error -> {
Log.e("실패", "error:" + error.getMessage());
}
));
}
먼저 앞서 정의한 Disposable에 해당 통신 처리를 추가하고 구독 사용할 스레드를 지정하는데 io 스케줄러를 선택함, 네트워크성 처리를 하기 때문에 일반 스케줄러를 쓸 수 없음
그 다음 Observable이 다음 처리할 스레드를 지정하는데 앞서 백그라운드 쓰레드로 작업을 했고 변경에 대해서는 메인 쓰레드로 처리하게 함 즉 받은 데이터에 대해서는 Android UI 쓰레드에서 작업을 함
마지막으로 subscribe로 데이터를 발행해서 처리를 함, 이 때 성공과 실패한 메소드를 나타나는데 이를 람다식으로 활용해서 각각 케이스에서 성공, 실패할 경우 처리를 추가함
그러면 위 부분으로 로그인 처리는 완료된 것인데 ViewModel에서 Data Binding으로 onLogin 메소드를 통해서 로그인 버튼 누르면 아래와 같이 위의 네트워크 처리를 한다고 볼 수 있음
public void onLogin(View view) {
AccountLoginDto accountLoginDto = new AccountLoginDto(email.getValue(), password.getValue());
userRepository.getToken(accountLoginDto);
// ....
}
이렇게 RxJava를 활용해서 네트워크 통신을 리팩토링함, 근데 어떻게 보면 이 돌아가는 원리 자체가 Reactive Programming으로 데이터 처리하는 다른 것임
하지만 어찌됐든 Android에서 네트워크나 DB처리에 있어서 UI Thread에서 하지 않고 백그라운드 쓰레드에서 작업을 처리하고 변경을 UI Thread 하는 것과 같은 원리는 동일하게 적용이 됨
그리고 앞서 말했듯이 이 부분이 다 활용된다면 구독 해제를 위해서 아래 부분도 추가를 해 줌
@Override
protected void onCleared() {
super.onCleared();
disposable.clear();
}
지금까지 Java 버전으로 적용을 진행하였으나 이 부분에서 레거시하게 그리고 러프하게 작성한 부분도 있고 코드 길이 자체가 좀 길어지고 스파게티 코드가 그리고 보일러 플레이트 코드가 되는 부분이 존재함
이 부분에서 코틀린을 활용하여 적용하면 현재 개발하는 코드적인 부분과 이런 부분들을 1차적으로 개선을 할 수 있을것으로 보여 본격적으로 메인 기능을 다 담고 디테일하게 들어가기 전에 마이그레이션 할 예정
DI 패턴을 적용하려는 이유는 현재 관심사 분리로 코드 관리가 수월해지고 명확해졌지만, 각각 레이어에 있어서 new 연산자로 인스턴스 생성을 직접 하는 방식으로 쓰고 있음
이 부분은 ViewModel을 View에서 생성할 때 그리고 Repository를 쓰기 위해서 ViewModel에서 생성할 때 또 Repository에서 역시 Preferences를 위해서 해당 init으로 객체를 생성하는 등 상당히 여러 부분에서 의존관계가 나타나고 있음
이 부분을 DI를 활용, 주입할 객체와 주입받아야 할 부분에 대해서 구분을 확실히 처리하여서 좀 더 효과적으로 new 연산자를 남발하고 인스턴스를 생성하는 부분에 대해서 개선을 할 수 있을 것으로 보임
그래서 Dagger2, Hilt 등 DI를 적용할 예정, Kotlin으로 옮기면서 진행할 수 있음