해당 글은 아래 링크의 번역본 입니다. 따라서 의역 오역이 있을 수도 있습니다.
Part 1: 반응형 UIs
안드로이드 초기부터, 엣지 케이스가 가득한 안드로이드 생명주기 을 이해하는 것은 매우 어렵다는 것을 잘 알고 있습니다. 그래서 최대한 로직을 생명주기에 상관없이 사용할 수 있도록 노력해왔습니다.
위의 방식처럼 하기 위해서, 계층이 구분되는 아키텍쳐를 만드는 것을 추천합니다. 계층이 구분됨으로써 우리는 안드로이드 생명주기에 대해 복잡한 생각 없이 UI 에 독립적인 코드를 작성할 수 있습니다. 예를 들어 비즈니스 로직을 처리하는 도메인 계층을 추가할 수 있고, 데이터 계층도 추가할 수 있습니다.
더 나아가, presentation 계층은 각각 다른 책임을 가지는 컴포넌트들로 나눠질 수 있다는 것을 우리는 알고있습니다.
- View - 생명주기 콜백 메서드(onCreate, OnResume... etc). 유저 터치 이벤트, 화면 전환(Navigation) 등을 처리하는 Activity 와 Fragment 가 있습니다.
- Presenter 또는 ViewModel - View 에 데이터를 제공하고, View 의 생명주기와는 관계 없이(의식하지 않는) 구성되어 있습니다. 이것은 의미는 뷰가 재성성될 때 Presenter 또는 ViewModel 에서 View 를 위해 어떠한 추가처리할 필요가 없습니다
Presenter, ViewModel 은 이름에서 느껴지듯이 데이터를 가공하여 뷰에 전달하는 각각의 다른 메커니즘을 가지고 있습니다.
- 뷰에 대한 참조를 가지고, 참조를 이용하여 뷰를 제어하는 방식은 Presenter 의 방식입니다.
- 옵저버(관찰자)에게 관측 가능한 데이터를 노출하여, 뷰를 제어하는 방식은 ViewModel 의 방식입니다.
위와 같은 방식은 안드로이드 커뮤니티에 이미 너무나 잘 알려지있고 컨벤션으로 자리잡았습니다. 하지만 인터넷에는 다양한 ViewModel. Presenter 의 각기 다른 방식으로 구현되어 있고, 잘못 구현된 글들도 많습니다. 따라서 저자의 제안으로는 AAC's ViewModel 를(AAC 가 꼭 아니여도 되지만) Presentation 계층에서만 사용하도록 정의하고 있습니다. 이를테면 아래와 같은 특성이 있습니다.
- 회전, 언어 변경, 창 크기 조절, 다크모드 변경 등의 구성요소 변경으로 부터 값을 유지하는 작업
- Lifecycle owner 가 종료되면 호출되는 onCleared 라이프 사이클 콜백을 가진 매우 간단한 생명주기
그리고 ViewModel 은 옵저버 패턴을 사용하도록 디자인 되었습니다.
- 뷰에 대한 참조가 없습니다
- 옵저빙 하고 있는 것이 무엇인지 모르는채로 데이터를 받기만 하며 LiveData 를 사용하여 이것이 가능해집니다.
Activity, Fragment 또는 어떤 Lifecycle owner 로 부터 View가 생성될 때, View가 구독하는 라이브 데이터를 한개 혹은 한개 이상을 ViewModel 이 가지고 있게 됩니다.
구독에 대한 처리는 LiveData.observe 메서드 혹은 XML 에서 DataBinding 을 이용할 수 있습니다.
만약에 기기 회전이 발생하게 되면, 뷰가 파괴되고(#1), 새로운 인스턴트(#2)가 생기게 됩니다.
만약에 뷰모델 안에 액티비티 혹은 뷰에 대한 참조를 가지고 있었다면, 아래와 같은 처리가 필요했을 것 입니다.
- View가 파괴될 때 현재 동작하고 있는 작업을 정리하기
- View가 현재 변경되는 과정일 때, 문제가 생기지 않도록 접근 불가하도록 처리하기
그러나 우리는 VIewModel 과 LiveData 를 사용하면 더이상 위와 같은 처리에 신경쓰지 않아도 됩니다. 이것이 App Architecture 가이드 방식을 추천하는 이유 입니다.
(Lifecycle) Scope
Activity, Fragment 는 뷰모델의 생명주기 보다 같거나 더 짧은 생명주기를 가집니다. 따라서 작업(명령, Operation)의 범위(Scope)는 중요합니다. 작업으로는 네트워크로 부터 데이터 가져오기, 결과를 정렬하기, 글자를 재배열 하기 등으로 앱에서 일어나는 모든 것을 의미합니다. 어떤 작업이 시작되면 작업이 실행 될 때, 취소 될 때 등의 범위를 항상 생각해볼 필요가 있습니다.
실행과 취소 두가지 케이스에 대해 알아보겠습니다.
- Activity 의 onStart 에서 시작하고 onStop 에서 정지하는 어떤 작업이 있습니다. #1
- ViewModel 의 init 블록에서 시작하고, onCleared 에서 정지하는 작업이 있습니다 #2
- Activity 범위로 지정된 데이터를 가져오는 작업은 기기 회전등이 일어나게 되면 다시 데이터를 가져와야합니다. 따라서 ViewModel 을 범위로 지정하는 것이 좋습니다.
- 텍스트 위치 재배치는 기기 회전이 일어나면, 해당 가로/세로 모드에 맞게 재배치가 되어야하기 때문에 ViewModel 범위를 지정하게되면 문제가 생기기 때문에 Activity 범위로 지정해야 합니다.
실제로 현업에서는 위의 Activity, ViewModel 범위 뿐만 아니라 다양한 범위를 가질 수 있습니다.
예를 들어 Android dev summit app 에서는 아래와 같은 범위들이 존재합니다.
- 화면당 여러개 존재하는 Fragment 범위
- 화면에 하나만 존재하는 Fragment's viewModel 범위
- MainActivity 범위
- MainActivity's viewModel 범위
- Application 범위
이렇게 다양한 범위 들이 생길 수 있고, 다양한 범위를 모두 고려하면서 개발하는 것은 매우 어렵습니다. 그렇기 때문에 최대한 동시성을 구조화하는 방법들이 필요합니다.
하나의 해결책은 코틀린에서 제공하는 코루틴 입니다
많은 이유로 안드로이드에서 코루틴이 사용되고 있고 그 이유들로는 아래와 같은 것들이 있습니다.
- MainThread 에서 WorkerThread 로 변경이 매우 편리합니다. 안드로이드 어플리케이션은 유저들에게 좋은 경험을 제공하기 위해 끊임 없이 스레드 사이를 스위칭 합니다. 코루틴은 이런 작업을 단순하게 만듭니다.
- 보일러 플레이트가 매우 작습니다. 코루틴은 언어스펙에서 지원되기 때문에, suspend 같은 예약어가 존재하고 사용하기 쉽습니다.
- 구조화된 동시성을 가집니다. 구조화된 동시성의 의미는 어떤 작업을 시작할 때는 작업 범위를 필수적으로 정의해야합니다. 이 과정에서 많은 보일러 플레이트 코드들이 제거되도록 보장합니다.(Rx 이전에는 AsyncTask 혹은 Thread 등으로 많은 처리 코드가 필요했습니다), 자동 취소와 같은 동시성 구조를 생각해볼 수도 있습니다.
만약 코루틴에 대해 공부를 하고 싶다면 코틀린 오피셜 문서 혹은 안드로이드 코루틴 소개를 참고하시면 됩니다.
'Android > 번역' 카테고리의 다른 글
LiveData, Coroutine, Flow 를 이용한 반응형 UI - Part 3 (0) | 2021.06.07 |
---|---|
LiveData, Coroutine, Flow 를 이용한 반응형 UI - Part 2 (0) | 2021.06.06 |
코루틴을 뷰에 적용하기 (1) | 2021.03.14 |
코루틴 플로우(Flow) 읽어보기 (1) | 2021.03.08 |
코틀린으로 알아보는 SOLID 원칙 (2) | 2021.03.03 |