[Android] 클린 아키텍처(Clean Architecture)

2025. 8. 11. 22:10·Android

안드로이드 권장 아키텍처는 다음 링크를 참고하기!- - >  2025.07.31 - [안드로이드] - [Android] 안드로이드 권장 아키텍처

 

Clean Architeture의 개념

모든 프로그램을 개발할 때는 유지보수와 장기적인 안정적인 운영을 위해 설계의 견고함과 확장성을 고려해야 한다.
안드로이드 애플리케이션도 예외는 아니다.
이에 앱의 기능이 점점 확장되고 내부 로직이 변경되는 변화에 유연하게 대응하기 위한 클린 아키텍처가 안드로이드에 도입되었다.

 

클린 아키텍처의 핵심은 "관심사 분리"에 있다.

관심사가 분리되면 결합도가 낮아지고 확장이 쉬워지며, 테스트 또한 훨씬 수월해진다!

 

이제 클린 아키텍처 다이어그램을 통해 각 책임이 어떻게 구분되는지 살펴보자.

[Fit.1] 클린 아키텍처 다이어그램

 

 

각 구성 요소는 원의 외곽에서 중심으로 의존성을 가진다.

즉 바깥 계층은 안쪽 계층을 알 수 있지만, 안쪽 계층은 바깥 계층을 몰라야 한다.

이 원칙에 따라 Presentation → Domain ← Data 구조가 형성된다!

다시 말해 presentation 계층은 domain 계층에 의존성을 가지게 되며, data 계층 역시 domain 계층에 의존성을 가지게 된다.

 

이제 각 계층의 역할에 대해 알아보고, 어떠한 방식으로 상호작용하는지 차근차근~ 알아보자!


 

Presentation Layer

Presentation 계층은 화면에 데이터를 표시하는 계층으로,

사용자 이벤트가 발생하면 그에 따라 업데이트된 데이터가 화면에 반영되어야 한다.

 

[Fit.2] 안드로이드 프레임워크

 

 

위 사진을 보면 Activity, Fragment, View와 ViewModel로 나뉘어져 있다.

  • Activity, Fragment, View는 화면에 데이터를 나타내는 부분이다.
  • ViewModel은 화면 업데이트와 관련된 로직을 구현하며, UseCase을 이용한다.

ViewModel에서 UseCase를 활용하는 코드 예시이다.

class NewsViewModel(
	private val getNewsUseCase : GetNewsUseCase
) : ViewModel() {

    private val _uiState = MutableStateFlow(NewsUiState())
	val uiState: StateFlow<NewsUiState> = _uiState
    
    fun getNews() {
    	viewModelScope.launch {
        	val result = getNewsUseCase()
            ...
        }
    }
}

 

ViewModel에서 활용할 UseCase를 주입받아 호출하고, 그 결과(result)를 UI 상태에 반영하여 화면을 갱신한다.

이쯤되면 UseCase는 어떤 역할을 할지 궁금해질 시점이다!!

바로 Domain Layer에 대해 알아보자. 😇

 


 

Domain Layer

Domain Layer는 어플리케이션의 비즈니스 로직을 담당하는 계층이다.

설명하였듯이, 해당 계층은 Presentation 계층과 Data 계층에 모두 의존하지 않고 독립적으로 동작한다.

때문에 재사용과 테스트가 용이한 계층이기도 하다.

 

[Fit.2]의 안드로이드 프레임워크에서 Domain Layer는 UseCase와 Entity 뿐만 아니라, Repository 인터페이스도 함께 포함된다.

이러한 구조 덕분에 "의존성 역전 원칙"이 적용되며, Domain Layer는 외부 계층에 영향 받지 않게 된다.

이 부분은 Data Layer 설명에서 더 명확하게 이해할 수 있을 것이다.

 

먼저 Domain Layer의 코드 예시이다.

class GetLatestNewsWithAuthorsUseCase(
  private val newsRepository: NewsRepository
  , private val authorsRepository: AuthorsRepository
) {
	suspend operator fun invoke(): NewsWithAuthor {
    	val latestNews = newsRepository.getLatestNews()
        
        val author = authorRepository.getAuthorById(latestNews.authorId)
        
        return NewsWithAuthor (
        	news = latestNews
            , author = author
        )
    }
}

 

위 UseCase는 최신 뉴스와 기자의 정보를 함께 조회하는 비즈니스 로직이다.

이 과정에서 Repository를 참조하며, 각각의 Repository에 정의된 함수를 호출해 뉴스와 기자 정보를 개별적으로 가져온다.

같은 계층의 Repository 인터페이스를 살펴보자.

interface NewsRepository {
	suspend fun getLatestNews(): News
}

interface AuthorsRepository {
    suspend fun getAuthorById(id: String): Author
}

 

Domain Layer에서 위와 같은 인터페이스는 선언만 하고 구현하지 않는다.

그 구현은 이어서 설명할 Data Layer에서 담당한다!

 

추가로 Entity는 주로 Data Class 로 정의되며,

UseCase 내에서 Repository를 통해 가져온 데이터를 담아서 활용하는 Domain 클래스이다.

코드 예시는 다음과 같다.

data class News(
    val id: String,
    val title: String,
    val authorId: String
)

 


 

Data Layer

Data Layer은 로컬 DB, 네트워크, 캐시 등 다양한 데이터 소스와의 통신을 담당하는 계층이다.

Data Layer은 크게 RepositoryImpl, DataSource, Mapper로 구분된다.

 

