본문 바로가기
Android/Architecture

안드로이드 Clean Architecture - 예제 (Rxjava)

by 태크민 2023. 5. 27.

모듈 생성부터가 시작이다.

이러한 방식으로 presentation, domain, data layer에 대한 모듈을 생성하자.

물론, 기존에 생성되어있던 app 모듈을 제거해도 상관 없고 위의 3가지 모듈 중 하나로 생각하고(혹은 Refactor를 통해 Rename) 사용해도 무관하다.

 

그렇게 생성하고 나면 다음과 같은 형태로 프로젝트 구조가 잡힐 것이다.

그 후에는 이제 각 계층에 맞춰서 필요한 package를 생성하고, 클래스를 만들어서 넣으면 된다.


실제 프로젝트를 진행하게 된다면 패키지를 전부 구분하여 만들어 두고 의존성을 주입하는 등의 작업을 시작하기는 쉽지 않겠지만, 우리는 공부를 위한 예제를 맞드는 것이니 정석대로 만들어 넣어보자.

 

각 계층에 필요한 것들은 무엇인가 확인해보자.

  • Domain Layer: Data Model (Entity), Repository, UseCase
  • Data Layer: Local/Remote Data (DTO), DB(Room, Dao), DataSource (Repo), Mapper
  • Presentation Layer: UI, VM, DI, Module

 

우선 패키지를 만들어 넣어보자.

여기서 필자가 작성해둔 Entity, DTO에 관련된 부분은 사람마다, 회사마다 어떠한 기준으로 생각하느냐에 따라 다르다고 생각한다. 어쨋든 데이터 클래스 라는 것만 기억하자

 

패키지를 만들었으면,

 

각 패키지에 필요한 클래스들을 생성해주자.

이때 필자는 Presentation 모듈에 관련된 것들부터 생성해주었다. 추상적인 것들을 구현하는 것보다 실제로 눈으로 확인 할 수있는 즉, UI에 있는 부분부터 구현해 나가는 것이 조금 더 수월하게 진행이 된다고 생각하고, 그렇게 느끼기 때문이다.

 

BaseActivity, ViewModel을 생성하고, DI 관련된 것들을 생성해 준다. DI로 Koin을 사용하였으며 Api, Repository, Usecase, VM 등 필요한 모듈을 생성하여 의존성 주입을 해주었다.

 

util 패키지에는 presentation에서 사용할 util 클래스를, View 에는 필요한 activity, adapter, ViewModel을 선언해준다.

 

솔직히 말해서 Presentation 모듈에서는 VM을 선언하여 사용하고, 이 VM이 domain모듈의 Usecase를 사용하게 된다는 것 분이며, 그외의 코드들은 기존의 다른 구조와 동일하게 사용한다고 생각하면 된다.

 

그렇다면 VM의 코드를 확인해보자.

이처럼 해당 VM에서 사용하는 Usecase를 주입 하여 필요한 데이터를 가져와서 UI에 뿌려주게 되는 것이다.

각 Usecase를 주입받아서 이처럼 Usecase에 선언된 함수를 호출하여 원하는 데이터를 가져오게 된다.

Usecase를 통하여 Repository 구현부를 호출하고 구현부에서 CRUD를 통해 데이터를 가져오는 형식이다.

 

의존성 주입을 위해 Domain, Data모듈의 참조가 필요하게 되며, Usecase 사용을 위해 Domain 모듈의 참조가 필요하게 된다.

따라서, Presentation 모듈에서 Domain, Data 모듈에 대한 참조가 생기게 되는 것이다.

이러한 형태로 gradle에 의존성을 추가해주자.


다음은, 위에 말했던 ViewModel에 사용할 Usecase가 들어가는

 

Domain 모듈을 확인해보자.

 

domain 모듈에서는 실제로 UI 작업에 사용할 모델 (Entity)가 필요하며, 해당 모델을 통해 원하는 데이터를 가져오기 위한 Repository Interface와 Usecase를 선언한다.

 

Usecase를 사용함으로써 해당 VM이 어떤 것을 하고자 하는지 직관적으로 파악할 수 있으며, 의존성을 줄일 수 있다는 장점을 가지고 있기 때문에 Usecase를 사용한다.

