0Pricing
Android Academy · 강의

기능 모듈과 핵심 모듈

모듈 경계를 정합니다.

기능 모듈과 핵심 모듈은(는) CoddyKit의 무료 Android Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Android Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

두 가지 모듈

대부분의 모듈화된 Android 앱은 코드를 두 가지 주요 모듈로 구성합니다. 기능 모듈과 핵심 모듈이며, 그 위에 얇은 :app 모듈을 둡니다.

  • 기능 모듈은 앱에서 사용자에게 표시되는 한 부분(화면 또는 흐름)을 담습니다.
  • 핵심 모듈은 여러 기능에서 사용하는 공유 인프라를 담습니다.

이 레슨에서는 이러한 경계를 잘 설정하는 방법을 배웁니다.

기능 모듈의 구성

:feature:profile과 같은 기능 모듈에는 하나의 기능에 필요한 모든 것이 들어 있습니다. Compose 화면, ViewModel, UI 상태가 여기에 포함됩니다. 기능 모듈은 UI부터 뷰 모델까지 전체 세로 영역을 소유하는 수직적 구조입니다.

공유 요소는 핵심 모듈에 의존하지만 다른 기능 모듈에는 의존하지 않아야 합니다.

// feature/profile/ProfileScreen.kt
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = hiltViewModel()) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    when (state) {
        is ProfileUiState.Loading -> CircularProgressIndicator()
        is ProfileUiState.Success -> ProfileContent((state as ProfileUiState.Success).user)
        is ProfileUiState.Error -> ErrorMessage()
    }
}

기능 ViewModel

각 기능은 자체 ViewModel을 소유합니다. 핵심 모듈의 저장소를 통해 데이터를 가져오고 UI 상태를 노출합니다. 기능 모듈은 데이터가 어떻게 가져와지는지는 알지 못하고 저장소 계약만 알고 있습니다.

// feature/profile/ProfileViewModel.kt
@HiltViewModel
class ProfileViewModel @Inject constructor(
    private val userRepository: UserRepository // from :core:data
) : ViewModel() {
    val uiState: StateFlow<ProfileUiState> =
        userRepository.observeUser()
            .map { ProfileUiState.Success(it) }
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ProfileUiState.Loading)
}

핵심 모듈의 구성

핵심 모듈은 수평적 구조로, 여러 기능에서 사용하는 하나의 기능을 제공합니다. 일반적인 핵심 모듈은 다음과 같습니다.

  • :core:model — 모든 곳에서 공유하는 일반 데이터 클래스
  • :core:network — Retrofit/Ktor 클라이언트
  • :core:database — Room 설정
  • :core:data — 네트워크와 데이터베이스를 결합하는 저장소
  • :core:designsystem — 테마와 재사용 가능한 컴포저블
// core/model/User.kt
data class User(
    val id: String,
    val name: String,
    val avatarUrl: String
)

디자인 시스템 모듈

:core:designsystem은 가장 많이 재사용되는 모듈 중 하나입니다. MaterialTheme, 색상 체계, 타이포그래피, 버튼과 카드 같은 재사용 가능한 컴포저블을 담습니다. 모든 기능이 코드를 복사하지 않고도 동일한 디자인을 적용할 수 있습니다.

// core/designsystem/AppTheme.kt
@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) DarkColors else LightColors
    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        content = content
    )
}

데이터 모듈이 저장소를 소유합니다

:core:data 모듈은 기능 모듈이 의존하는 저장소 인터페이스를 노출하면서 구현은 숨깁니다. 일반적으로 :core:network와 :core:database에 의존하며, 이 둘을 결합하여 단일 진실 공급원을 제공합니다.

// core/data/UserRepository.kt
interface UserRepository {
    fun observeUser(): Flow<User>
    suspend fun refresh()
}

// core/data/OfflineFirstUserRepository.kt
internal class OfflineFirstUserRepository @Inject constructor(
    private val api: UserApi,        // :core:network
    private val dao: UserDao         // :core:database
) : UserRepository {
    override fun observeUser(): Flow<User> = dao.observe().map { it.toUser() }
    override suspend fun refresh() { dao.upsert(api.fetch().toEntity()) }
}

model과 designsystem을 가볍게 유지하기

가장 낮은 수준의 핵심 모듈은 가능한 한 적은 항목에 의존해야 합니다. :core:model에는 이상적으로 Android 의존성이 전혀 없어야 하며 일반 Kotlin 데이터 클래스만 포함해야 합니다. 이렇게 하면 어디서나 사용할 수 있고 빌드도 빠릅니다.

