이동식 저장소

Android Architecture Layers - 4. UI events 본문

Primary/Android

Android Architecture Layers - 4. UI events

해스끼 2022. 6. 27. 12:05

다음 문서를 요약한 글입니다. 원문으로 일독을 권합니다.

 

UI 이벤트  |  Android 개발자  |  Android Developers

UI 이벤트 UI 이벤트는 UI 레이어에서 UI 또는 ViewModel로 처리해야 하는 작업입니다. 가장 일반적인 이벤트 유형은 사용자 이벤트입니다. 사용자는 화면 탭하기 또는 동작 생성과 같은 앱과의 상호

developer.android.com


UI 이벤트는 UI 레이어에서 처리되어야 하는 이벤트이다. UI가 아닌 UI 레이어임에 주의하자. 가장 대표적인 예시로는 사용자가 앱을 사용하면서 발생시키는 사용자 이벤트가 있다. UI는 ``onClick()`` 등의 콜백을 이용하여 사용자 이벤트를 처리한다. 

 

사용자 이벤트를 처리할 때 UI 로직이 필요한 경우도 있다. 다른 화면으로 navigate하거나 ``SnackBar``를 보여주는 행위 등이 UI 로직이다. 비즈니스 로직은 다른 플랫폼에서도 동일하게 적용될 수 있지만, UI 로직은 플랫폼마다 구현되는 방법이 다르다.

 

비즈니스 로직은 최대한 UI에 독립적이므로 작성되어야 하고, UI 로직은 철저히 UI의 행위만을 처리해야 한다. 물론 UI 이벤트를 처리할 때 비즈니스 로직과 UI 로직 둘 다 필요할 수도 있다. 둘은 서로 양립 가능하지만, 서로 다른 영역을 담당한다는 점을 기억하자.

UI 이벤트 처리방법

이벤트 처리법을 결정할 때는 다음의 트리를 참고하면 좋다.

ViewModel에서 발생한 이벤트의 예시로는 하위 레이어에서 새로운 데이터가 주어진 경우가 있다. 새로운 데이터가 주어졌으므로 ViewModel은 새로운 state 객체를 만들어야 한다.

 

호출 장소에 상관없이 비즈니스 로직은 ViewModel에 선언되어야 한다. UI에서는 ViewModel 전체를 참조할 필요는 없고, 콜백으로 전달받아 호출하면 좋다. ViewModel을 구현할 때는 UI state가 어떻게 바뀌는지만 고려하고, state가 어떻게 UI로 표현될지는 신경쓰지 말자.

ViewModel의 처리 예시

ViewModel에서 발생한 이벤트는 반드시 state를 업데이트해야 한다. 직접 UI를 조작하려 해서는 안 된다.

 

UI 이벤트를 분리한 예시. ``LoginScreen``의 ``onUserLogIn()``은 상위 composable(아마도 navigation)에서 넘겨줘야 한다.

data class LoginUiState(
    val isLoading: Boolean = false,
    val errorMessage: String? = null,
    val isUserLoggedIn: Boolean = false
)

class LoginViewModel : ViewModel() {
    var uiState by mutableStateOf(LoginUiState())
        private set
    /* ... */
}

@Composable
fun LoginScreen(
    viewModel: LoginViewModel = viewModel(),
    onUserLogIn: () -> Unit
) {
    val currentOnUserLogIn by rememberUpdatedState(onUserLogIn)

    // Whenever the uiState changes, check if the user is logged in.
    LaunchedEffect(viewModel.uiState)  {
        if (viewModel.uiState.isUserLoggedIn) {
            currentOnUserLogIn()
        }
    }

    // Rest of the UI for the login screen.
}

이벤트를 소비한 결과 state가 바뀌어야 한다면, ViewModel의 함수를 호출하자.

@Composable
fun LatestNewsScreen(
    snackbarHostState: SnackbarHostState,
    viewModel: LatestNewsViewModel = viewModel(),
) {
    // Rest of the UI content.

    // If there are user messages to show on the screen, 
    // show it and notify the ViewModel.
    viewModel.uiState.userMessage?.let { userMessage ->
        LaunchedEffect(userMessage) {
            snackbarHostState.showSnackbar(userMessage)
            // Once the message is displayed and dismissed, notify the ViewModel.
            viewModel.userMessageShown()
        }
    }
}

UI 이벤트가 복잡하다면

UI 이벤트가 UI state만으로 처리하기 어렵다면, 다음을 고려해 보자.

  • 클래스는 자신의 책임만을 수행해야 한다. UI는 navigation, 클릭 이벤트 등 화면과 관련된 로직만을 처리해야 한다. VieWModel은 비즈니스 로직을 수행하며, 하위 레이어가 반환한 결과를 UI state로 변환해야 한다.
  • 이벤트가 어디서 발생하는지 생각하자. 위에 첨부한 decision tree를 참고하면 좋다. 예를 들어 UI에서 발생하는 이벤트의 결과로 navigation 이벤트가 발생한다면, 이 이벤트는 UI에서 처리돼야 한다. 물론 ViewModel에서 일부 로직을 처리해야 할 수도 있지만, 이것은 UI 이벤트이므로 UI가 책임지고 처리해야 한다.
  • 변경된 state가 언제 처리돼야 하는지 생각하자. 특정 상황에서는 state를 처리하지 않아야 할 수도 있다. 예를 들어 앱이 백그라운드에 있을 때 ``Toast``를 보여주고 싶지 않을 수도 있다. 이런 경우에는 앱이 화면에 띄워져 있을 때에만 이벤트를 처리하도록 수정해 보자.

정리하면, UI 이벤트는 UI 또는 ViewModel (혹은 둘 다)에서 처리되어야 한다. UI는 UI 로직을 통해, ViewModel은 비즈니스 로직을 통해 이벤트를 처리한다.

 

UI 로직은 state에 따른 UI의 반응이다. ``Toast`` 메시지를 보여준다던가, 다른 UI로 navigate하는 코드 등이 포함된다. UI 로직은 전적으로 UI에서 선언되고 처리되어야 한다. 필요하다면 별도의 객체에 UI 로직을 정의할 수도 있다. 단, 이 객체는 UI에서 선언되어야 한다. 

 

UI 이벤트를 처리할 때 적절한 UI 함수를 호출하면 좋다. Compose의 ``LaunchedEffect``라던가..

 

ViewModel은 비즈니스 로직을 담고 있다. 하위 레이어에서 받은 데이터를 변환하거나, 사용자 이벤트를 받아 state를 업데이트하는 로직이 포함된다. UI에서 비즈니스 로직이 필요하다면 ViewModel의 함수를 호출해야 한다.

 

비즈니스 로직은 UI에 독립적으로 작성되어야 한다. 

'Primary > Android' 카테고리의 다른 글

[Android] proguard-rules.pro는 무엇인가  (0) 2022.07.07
Hilt Test Principles  (0) 2022.06.30
Android Architecture Layers - 3. UI  (0) 2022.06.23
Android Architecture Layers - 2. Domain  (0) 2022.06.18
Android Architecture Layers - 1. Data  (0) 2022.06.15
Comments