UI 레이어와 단방향 데이터 흐름(Unidirectional Data Flow)
UI 레이어와 단방향 데이터 흐름(Unidirectional Data Flow)
안드로이드 아키텍처에서 UI 레이어는 두 가지 핵심 역할을 담당합니다. 첫째는 애플리케이션 데이터를 화면에 렌더링하는 것이고, 둘째는 사용자와 상호작용하는 인터페이스 역할입니다. UI 레이어 내에서 상태(state)가 어떻게 흘러가는지에 따라 전체 애플리케이션의 예측 가능성과 유지보수성이 결정됩니다. 단방향 데이터 흐름(Unidirectional Data Flow, 이하 UDF)은 상태 업데이트와 이벤트 처리를 하나의 방향으로만 강제하며, 불변(immutable) 상태 객체를 통해 각 렌더링이 일관된 스냅샷을 기반으로 이루어지도록 보장합니다.
이번 면접 질문을 통하여 아래 내용들을 학습하실 수 있습니다.
- 안드로이드 아키텍처에서 UI 레이어의 역할과 데이터 레이어의 출력을 화면에 표시하기 위해 어떻게 변환하는지 설명할 수 있습니다.
- UDF 사이클의 구조를 이해할 수 있습니다.
ViewModel에서의 상태 생성, UI에서의 상태 관찰, 그리고ViewModel로의 이벤트 전달 과정을 포함합니다. - UI 상태를 불변
data class로 모델링해야 하는 이유를 설명할 수 있습니다. - UI에서 상태를 직접 변경할 때 발생하는 문제점을 파악할 수 있습니다.
StateFlow와 Jetpack Compose를 활용하여 실제 화면 구현에 UDF를 적용할 수 있습니다.
UI 레이어의 역할
UI 레이어는 사용자와 나머지 애플리케이션 사이에 위치합니다. 데이터 레이어로부터 데이터를 가져와 렌더링에 최적화된 형태로 변환하거나 결합한 뒤, 화면에 표시합니다. 이때 데이터 레이어에서 제공하는 데이터가 UI에서 필요로 하는 형태와 일대일로 매핑되는 경우는 드뭅니다. 가령, 뉴스 화면이라면 기사 데이터, 북마크 상태, 사용자 메시지, 접근 권한 등 여러 데이터를 하나의 UI 상태 객체로 결합해야 합니다.
이러한 변환 책임이 있기 때문에, UI 레이어는 단순히 수동적으로 데이터를 표시하는 렌더러가 아닙니다. 여러 데이터 소스를 통합된 스냅샷으로 합쳐 사용자에게 어떻게 보여줄지 능동적으로 결정하는 역할을 합니다. 이 변환 작업은 ViewModel이 담당합니다. 컴포저블이나 Activity는 렌더링에 바로 사용할 수 있는 상태를 전달받을 뿐, 비즈니스 로직이나 데이터 필터링, 결합 코드를 포함하지 않습니다.
이러한 분리 덕분에 UI 레이어를 얇고 테스트하기 쉬운 구조로 유지할 수 있습니다. ViewModel은 UI와 독립적으로 단위 테스트가 가능하며, 컴포저블은 실제 데이터 소스 없이도 가짜 상태(fake state)만으로 테스트할 수 있습니다. 면접에서 UI 레이어의 역할을 설명할 때, 이처럼 관심사 분리(separation of concerns)가 테스트 용이성과 직결된다는 점을 함께 언급하시면 좋습니다.
UDF 사이클
UDF는 상태 관리를 세 가지 참여자가 포함된 엄격한 사이클로 구성합니다.
ViewModel: UI 상태의 단일 진실 공급원(single source of truth) 역할을 하며, 관찰 가능한 스트림 형태(일반적으로StateFlow)로 상태를 노출합니다.- UI: 해당 스트림을 관찰하고 수신한 상태를 렌더링합니다. UI는 상태를 직접 수정하지 않습니다.
- 사용자 액션: 버튼 탭이나 텍스트 입력과 같은 사용자 동작은 UI에서
ViewModel로 이벤트 형태로 전달됩니다.ViewModel은 각 이벤트를 처리하고, 내부 데이터를 업데이트한 뒤, 새로운 UI 상태를 방출합니다.
이 사이클을 통해 상태 변경은 항상 ViewModel에서 시작되고, 하나의 관찰 가능한 채널을 거쳐 완전한 스냅샷 형태로 UI에 도달합니다. 어디에서 상태가 변경되었는지, 어떤 컴포넌트가 특정 변경을 담당하는지에 대한 모호함이 사라집니다.
또한 이 사이클 구조는 테스트를 간단하게 만들어 줍니다. ViewModel을 테스트하려면 이벤트를 전송하고 방출된 상태를 검증하면 됩니다. UI를 테스트하려면 알려진 상태를 제공하고 렌더링 결과가 기대값과 일치하는지 확인하면 됩니다. 어느 쪽 테스트든 상대 컴포넌트가 필요하지 않습니다.
불변 UI 상태
UI 상태는 불변(immutable) data class로 모델링합니다. 한 번 생성된 인스턴스는 변경할 수 없으며, 업데이트가 필요하면 copy()를 통해 새 인스턴스를 생성합니다.