본문 바로가기
오늘은 뭘 배울까?/공식문서

[번역] UI Layer - 안드로이드 앱 아키텍처

by Kim Juhwan 2022. 11. 11.

1. 왜 앱 아키텍처 설계를 해야 할까?
2. UI 레이어가 하는 일
3. UI 레이어를 구현하는 방법
   3-1. UI 상태를 정의하는 방법
   3-2. UI 상태의 생산을 관리하는 방법

   3-3. UDF 원칙에 따라 관찰 가능한 데이터 유형으로 UI 상태를 노출하는 방법
   3-4. 관찰 가능한 UI 상태를 소비하는 UI를 구현하는 방법

 

 

 


 

 

이 글은 안드로이드 공식문서 중 "앱 아키텍처 학습 과정" 페이지를 공부하며 작성한 글입니다.
개인적인 생각이나 의견이 포함되어 있음을 알려드립니다.

1. 왜 앱 아키텍처 설계를 해야 할까?

아키텍처(설계도)를 나타내는 그림

 

건물을 지을 때 설계도를 그려야 올바른 구조를 잡을 수 있다.

만약 올바른 구조가 없다면 건물이 무너질 수 있다.

앱도 마찬가지다.

올바른 구조가 없다면 앱도 무너지기 마련이다.

그렇다면 앱에서의 올바른 구조란 무엇일까?

 

추천 아키텍처 구조 - 3개의 레이어로 이루어져 있다.

 

구글에서 권장하는 아키텍처 구조는 크게 3가지 레이어로 이루어져 있다.

UI 레이어, Domain 레이어, Data 레이어

[목차 1]에서는 각 레이어들의 간단한 개념을 알아보고

나머지 이번 포스팅에서는 UI 레이어에 대해 자세히 알아보려 한다.

 

Data 레이어 설명을 위한 그림

 

건물에는 고유한 목적을 수행하는 다양한 방들이 필요하다.

욕실, 주방, 발코니 등 용도에 따라 방이 나뉘듯이

Data 레이어 또한 각 데이터 유형에 대해 다른 repository class를 가진다.

영화와 관련된 Movies Repository Class

음악과 관련된 Music Repository Class처럼 말이다.

 

Repository를 포함한 Data 레이어의 구성 요소들은

데이터를 생성, 저장 및 변경하는 방법을 결정하는 규칙으로 구성된다.

 

UI 레이어 설명을 위한 그림

 

벽을 페인팅하고, 바닥 타일을 선택하고, 주방 조리대를 고르고...

마치 우리가 건물의 시각적인 세부사항을 정하듯

UI 레이어에서는 사용자가 상호 작용할 수 있는 시각적인 화면을 그린다. (정확히는 뷰를 그리고 데이터를 표시한다)

 

 

버튼을 누른다거나 인터넷으로 글 리스트를 읽어오는 등

다양한 외부 입력에 반응하여 끊임없이 변화하는 것.

이것이 바로 UI 레이어가 하는 일이다.

 

Domain 레이어를 설명하기 위한 그림

 

삼성 smart things 같은 앱을 사용해봤다면

앱 안에서 특정 기능을 수행할 수 있다는 것을 알 것이다.

사용자들은 기능을 사용하지만 어떠한 복잡한 로직이 수행되는지 알지 못한다.

스마트폰의 화면을 TV에 미러링한다고 해서 사용자가 그 로직을 알고 이해하는 것은 아니다.

그저 그 기능을 사용할 뿐...

 

Domain 레이어의 역할도 같다.

복잡한 비즈니스 로직의 캡슐화를 담당하는 곳이 바로 Domain 레이어이다.

 

지금까지 좋은 구조를 설계하기 위한 큰 개념을 알아보았다.

이제 3개의 레이어를 하나하나 자세하게 살펴볼 시간이다.

오늘은 UI 레이어를 배워보자. 시작!

 

2. UI 레이어가 하는 일

아키텍처 구조와 UI 레이어

 

UI의 역할은 앱 데이터를 화면에 보여주고 사용자 상호 작용의 주요 지점 역할을 하는 것이다.

데이터가 변경될 때마다 "어! 데이터 바뀌었다!"하고

