관리 메뉴

오늘도 더 나은 코드를 작성하였습니까?

Now In Android를 분석하자 (Core 모듈의 구조1) 본문

카테고리 없음

Now In Android를 분석하자 (Core 모듈의 구조1)

hik14 2025. 9. 30. 01:54

'Now in Android' 앱의 멀티모듈 구조는 구글의 공식 Android 앱 아키텍처 가이드를 따르고 있습니다. 이 가이드는 **클린 아키텍처(Clean Architecture)**의 원칙과 유사한 부분은 많지만, 완전히 동일하지는 않습니다.

주요 차이점은 다음과 같습니다:

  1. 도메인 계층(Domain Layer)의 의존성:
    • 클린 아키텍처에서는 도메인 계층(비즈니스 로직)이 다른 어떤 계층(데이터, UI 등)에도 의존하지 않고 가장 독립적이어야 합니다. (의존성 역전 원칙, DIP)
    • **구글 공식 가이드 (Now in Android가 따르는 구조)**에서는 도메인 계층이 선택 사항이며, 존재하더라도 일반적으로 데이터 계층에 의존합니다. (UI (선택적 도메인) 데이터 의존성 흐름)
  2. 도메인 계층의 필수 여부:
    • 구글은 대부분의 Android 앱이 클린 아키텍처의 복잡성을 필요로 할 만큼 복잡하지 않다고 보고 도메인 계층을 선택적으로 제시합니다.

'Now in Android'는 이러한 공식 가이드를 멀티모듈로 구현한 예시로서, 모듈화를 통해 책임 분리, 재사용성, 확장성 등의 이점을 얻고 있습니다. 이는 클린 아키텍처가 추구하는 많은 목표와 일치합니다.

따라서 'Now in Android'는 **"클린 아키텍처의 많은 이점을 수용하면서, Android 개발에 최적화된 구글의 권장 아키텍처를 멀티모듈로 구현한 프로젝트"**로 이해할 수 있습니다.

Core 모듈의 의존성 도식화

 

Level 0  Modules

 

 core:model

  • 비유: "앱의 명사 사전 (The App's Dictionary)"
  • 역할: 앱의 모든 데이터가 어떤 형태를 가져야 하는지, 즉 '무엇(What)'을 정의합니다. 앱 전체에서 사용되는 공유 데이터 구조(주로 data class)를 포함합니다. 이 모듈은 앱의 모든 계층이 소통하기 위한 공통 '언어'를 제공합니다.
  • 주요 내용물:
    • NewsResource.kt: 뉴스 기사 하나를 나타내는 데이터 클래스.
    • Topic.kt: 'Android Dev', 'Compose' 같은 주제를 나타내는 데이터 클래스.
    • Author.kt, Episode.kt 등 앱에서 다루는 모든 핵심 데이터 모델.
    • @Serializable 어노테이션: 네트워크(Ktor)나 DB(Room)에서 데이터를 변환할 수 있도록 kotlinx.serialization 라이브러리를 사용합니다.
  • 왜 레벨 0 인가?
    • 이 모듈은 오직 데이터의 '구조'만 정의합니다. 이 데이터가 어떻게 화면에 그려지는지(designsystem), 어디서 오는지(network, database), 또는 어떤 비즈니스 로직에 사용되는지(domain)에 대해 전혀 알지 못합니다. 가장 순수한 데이터 그 자체입니다.

core:designsystem

  • 비유: "앱의 브랜드 가이드 & 레고 기본 블록 (The App's Brand Guide & Basic Lego Bricks)"
  • 역할: 앱의 시각적 정체성, 즉 **'어떻게 보이는가(How it looks)'**를 정의합니다. 색상, 글꼴, 아이콘 등 디자인 시스템의 가장 기초적인 요소와, 이것들을 조합하여 만든 가장 단순하고 재사용 가능한 UI 컴포넌트를 제공합니다.
  • 주요 내용물:
    • NiaColor.kt: Material3 테마를 기반으로 한 앱의 공식 색상 팔레트.
    • NiaTypography.kt: 앱 전체에서 사용되는 글꼴 스타일.
    • NiaIcons.kt: 앱에서 사용하는 모든 아이콘 리소스.
    • NiaButton.kt, NiaTopicTag.kt, NiaFilterChip.kt: 디자인 시스템 규칙에 따라 만들어진, 상태를 갖지 않는(stateless) 가장 기본적인 컴포저블(Composable) UI 요소.
  • 왜 레벨 0 인가?
    • 이 모듈은 순수한 시각적 '재료'만 제공합니다. 이 버튼이 눌렸을 때 어떤 일이 일어나는지, 이 태그에 어떤 Topic 데이터가 연결되는지에 대해 전혀 알지 못합니다. 그저 주어진 값에 따라 어떻게 보일지만 결정합니다.

core:common

  • 비유: "만능 공구함 (The Universal Toolbox)"
  • 역할: 특정 도메인(뉴스, UI, 데이터 등)에 속하지 않는, 앱 전반에서 사용되는 공통 유틸리티와 순수 Kotlin 코드를 모아놓은 곳입니다.
  • 주요 내용물:
    • NiaDispatchers.kt: IO, Default 등 Coroutine Dispatcher를 제공하여 앱 전체의 스레드 관리를 일관되게 합니다.
    • Result.kt: 네트워크 요청 등의 결과를 Success 또는 Error로 감싸는 래퍼 클래스.
    • WhileSubscribed 동작 정의: StateFlow를 언제 시작하고 중지할지에 대한 정책을 정의합니다.
  • 왜 레벨 0 인가?
    • 이 모듈은 앱의 기능이나 데이터와는 완전히 무관한, 범용적인 '도구'들만 포함하고 있습니다. 어떤 모듈이든 필요하면 가져다 쓸 수 있는 독립적인 라이브러리 같은 역할을 합니다.

core:analytics

  • 비유: "사건 기록원 (The Event Scribe)"
  • 역할: 앱에서 발생하는 이벤트를 기록하는 방법을 추상화하여 제공합니다. 실제 분석 도구(Firebase Analytics)의 구체적인 구현을 숨기고, 앱의 나머지 부분에는 AnalyticsHelper라는 간단한 인터페이스만 노출합니다.
  • 주요 내용물:
    • AnalyticsHelper.kt: logEvent(event)라는 단 하나의 메서드를 가진 인터페이스.
    • AnalyticsEvent.kt: 분석 이벤트의 이름과 파라미터를 담는, 이 모듈만의 자체적인 데이터 클래스.
    • FirebaseAnalyticsHelper.kt: AnalyticsHelper 인터페이스의 실제 구현체.
  • 왜 레벨 0 인가?
    • 이 모듈은 분석이라는 **횡단 관심사(Cross-cutting concern)**를 완벽하게 캡슐화합니다. core:model의 NewsResource가 무엇인지조차 알 필요 없이, 자신만의 AnalyticsEvent 데이터 모델을 사용하여 독립적으로 동작합니다.