:core:model이 Retrofit이나 Room에 의존하기 시작하면 데이터 클래스를 사용하는 모든 모듈에 이 무거운 라이브러리들이 함께 포함됩니다.

// core/model/build.gradle.kts
plugins {
    id("myapp.jvm.library") // pure Kotlin, no Android
}
// No Retrofit, no Room, no Compose here — just data classes

얇은 :app 모듈

:app 모듈은 조립자입니다. 이곳에는 로직을 아주 조금만 포함해야 합니다. 구체적으로는 Application 클래스, 하나의 MainActivity, 최상위 NavHost, 그리고 의존성 주입 연결 코드만 포함합니다. 실제 화면은 모두 기능 모듈에 배치합니다.

앱 모듈이 얇으면 대부분의 변경이 기능 모듈에서 발생하므로 앱 모듈을 다시 빌드하는 일이 드뭅니다.

// app/MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AppTheme {            // from :core:designsystem
                AppNavHost()      // routes into :feature:* screens
            }
        }
    }
}

좋은 경계 만들기

무엇을 모듈로 만들지 어떻게 결정할까요? 다음과 같은 실용적인 기준을 사용할 수 있습니다.

  • 기능 = 사용자가 이름을 붙여 말할 수 있는 화면 또는 흐름입니다(Home, Profile, Checkout).
  • 핵심 모듈 = 두 개 이상의 기능에서 재사용하는 기능입니다.
  • 두 기능이 같은 코드를 모두 필요로 한다면 해당 코드를 핵심 모듈로 내립니다.
  • 한 모듈이 서로 관련 없는 일을 너무 많이 한다면 분리합니다.

선택 사항: api와 impl 분리

대규모 앱에서는 기능을 공개 :feature:profile:api(인터페이스, 탐색 경로)와 비공개 :feature:profile:impl(화면, 뷰 모델)로 나누기도 합니다. 다른 기능은 작은 api에만 의존하고 구현에는 의존하지 않습니다.

이는 고급 기법입니다. 대부분의 앱에서는 기능마다 모듈 하나면 충분합니다. 규모가 매우 큰 코드베이스에 사용할 수 있는 패턴이 있다는 점만 알아 두시면 됩니다.

// Other features see only the contract, not the screens
// feature/home depends on :feature:profile:api
interface ProfileEntry {
    val route: String
    fun NavGraphBuilder.register(navController: NavController)
}

그래프 구성하기

다음은 예제의 계층이 연결되는 방식입니다. 의존성이 항상 아래 방향으로만 향한다는 점에 주목하세요. app -> feature -> data -> network/database -> model

// :app           -> :feature:home, :feature:profile
// :feature:home  -> :core:data, :core:designsystem
// :feature:profile -> :core:data, :core:designsystem
// :core:data     -> :core:network, :core:database, :core:model
// :core:network  -> :core:model
// :core:database -> :core:model
// :core:model    -> (nothing)

빠른 확인

:feature:home과 :feature:profile 모듈 모두 사용자 데이터를 가져오고 캐시해야 합니다. 이 저장소 코드는 어디에 배치해야 할까요?

복습: 기능 및 핵심 모듈

코드를 두 계층으로 나누는 방법을 배웠습니다.

  • 기능 모듈은 세로 단위의 기능 조각입니다(화면 + ViewModel + UI 상태).
  • 핵심 모듈은 가로 방향으로 공유되는 기능입니다(model, network, database, data, designsystem).
  • :app 모듈은 얇게 유지하고 기능을 조립하기만 합니다.
  • 공유 코드는 핵심 모듈로 내리고, :core:model은 Android와 무관하고 가볍게 유지합니다.

다음에는 이러한 모듈 사이의 의존성을 관리하고 그래프를 깔끔하게 유지하는 방법을 알아봅니다.

자주 묻는 질문

“기능 모듈과 핵심 모듈” 강의는 무료인가요?

네 — “기능 모듈과 핵심 모듈” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Android Academy 강의 전체를 잠금 해제할 수 있습니다. Android Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“기능 모듈과 핵심 모듈”에서 뭘 배우나요?

모듈 경계를 정합니다. 브라우저에서 직접 실행하는 실습 코드로 Android Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Android Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Android Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“기능 모듈과 핵심 모듈” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Android Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Android Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 모듈화가 필요한 이유
  2. 기능 모듈과 핵심 모듈
  3. 모듈 종속성 관리
  4. 모듈 간 탐색
← Android Academy(으)로 돌아가기