해당 변경 사항을 업데이트하고 반영하는 것이 UI 레이어의 역할이다.

 

UI 레이어는 버튼을 누른다거나, 네트워크로부터 응답이 도착했다거나 등

상태 변화에 따라 변경 사항을 업데이트한다.

 

 

UI 레이어 파이프라인

 

그럼 UI 레이어가 시간에 따라 어떤 단계를 거쳐 무슨 일을 하는지 한 번 알아보자.

 

1. 우선, 앱 데이터를 UI 데이터로 변환한다.
보통 Data 레이어에서 가져오는 데이터는 UI 레이어에서 표시해야 하는 정보랑 다른 형식이다.
예를 들어, student라는 데이터가 있으면 거기서 이름만 필요할 수도 있고
아니면 성적이라는 데이터 소스와 병합하여 보여줘야 할 수도 있고 말이다.
즉, UI가 필요로 하는 데이터로 변환하는 일을 한다.

2. UI 데이터를 UI 요소로 업데이트한다.
번역이 좀 어렵게 되었는데, 데이터를 이용해 뷰를 그린다고 생각하면 된다.
예를 들어 학생 이름을 텍스트뷰에 넣는 것처럼 말이다.

3. 사용자 입력 이벤트를 처리하여 UI를 변경시킨다.
예를 들어 버튼이 클릭되면 토스트 메시지를 띄우는 행위가 여기에 해당한다.

4. 위 행위를 반복한다.

마치 뭐 대단한 것 마냥 써놨지만 사실 이미 다 알고 있는 내용이다.

데이터를 가공해서 뷰를 그리고 이벤트 처리를 하는 것.

이것이 UI 레이어가 일하는 흐름이다.

 

그렇다면 그런 UI 레이어를 구현하려면 어떻게 해야 할까?

다음 목차에서 4가지 방법에 대해 알아보자.

 

3. UI 레이어를 구현하는 방법

3-1. UI 상태를 정의하는 방법

UI 란...

 

UI Elements는 리스트 뷰나 이미지 뷰 등 UI 요소들을 뜻하고

UI State는 기사 목록이나 북마크 여부 등 UI의 상태를 나타낸다.

이 두 가지가 합쳐져 UI가 완성된다.

 

data class NewsUiState(
    val isPremium: Boolean = false, // 유료 구독자만 볼 수 있는지의 여부를 나타내는 변수다. 의미는 크게 신경 안써도 됨
    val newsItems: List<NewsItemUiState> = listOf()
)

data class NewsItemUiState(
    val title: String,
    val body: String,
    val bookmarked: Boolean = false,
    ...
)

예를 들어 우리는 뉴스 UI 상태를 위와 같이 정의할 수 있다.

UI를 나타내기 위해 필요한 상태 값들을 모아 클래스로 정의하면 된다.

공식문서에서는 네이밍을 기능 + UiState라고 지을 것을 권장하고 있다.

 

이렇게 정의된 UI 상태는 UI에서 직접 수정할 수 없도록 해야 한다.

UI는 상태를 읽고 UI 요소(뷰)를 업데이트하는 한 가지 일에 집중할 수 있도록 구조를 짜야한다.

-> Activity나 Fragment에서 직접 상태 값을 변경하는 로직을 구현하지 말라는 뜻이다.

Activity나 Fragment는 값을 받아서 뷰에 할당하고 그리는 역할만 해야 한다.

만약 이 원칙을 위반하면 데이터 불일치와 미세한 버그가 발생하게 된다.

 

3-2. UI 상태의 생산을 관리하는 방법

바로 직전 내용에서 UI 상태는 UI에서 직접 수정하지 못하게 해야 한다고 했다.

그렇다면 UI가 수정하지 못하게 방지하려면 어떻게 해야 할까?

UI 상태 관리를 어떻게 해야 할까?

 

바로 ViewModel을 사용하면 된다.

state holder(상태 보유자)의 역할을 하는 ViewModel은

상태를 생성하는 작업을 하며, 생성 작업에 필요한 로직도 포함한다.

 

앱 아키텍처에서 단방향 데이터 흐름의 작동 방식을 보여주는 데이터그램

 

