안드로이드 클린 아키텍처 정리

안드로이드 클린 아키텍처 정리

안드로이드 클린 아키텍처 정리

클린 아키텍처란?

최근에 클린 아키텍처를 안드로이드 프로젝트에 적용해보고 있는데, 처음에는 계층이 왜 이렇게 복잡한지 이해가 안 됐다. 하지만 막상 구조를 나눠서 코드를 작성해보니 “UI와 로직, 데이터가 뒤엉키지 않는다”는 게 이렇게 큰 장점일 줄은 몰랐다.

기본적으로 계층은 세 가지로 나뉜다:

  • presentation: UI, ViewModel
  • domain: UseCase, Entity, Repository Interface
  • data: API/DB, Repository 구현체

모듈 구조는 이렇게 나눴다:

/app     ← presentation
/domain  ← domain
/data    ← data
  

의존성 흐름은 단방향

app → domain
     ↘
      data → domain
  

이 방향성이 무너지면 프로젝트가 산으로 간다…!!!!!!!

Gradle 설정 예시

settings.gradle.kts

include(":app", ":domain", ":data")

app/build.gradle.kts

dependencies {
    implementation(project(":domain"))
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
    implementation("androidx.compose.ui:ui:1.6.0")
}

domain/build.gradle.kts

plugins {
    id("org.jetbrains.kotlin.jvm")
}

data/build.gradle.kts

dependencies {
    implementation(project(":domain"))
    implementation("com.squareup.retrofit2:retrofit:2.9.0")
    implementation("androidx.room:room-runtime:2.6.1")
}

라이브러리 위치 정리

라이브러리위치
UI (Compose 등)app
비즈니스 로직domain
Retrofit, Room 등data

계층별 예시 (User 정보 불러오기)

“UseCase는 왜 굳이 따로 둬야 하지?” 싶었는데 테스트나 재사용성을 생각하면 결국 분리하는 게 낫더라.

  • UserRepository: domain에서 인터페이스
  • UserRepositoryImpl: data에서 API/DB 로직
  • GetUserUseCase: domain에서 로직 처리
  • UserViewModel: presentation에서 상태 처리

Fallback 이란?

API가 터졌을 때 Room에서 데이터를 보여주는 전략. 백그라운드 안전장치 같은 개념이다.

try {
    val user = api.getUser()
} catch (e: Exception) {
    val fallback = dao.getUser()
}

Test Mock 이란?

실제 서버 없이 로직이 잘 도는지 확인하고 싶을 때 가짜 구현(Fake)을 만들어서 테스트하는 방식.

class FakeUserRepository : UserRepository {
    override suspend fun getUser(userId: String): User = User("1", "테스트용")
}

마치며

지금은 이 구조에 적응했지만, 처음엔 “이게 진짜 실무에 맞나?” 싶었다. 그런데 앱이 점점 커지고 기능이 늘어나면서 이 구조 없이는 협업도 테스트도 어렵다는 걸 실감하게 됐다. 그래서 요즘은 개인 프로젝트든 팀 프로젝트든 습관처럼 클린 아키텍처를 도입하고 있다.

Leave a Comment