RepositoryImpl은 Domain Layer에서 정의된 인터페이스의 구현체이다.

class NewsRepositoryImpl(
    private val remoteDataSource: NewsRemoteDataSource
    , private val localDataSource: NewsLocalDataSource
) : NewsRepository {

    override suspend fun getLatestNews(): NewsResponseDto {
        return try {
            val remoteNews = remoteDataSource.getLatestNews()
            localDataSource.saveNews(remoteNews)
            remoteNews
        } catch (e: Exception) {
            localDataSource.getSavedNews() ?: throw IllegalStateException("No news available")
        }
    }
}

 

두 DataSource를 조합하여 네트워크에서 데이터를 가져와 로컬 데이터에 저장하는 코드이다.

Remote 방식은 네트워크, 즉 api 호출을 통해 외부 DB에서 데이터를 가져오는 것이고,

Local 방식은 안드로이드 내부의 DB인 Room과 같은 로컬 DB에서 데이터를 가져오는 것이다.

 

또한 DataSource도 인터페이스와 그 구현체로 구성되어 있다.

이 역시 Repository가 DataSource의 구현 방식에 대해 알 필요가 없기 때문이다.

interface NewsRemoteDataSource {
    suspend fun getLatestNews(): NewsResponseDto
}

class NewsRemoteDataSourceImpl(
    private val apiService: NewsApiService
) : NewsRemoteDataSource {
    override suspend fun getLatestNews(): NewsResponseDto {
        return apiService.fetchLatestNews()
    }
}

 

외부 API(Retrofit)와 통신하여 데이터를 가져오는 코드이다.

내부 데이터를 가져올 때도 Room을 이용해 비슷한 방식으로 DataSource를 구현할 수 있다.

 

마지막으로 Mapper는 데이터를 Entity로 매핑하는 클래스이다.

Domain Layer에서 비즈니스 로직에 맞는 데이터만 전달하려면, 즉 Entity만 전달하려면

Data Layer에서는 받아온 데이터를 적절히 필터링해야 한다.

우선, Mapper 클래스를 만들어보자.

data class NewsResponseDto(
    val id: String
    , val title: String
    , val authorId: String
    , val publishedAt: String
    , val url: String
) {
    fun toEntity(): News {
        return News(
            id = id
            , title = title
            , authorId = authorId
        )
    }
}

 

응답으로 받아온 데이터 중 비즈니스에 필요한 로직만 Entity로 캡슐화하는 toEntity() 함수를 만들었다.

이것을 RepositoryImpl 내 함수에서 return 하기 전에 호출하여 변환하면 된다!

 

 


 

지금까지 안드로이드의 Clean Architecture에 대해서 알아보았다.

클린 아키텍처의 핵심은 관심사를 분리하여 각 계층의 책임을 명확히 하는 데 있다.

 

중복된 비즈니스 로직이 많을수록, 그리고 프로젝트의 규모가 커질수록 해당 아키텍처를 사용하면

코드의 재사용성과 유지보수성이 크게 향상될 것이다.

 

하지만 그 반대의 경우라면, 복잡한 계층 구조를 전부 거쳐야 하는지 의문이 든다.
따라서 상황에 맞게 아키텍처의 계층도 적절히 조정하고, 필요에 따라 단순화할 필요가 있다!

 

:)

 

출처

https://bj-turtle.tistory.com/109

https://jminie.tistory.com/196

https://superohinsung.tistory.com/74#%ED%81%B4%EB%A6%B0%20%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%A5%BC%20%EC%99%9C%20%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C%3F-1

'Android' 카테고리의 다른 글

[Android] Hilt- 프로젝트 세팅, Hilt 기본 흐름 (2)  (5) 2025.08.15
[Android] Hilt- Hilt를 사용하는 이유 (1)  (1) 2025.08.12
[Android] Work Manager(+Job Scheduler)  (2) 2025.08.09
[Android] 안드로이드 권장 아키텍처  (4) 2025.07.31
[Android] Fragment의 데이터 통신  (1) 2025.06.16
'Android' 카테고리의 다른 글
  • [Android] Hilt- 프로젝트 세팅, Hilt 기본 흐름 (2)
  • [Android] Hilt- Hilt를 사용하는 이유 (1)
  • [Android] Work Manager(+Job Scheduler)
  • [Android] 안드로이드 권장 아키텍처
jjangsudiary
jjangsudiary
jjangsudiary 님의 블로그 입니다.
  • jjangsudiary
    jjangsudiary 님의 블로그
    jjangsudiary
  • 전체
    오늘
    어제
    • 분류 전체보기 (81) N
      • 이모저모 (0)
        • 회고 (0)
      • Development (17) N
        • 개발 공부 (14) N
        • 프로젝트 (2)
      • Android (10)
        • Compose (1)
      • AI (15)
      • Computer Science (25)
        • 네트워크 (8)
        • 데이터베이스 (10)
        • 운영체제 (6)
        • 자료구조 (0)
        • 컴퓨터구조 (1)
      • Java (9)
        • 디자인패턴 (2)
      • Spring (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    안드로이드
    database
    인공지능
    자바
    CS
    db
    Ai
    Python
    딥러닝
    운영체제
    머신러닝
    android
    파이썬
    baekjoon
    os
    백준
    코딩 테스트
    TensorFlow
    프로그래머스
    java
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
jjangsudiary
[Android] 클린 아키텍처(Clean Architecture)
상단으로

티스토리툴바