Usecase는 직관적으로 어떠한 서비스를 제공하는지 알 수 있도록 네이밍을 해주어야 한다.

그리고 다음과 같이 repository를 사용하여 각 Usecase 별로 원하는 데이터를 가져올 수 있도록 함수를 구현해준다.

*

domain 모듈에서는 다른 모듈의 별다른 참조가 필요하지 않게 된다.

그렇기 때문에, presentation 모듈에서 처럼 gradle에 다른 모듈에 대한 참조를 추가할 필요는 없다.


마지막으로, data 모듈을 확인해보자.

 

Data 모듈에서는 로컬(DB) 데이터와 서버(API) 데이터들을 모두 가져와서 사용하기 때문에 이에 대한 패키지를 만들어 구분지어 주었다.

 

API, DB, Model에 관련된 소스코드는 별 다른거 없이 기존에 사용하던 방법과 동일하니 설명은 생략한다.

전체 코드는 본 게시물 하단의 깃허브를 확인하길 바란다.

각 DataSource 들은 위의 API, DB를 통해 데이터를 컨트롤 하는 CRUD라고 생각하면 편하다.

하지만 API를 사용하는 DataSource의 경우 API를 통해 값을 가져오는 것 밖에 하지 못하므로 API 의존성이 높아 개발자가 임의로 추가, 제거하기는 쉽지 않다. (서버와의 커뮤니케이션이 필요해진다.)

반대로, Local의 경우 DB (Room)에 저장하여 사용하는 부분이기 때문에 개발자가 원하는데로 데이터를 컨트롤 할 수 있게 된다.

 

따라서,

  • RemoteDataSource는 API에 맞춰서 구현을 해야하는 것이고
  • LocalDataSource는 개발자가 필요한 데이터를 가져오도록 커스터마이징이 가능하다.

마지막으로 Domain 모듈에서 선언했던 Repository의 구현부이다.

이 구현부에서는

 

  1. 위에 선언한 DataSource 들을 인자로 받아서 사용하며,
  2. Domain 모듈의 Repository에서 선언했던 함수들을 override 하여
  3. 원하는 데이터를 Presentation 계층으로 던져주는 부분

을 구현해주면 된다.

이처럼 각 DataSource를 사용하여 원하는 데이터를 Local 혹은 Remote에서 가져오고, 원하는 데이터 타입/형태로 만들어서 return 시켜주면 되는 것이다.

 

여기서, Mapper 클래스를 사용하여 원하는 데이터 타입/형태로 만들어 던지게 된다.

 

해당 예제에서는 DTO와 Entity 객체의 형태가 동일하게 사용되기 때문에 그대로 값을 던져주는 형식으로 구현이 되어 있지만, 실제 사용시에는 가져온 데이터와 사용할 데이터의 차이가 있기 때문에 해당 함수를 통해 변환시켜서 전달하도록 구현한 것이다.

*

Mapper 클래스나, Repository의 구현부를 사용하기 위하여 Data 모듈에서는 Domain모듈의 참조가 필요하게 된다.


이런식으로, Clean Architecture의 3가지 계층을 모듈로 나누어서 생성하고, 각 모듈에 따른 의존성을 gradle로 추가한다.

그 후, 각각 모듈의 특성에 맞춰서 필요한 클래스들을 선언하여 사용한다.

라는 아주 간단하고 깔끔한 개념이다.

 

하지만, 생각보다 구현하는데 생각을 많이 하게 되는 아키텍처라고 생각된다.

예제를 만들기 전까지는 머리로는 이해해도 실제로 어떻게 구현을 해야할지 막막했지만, 예제를 만들어보니 이해도 빠르고 어떻게 구조를 잡아야하는지 감이 잡히는 것 같다.

 

역시, 이론 공부도 중요하지만 실제로 해보는게 지식 습득에는 큰 도움을 준다.

 

해당 게시글에 사용되는 예제 코드

https://github.com/HeeGyeong/CleanArchitectureSample

 

GitHub - HeeGyeong/CleanArchitectureSample

Contribute to HeeGyeong/CleanArchitectureSample development by creating an account on GitHub.

github.com

 

 

출처

[Android] Clean Architecture 실전 압축 정리 - 예제 (tistory.com)