영어 데일리

MVI 보일러플레이트는 이제 작별, 코드를 대신 작성해주는 BuildKt MVI 라이브러리 소개

현욱 정리장 2026. 1. 6. 11:15

Jetpack Compose 로 개발하는 안드로이드 개발자라면, 

MVI (Model-View-Intent) 가 정답이라는 말을 한번씩은 들어봤을 것입니다. 

실제로도 종종 그렇죠

 

단방향 데이터 흐름과 명확한 책임 분리는 앱을 예측가능하게하고,

테스트 하기 쉽고, 

디버깅하기 편합니다. 

하지만 솔직히 말해봅시다.

우리는 하나의 화면을 만들때마다 똑같고 반복적이며 영혼을 갈아넣는 보일러 플레이트를 계속 작성하고 있습니다.

MVI 화면 하나 만들 때 보통 이런걸 매번 직접한다

  • state와 model을 보관하기 위한  ViewModel을 생성한다
  • 네비게이션 인자를 처리하기 위해 ViewModelProvider.Factory 를 작성한다.
  • UI 상태를 관리하기 위한 StateFlow를 직접생성한다. 
  • 한 번만 발생하는 Ui 이벤트(토스트, 네비게이션 등)를 위해 SharedFlow 또는 채널을 직접구성한다.
  • 화면 이동을 위하 NavGraphBuilder 확장 함수를 작성한다.
  • SavedStateHandle에서 네비게이션 인자를 하나하나 신경 써서 직접 꺼낸다. 

우리는 이런 작업들을 계속해서 반복합니다.

이 과정은 지루하고 ,실수하기 쉽고, 우리가 정말로 만들고싶은 것 훌륭한 기능으로부터 우리의 집중력을 빼았습니다.

저는 이런 반복적인 사이클에 지쳐버렸습니다. 그래서 이렇게 생각했습니다

"더 나은 방법이 분명히 있을텐데"

그리고, 오늘 이러한 보일러 플레이트를 거의 전부 제거해주는 

Jetpack compose를 위한 어노테이션 기반 MVI framework인 BuildKT MVI의 오픈소스 공개를 기쁘게 소개합니다. 

 

BuildKt MVI란 무엇인가요? 

BuildKt MVI는 가볍지만 강력한 프레임워크로 

KSP (Kotlin symbol PRocessing)을 사용해 각 화면에 필요한 전체 MVI 스캐폴딩을 컴파일 타임에 자동 생성합니다.

개발자의 역할은 비즈니스 로직과 UI에 집중하는 것입니다.

나머지는 프레임워크가 대신 처리합니다.

이것이 실제로 무엇을 의미하는지, 이제 직접 살펴보겠습니다.

 

아하 모먼트: Before & After

간단한 프로필을 만든다고 상상해봅시다.

BuildKt MVI를 사용하지 않는다면, 

단 하나의 화면을 위해서도 파일 구조는 다음과 같을 것입니다.

❌ ProfilePane.kt // Your UI
❌ ProfileViewModel.kt // The ViewModel
❌ ProfileViewModelFactory.kt // The factory for the ViewModel 
❌ ProfileNavGraph.kt // The NavGraphBuilder extension

실제로 기능을 구현하기도전에, 

이 파일들 안에서 각종 플러밍 코드를 작성하는 데 상당한 시간을 쓰게 됩니다.

하지만 BuildKt MVI 를 사용하면 필요한 것이 이것이 전부입니다.

✅ ProfilePane.kt             // Your UI, annotated
✨ The rest is auto-generated!

파일 하나를 작성하고, 어노테이션 하나만 추가하면 

프레임워크가 컴파일 타임에 VIewModel, Factory, NavGraphBuilder 확장 함수까지 전부 생성해줍니다.

 

어떻게 동작하나요? 3단계 가이드 

5분 안에 그 프로필 화면을 만들어봅시다.

 

1. State와 Intent를 정의합니다.

이 단계는 순수한 비즈니스 로직입니다.

화면에 어떤 데이터가 필요하고, 사용자가 어떤 행동을 할 수 있는지를 정의합니다. 

 

data class ProfileUiState(
    val isLoading: Boolean = true,
    val userName: String = ""
)