Data Layer에서 데이터를 가져오는 시점부터해서

UI 상태가 어떻게 관리되는지 그 순서를 파악해보자.

 

1. ViewModel이 UI에 사용될 상태를 보유하고 노출한다.
UI에 사용될 상태라는 것은 공지사항 목록이라든지, 북마크 여부라든지 등
화면에 보여주기 위한 데이터의 상태를 의미한다.
이 데이터는 ViewModel에 의해 UI 데이터로 변환된다.

2. UI가 ViewModel에 사용자 이벤트를 알린다.
버튼 클릭 같은 이벤트 말이다.

3. ViewModel이 사용자 작업을 처리하고 상태를 업데이트한다.
발생된 이벤트에 맞는 사용자가 원하는 작업을 처리하고 상태를 업데이트한다.
예를 들어, 새로고침을 누르면 리스트를 다시 가져와 업데이트하는 것처럼 말이다.

4. 업데이트된 상태가 렌더링 할 UI에 다시 제공된다.
예를 들어, 가져온 공지사항 목록을 리스트 뷰에 뿌려주는 것이다.

5. 반복

글을 잘 읽었다면 눈치챘겠지만 [목차 2-1]에 있는 UI 레이어 파이프라인 설명과 유사하다.

다만, ViewModel이라는 state holder를 명시하여 다시 설명한 것뿐이다.

 

또한, UI 상태 관리를 위해서는 ViewModel과 더불어 단방향 데이터 흐름(UDF)을 사용해야 한다.

그림 속 화살표가 한 방향으로만 흘러가듯 한 방향으로만 데이터가 흘러야 한다는 것이다.

UDF를 사용하면 다음과 데이터 일관성, 테스트 가능성, 유지 관리성을 챙길 수 있다.

 

3-3. UDF 원칙에 따라 관찰 가능한 데이터 유형으로 UI 상태를 노출하는 방법

[목차 2-1]에서 "어! 데이터 바뀌었다!"하고 해당 변경 사항을 업데이트해야 한다고 했다.

근데 어떻게 데이터가 바뀌었다는 것을 감지할까?

이때 사용되는 것이 LiveData 또는 StateFlow 같은 관찰 가능한 데이터 홀더 클래스이다.

두 라이브러리에 대해서는 포스팅 주제를 벗어나므로 자세하게 다루진 않으려고 한다.

 

class NewsViewModel(...) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
    val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()

    ...

}

다만, 위 패턴은 언급하고 넘어가면 좋을 것 같아서 코드를 가져왔다.

ViewModel에서 변경 가능한 스트림을 변경 불가능한 스트림으로 노출하는 패턴이다.

그렇게 하는 이유는? UI가 UI 상태를 직접 변경하지 못하도록 하기 위함이다.

 

3-4. 관찰 가능한 UI 상태를 소비하는 UI를 구현하는 방법

class NewsActivity : AppCompatActivity() {

    private val viewModel: NewsViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        ...

        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect {
                    // Update UI elements
                }
            }
        }
    }
}

관찰 가능한 데이터 홀더를 써보지 않았거나 (특히) Flow를 접해보지 않았다면 위 코드가 이해가 잘 안 갈 수도 있다.

위 코드는 ViewModel에서 노출하는 UI 상태를 소비하는 코드이다.

이번 포스팅 주제에서는... 코드를 이해하려고 하기보단 개념을 이해하면서 넘어가면 될 것 같다.

 

UI 레이어 공부하기 끄읏!

다음 포스팅에서는 Data Layer에 대해 알아볼 예정이다.

 


💡 느낀 점

  • 그동안 나는 UI 레이어를 UI 레이어답게 작성하지 못했던 것 같다.
    Fragment에서 상태 값을 변경하는 코드를 너무 많이 작성한 듯...
  • 공식 채널 유튜브 영상 보면서 정말 설명이 기깔나다는 생각이 들었다. 설명 맛집인 듯.
  • 무작정 따라 해 보면서 아키텍처 개념을 쌓아왔는데
    이제야 아 그래서 그랬구나!라는 게 하나씩 맞춰지는 느낌이다. 재밌다.

📘 참고한 자료


 

 

반응형

댓글