2주차에 이어 팀 프로젝트를 진행하였다. 구현과 리팩토링을 통해서 배운 부분과 수업에서 배운 내용을 정리하였다.
Android App Architecture
안드로이드 앱의 크기가 점점 커지면서 앱을 확장하고 품질과 견고성을 개선하고 더 쉽게 테스트할 수 있는 아키텍처를 지닌 코드를 설계하는 것이 중요하다.
앱 아키텍처를 설계함에 있어서 관심사를 분리하고 데이터 모델에서 UI를 도출하는 것이 중요하다. Activity나 Fragment에서 모든 코드를 작성하는 경우 구성요소 수명 주기와 관련된 많은 문제가 발생할 수 있고 테스트가 어려워질 수 있기 때문에 관심사를 분리하고 클래스에 대한 의존성을 최소화하는 것이 좋다.
따라서 구글에서 제공하는 앱 아키텍처에 대한 가이드에 따라서 개발할 수 있도록 하여야하고 이 앱 아키텍처는 UI Layer, Data Layer, Damain Layer로 나뉘어질 수 있다.
참고: https://developer.android.com/jetpack/guide?hl=ko
https://developers-kr.googleblog.com/2022/02/rebuilding-our-guide-to-app-architecture.html
UI Layer
UI Layer는 간단하게 말하자면 입력을 처리하고 화면상에 나타내는 부분으로 UI를 그리는데 필요한 데이터를 처리한다. 구글의 UI 레이어 문서에서는 UI State를 정의하고 UI State에 따라 UI를 다르게 변화시킬 수 있다고 한다. 이 때 Flow나 Livedata를 이용해서 관찰 가능한 데이터 홀더에 UI 상태를 노출시켜서 뷰모델에서 직접 가져오지 않아도 상태 변경사항에 따라 UI가 빠르게 반응하도록 할 수 있다.
이번 프로젝트에서는 UI State를 사용하여 개발을 진행하지는 않았기 때문에 다음 프로젝트나 이번 프로젝트를 리팩토링할 때 적용해보는 것이 좋을 것 같다.
참고 : https://developer.android.com/jetpack/guide/ui-layer
DataBinding
DataBinding 라이브러리는 프로그래매틱 방식이 아니라 선언적 형식으로 레이아웃의 UI 구성요소를 앱의 데이터 소스와 결합할 수 있는 지원 라이브러리이다.
즉 findViewById()와 같이 액티비티에서 직접 호출할 필요없이 xml에서 데이터를 할당할 수 있도록 해준다.컴포넌트의 생명주기와 LiveData를 이용하여 binding을 처리하면 데이터 처리에 따라 UI를 손쉽게 변경할 수 있다.할당식에 대한 이해가 필요하며, 성능면에서는 더 우수한 ViewBidning과 비교하여 언제 무엇을 사용할 지에 대해 고민을 해야한다.
참고: https://developer.android.com/topic/libraries/data-binding?hl=ko
ViewModel
ViewModel은 뷰의 UI 데이터를 가지고 있는 홀더 역할을 한다. UI를 그릴 때 필요한 데이터와 UI State를 가지고 있을 수 있다.
Data Layer의 Repository와 데이터 처리나 관리의 경계를 정하는 것이 중요한데 ViewModel은 UI를 그릴 때 필요한 데이터를 관리하기 때문에 UI와 관련있는 데이터인지를 판단하고 그에 맞게 처리 기준을 정하는 것이 좋다.
LiveData
LiveData는 lifecycle에 따라 관찰되어 질 수 있는 데이터 홀더 클래스이고 Observer 패턴을 기반으로 구현되어 있다.
ViewModel에서 LiveData를 객체를 두고 Activity나 Fragment에서 해당 LiveData를 Observe하는 형태로 구현을 할 수 있다.
추가로 아래의 LiveData 클래스 코드를 보면 currenState == DESTROYED 일 때 옵저버를 remove해주기 때문에 따로 removeObserver()를 해주지 않더라도 Destroy 될 때 옵저버가 제거된다.
@Override
public void onStateChanged(@NonNull LifecycleOwner source,
@NonNull Lifecycle.Event event) {
Lifecycle.State currentState = mOwner.getLifecycle().getCurrentState();
if (currentState == DESTROYED) {
removeObserver(mObserver);
return;
}
Lifecycle.State prevState = null;
while (prevState != currentState) {
prevState = currentState;
activeStateChanged(shouldBeActive());
currentState = mOwner.getLifecycle().getCurrentState();
}
}
참고 : https://developer.android.com/topic/libraries/architecture/livedata?hl=ko
Observer 패턴
Observer패턴은 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버들의 목록을 객체에 등록하여 상태 변화가 있을 때마다 메서드 등을 통해 객체가 직접 목록의 각 옵저버에게 통지하도록 하는 디자인 패턴이다.
참고 : https://ko.wikipedia.org/wiki/옵서버_패턴
Data Layer
Data Layer는 애플리케이션 데이터 및 비즈니스 로직이 포함된 레이어이다. Data Layer를 UI Layer와 분리하면 이 Data Layer를 여러 화면에서 사용할 수 있고 단위 테스트를 위해 UI 외부에서 비즈니스 로직을 재현할 수 있다. Data Layer는 DataSource를 포함하는 Repository로 구성되어 있다.
참고 : https://developer.android.com/jetpack/guide/data-layer
Repository
Repository는 지난 주에 작성했던 우아한 테크캠프 2주차 - 1 의 중반부에 정리를 한 번 했었는데 기본적으로 DataSource를 포함하고 있고 Repository에서만 이 DataSource에 접근이 가능하도록 구현해야 한다.
DataSource
LocalDataSource, RemoteDataSource와 같이 각 DataSource는 로컬 데이터 베이스나 네트워크와 같은 하나의 데이터 소스만 사용하고 DataSource는 데이터 작업을 위해 애플리케이션과 시스템 간의 가교 역할을 한다. DataSource는 Repository에서만 액세스 가능하고 UI Layer나 Domain Layer에서 DataSource 직접 종속 항목으로 있어서는 안된다.
DataSource와 Repository에서 처리해야하는 기준에 대해서도 명확하게 정할 수 있어야하는데 수업에서 배우고 또 개인적으로 생각했을 때는 DataSource에서는 API 호출이나 로컬 데이터베이스에 접근하고 그에 맞는 에러 처리를 한 후 그 데이터와 성공인지 실패인지를 포함하는 Result객체를 넘겨주는 형태로 구현하는게 좋을 듯 싶다.
Repository와 DataSource를 인터페이스로 두고 따로 다시 구현하는 이유?
인터페이스에서 먼저 구현할 기능을 정하거나 제한할 수 있어 인터페이스를 구현할 때 구현해야하는 부분이 명확해질 수 있고, 테스크 코드를 작성하여 테스트를 진행할 때에 FakeRepository와 같이 테스트를 위한 구현 클래스를 사용할 때에도 그에 맞는 추가적인 수정을 하지 않아도 되기 때문에 인터페이스로 구현을 한다고 생각한다.
'활동 > 우아한테크캠프5기' 카테고리의 다른 글
우아한 테크캠프 4,5주차 - 1 (0) | 2022.08.06 |
---|---|
우아한 테크캠프 3주차 - 2 (0) | 2022.07.26 |
우아한 테크캠프 2주차 - 2 (0) | 2022.07.18 |
우아한 테크캠프 2주차 - 1 (0) | 2022.07.17 |
우아한 테크캠프 1주차 (0) | 2022.07.10 |
댓글