sealed interface ProfileIntent {
    @TriggersSideEffect
    data object LoadUserName : ProfileIntent

    data class OnUserNameLoaded(val name: String) : ProfileIntent
}

여기서 @TriggersSideEffect 어노테이션에 주목하세요.

이 어노테이션은 LoadUserName이 API 호출과 같은 비동기 작업을 포함한 다는 것을 프레임워크에 알려줍니다.

 

2. Composable을 만들고 어노테이션을 추가합니다. 

평소처럼 Composable 을 작성하되

@MviScreen 어노테이션만 추가하면됩니다. 

이 어노테이션이 모든 코드 생성의 트리거가 됩니다.

@MviScreen(
    uiState = ProfileUiState::class,
    intent = ProfileIntent::class
)
@Composable
fun ProfilePane(
    state: ProfileUiState,
    onIntent: (ProfileIntent) -> Unit,
) {
    if (state.isLoading) {
        CircularProgressIndicator()
    } else {
        Text("Hello, ${state.userName}!")
    }
}


프로세서는 state 파라메터와 onIntent 콜백이 존재할 것을 기대하며, 

이를 기반으로 전체 구조를 자동으로 연결합니다. 

이게 전부입니다.

 

3. NavHost에서 로직을 구현합니다. 

BuildKt MVI는 타입 세이프하고, DSL 기반의 NavGraphBuilder 확장 함수를 자동으로 생성해줍니다.

개발자는 빈칸만 채우면됩니다.

 

Reducer를 살펴보면, 

이것은 설계상 순수 함수이며,오직 상태 전이 만 책임집니다. 

NavHost(navController, startDestination = "profilePane") {

    // KSP로 생성된 `profilePane` 함수
    profilePane(navController = navController) {

        // 상태를 업데이트하는 순수 함수 Reducer
        reducer = { state, intent ->
            when (intent) {
                is ProfileIntent.OnUserNameLoaded -> {
                    state.copy(isLoading = false, userName = intent.name)
                }
                else -> state
            }
        }

        // SideEffect는 모든 비동기 작업을 처리
        sideEffects {
            loadUserName = sideEffect {
                // API 호출 시뮬레이션
                delay(2000)
                // 결과를 담은 새로운 Intent 반환
                newIntent(ProfileIntent.OnUserNameLoaded("Matias"))
            }
        }
    }
}

 

이제 끝입니다!

수동 보일러 플레이트 없이 완전히 동작하고 테스트 가능한 MVI 화면이 완성되었습니다.

 

네비게이션 인자가 필요하신가요?

Composable에 @NavArgument가 붙은 파라메터만 추가하면됩니다.

나머지는 자동으로 처리됩니다. 

 

왜 BuildKt MVI 인가? 

  • 중요한 것에 집중하세요: 배관 코드를 작성하는대신 기능 구현에 집중할 수 있습니다.
  • 보일러플레이트 대폭감소: 단 하나의 어노테이션이 수백 줄에 코드를 대체합니다.
  • 설계 단계부터 타입 세이프: Reducer와 SideEffect를 위한 DSL이 자동 생성되어, 런타임 오류를 방지합니다.
  • 현대적인 안드로이드를 위하 설계됨: KSP와 jetpack Compose를 전제로 설계되었습니다.
  • 확장성과 테스트 용이성: 유지보수와 테스트가 쉬운, 깔끔하고 분리된 아키텍처를 지향합니다.

 

지금 바로 시작해보세요!

이 프로젝트는 실제 프로덕션 환경에서 겪은 고통에서 출발했으며,

오픈된 형태로 계속해서 발전하고 있습니다.

여러분의 피드백, 이슈 제보, 아이디어 하나하나가 이 프로젝트의 미래를 직접 만듭니다.

 

BuildKtMVI는 오픈소스이며, Maven Central에서 바로 사용할 수 있습니다.

다음 프로젝트에서 직접 사용해보고, 문서를 읽어보며 얼마나 많은 시간을 절약할 수 있는지 직접 확인해보시길 권합니다.

 

Github에서 프로젝트를 확인해보시고 유용하다면 스탈르 눌러주세요. 

이슈를 열거나 기요도 언제든 환영입니다.