리팩토링2

gang_shik·2022년 6월 18일
0

프로젝트 Fit-In

목록 보기
5/10

추가 개선사항

  • 이전에 구현에 포커스를 맞추고 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를 불러오고 데이터를 처리하는 등 스파게티 코드나 한 클래스 방대한 코드가 늘어서는 부분을 한결 더 간소화 시키고 효율성을 증대시킴


RxJava 적용

  • 어쨌든 전반적인 구조 개선을 통해 관심사 분리 및 각각 UI별 처리에 대해서 좀 더 효율적이게 관리할 수 있었음

  • 앞서 개선사항 중 네트워크 통신에 대해서 썼는데 이 부분의 경우 기존의 Retrofit을 활용해서 콜백 형식을 받아도 되지만, 앞으로 진행하고 받을 API에 대해서 그에 따른 콜백량 증가와 현재에도 Repository에서 할 부분이 많아져 보일 것으로 보임

  • 그래서 이 부분에 대해서 RxJava, Reactive Programming을 접목시켰음

Reactive Programming

  • 데이터 흐름과 전달에 관한 프로그래밍 패러다임인데, 데이터 흐름을 먼저 정의하고 변경되었을 때 함수나 수식이 업데이트 되는 방식으로 활용함

  • 모델의 값에 변화가 생겼을 때 뷰를 자동으로 업데이트 해주는 목적으로 사용하고 시스템에 어떤 이벤트가 발생했을 때 처리하는 것으로 현재 서버 다수와 통신하고 API가 많아질 수 있는 상황에서 각각 콜백 처리만 하는것은 콜백 지옥이 될 수 있어보였음

  • 그래서 RxJava를 통해서 좀 더 쉽게 데이터 모음으로써 비동기 처리를 하기 위해 RxJava를 도입함

  • 이 때 기존 Retrofit가 다르게 클라이언트 요청을 처리할 때 다수의 비동기 실행 흐름을 생성하고 그것의 결과를 취합하여 최종 리턴하는 방식으로 내부 로직이 바뀜

  • 그래서 콜백을 사용하지 않고 처리할 수 있는 것임

  • 이외에 Reactive Programming에 대해선 데이터를 구독하고 발행하는 처리 등 매우 다양하고 방대한 내용이 담고 있기 때문에 그 부분은 생략하고 실제 이 부분을 어떻게 Android에 적용해서 개선했는지 알아볼 것

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();
    }

추후 업데이트 추가

  • 이처럼 네트워크 통신 부분에 대해서 RxJava로 적용하여서 함수형으로 콜백을 떠나서 처리를 하여 네트워크 통신 부분을 간소화 하였음

Kotlin 버전 적용

  • 지금까지 Java 버전으로 적용을 진행하였으나 이 부분에서 레거시하게 그리고 러프하게 작성한 부분도 있고 코드 길이 자체가 좀 길어지고 스파게티 코드가 그리고 보일러 플레이트 코드가 되는 부분이 존재함

  • 이 부분에서 코틀린을 활용하여 적용하면 현재 개발하는 코드적인 부분과 이런 부분들을 1차적으로 개선을 할 수 있을것으로 보여 본격적으로 메인 기능을 다 담고 디테일하게 들어가기 전에 마이그레이션 할 예정

DI 패턴 적용

  • DI 패턴을 적용하려는 이유는 현재 관심사 분리로 코드 관리가 수월해지고 명확해졌지만, 각각 레이어에 있어서 new 연산자로 인스턴스 생성을 직접 하는 방식으로 쓰고 있음

  • 이 부분은 ViewModel을 View에서 생성할 때 그리고 Repository를 쓰기 위해서 ViewModel에서 생성할 때 또 Repository에서 역시 Preferences를 위해서 해당 init으로 객체를 생성하는 등 상당히 여러 부분에서 의존관계가 나타나고 있음

  • 이 부분을 DI를 활용, 주입할 객체와 주입받아야 할 부분에 대해서 구분을 확실히 처리하여서 좀 더 효과적으로 new 연산자를 남발하고 인스턴스를 생성하는 부분에 대해서 개선을 할 수 있을 것으로 보임

  • 그래서 Dagger2, Hilt 등 DI를 적용할 예정, Kotlin으로 옮기면서 진행할 수 있음

profile
측정할 수 없으면 관리할 수 없고, 관리할 수 없으면 개선시킬 수도 없다

0개의 댓글