사입앱 클린 아키텍쳐 적용기

클린 아키텍쳐를 써보며 고민했던 점과 장단점을 적어 보았습니다.

김영훈
2022.12.13

딜리셔스에서는 신상배송이나 딜리버드 서비스를 제공하기 위해 동대문에서 의류 사입을 진행하고 있습니다. 예전에는 업무가 모두 수작업과 지류로 처리 되어서 인적 자원이 많이 필요했고 시간이 오래 걸렸습니다. 이를 해결하기 위해 사입사 직원용 앱(이하 사입앱)을 제작하여 업무 편의성을 향상시켰습니다. 이번 글에서는 안드로이드 사입앱에 클린 아키텍쳐를 적용한 경험을 공유드리려 합니다.

목차

  • 도입 배경
  • Clean Architecture
  • Android CleanArchitecture
  • 사입앱 CleanArchitecture V1 , V2
  • Correct Way To Access String Resource In UseCase
  • Parallel Multiple Network Calls In UseCase
  • 사입앱 적용 후 느낀 장단점

도입 배경

초창기 사입앱은 MVVM패턴을 사용해서 개발을 진행했습니다. 처음 MVVM 패턴을 도입하면서 Activity or Fragment에서 처리되었던 UI + 비즈니스 로직들이 UI(Activity or Fragment) 와 ViewModel(비즈니스 로직) 으로 분리되었습니다. 그 결과 역할 분담이 되어 Activity or Fragemnt의 역할이 많이 줄었습니다.

그 대신 복잡한 비즈니스 로직이 들어가게 되는 경우 ViewModel의 코드가 길어지는 현상이 발생했습니다. 공통으로 사용 되는 부분들을 특정 ViewModel로 묶어서 작업을 진행 하였지만 이 방법은 중복 코드만 줄여 줄 뿐 ViewModel의 역할을 줄여주는 방법은 아니라고 생각 되었습니다. 그 해결 방법을 찾아보던 중 CleanArchitecture의 UseCase를 통해서 해결할 수 있다는 생각을 하게 되었습니다.


Clean Architecture

처음 Clean Architecture를 공부하다 보면 대부분 위의 그림을 접하게 될 것입니다. 클린 아키텍쳐의 개념을 도식화한 것으로, 포인트는 계층을 분리하고 계층간의 의존성을 단방향(바깥에서 안쪽)으로 만들어 준다는 점입니다. 간략하게 Entities , UseCases , Presenters 에 대해 설명하자면 다음과 같습니다.

  • Entities: 전사적 비즈니스 규칙을 캡슐화 합니다.

  • UseCases : 애플리케이션과 관련된 비즈니스 규칙을 포함하고 시스템의 모든 유즈 케이스 구현체들을 캡슐화 합니다.

  • Presenters : UseCase 또는 Entities 로부터 얻은 데이터를 가공하는 계층 안드로이드에서 ViewModel 이 해당 됩니다.


Android CleanArchitecture

안드로이드에서는 크게 3가지 계층으로 나눠서 CleanArchitecture를 적용합니다.

  • PresentationLayer
  • DomainLayer
  • DataLayer

View에서 어떤 액션이 발생하면 Presenter에 전달 하고 Presenter 는 UseCase 를 사용 합니다. API or DataBase를 사용한다면 Repository를 접근해서 Callback받은 데이터를 Translater를 통해 Entity -> Model 로 Mapping 해서 사용하게 됩니다.

사입앱 CleanArchitecture V1

Android CleanArchitecture 를 공부하고 난 후 사입앱에 처음 적용 했을때의 구조입니다. 해당 구조에서 고민한 점은 크게 3가지였습니다.

  • 무엇을 Entity로 생각해야 하는가?
  • Domain은 독립적인 Module로 알고 있는데 Data 모듈을 참조하는게 좋은 방향일까?
  • Repository Retrofit, DataBase가 중심부에 들어가 있는게 좋은 방향일까?

DTO를 Entity로 생각 했기 때문에 의존성 방향은 단방향으로 흘러 가지만 Domain은 독릭적인 모듈이 될 수 없다(Domain은 Data를 바라보기 때문)는 생각이 들었습니다. 그리고 DTO 와 Repository가 중심부에 있기 때문에 처음부터 어떤 통신 라이브러리 or DataBase를 사용할지 정해야 하기 때문에 중심이 아닌 바깥으로 가는게 맞다고 판단 되었습니다.

그래서 아래와 같이 V2를 만들어서 수정했습니다.

사입앱 CleanArchitecture V2

V1에서 고민했던 3가지 문제점이 해결되는 순간입니다😁

  1. 무엇을 Entity로 생각해야 하는가? 해당 부분은 Domain Layer에 있는 Model을 Entity로 생각 합니다.
  2. Domain 모듈이 어떤 Denpendency도 가지지 않은 독립적인 모듈이 됩니다.
  3. Repository Retrofit, DataBase가 중심부에 들어가 있는게 맞을까? Domain이 중심으로 들어갔기 때문에 Data는 자연스럽게 바깥으로 빠지게 됩니다.


Correct Way To Access String Resource In UseCase

UseCase에서 Context 을 사용하지 않기 위해 IdValidataionChecker 와 같은 특정 클래스를 통해서 결과값을 Return받게 되면 직접적으로 getString을 사용하는 부분을 막을 수 있습니다.

Parallel Multiple Network Calls In UseCase

첫번째 케이스는 하나의 UseCase에서는 하나의 Repository만 사용하게 2개가 필요한 상황에서는 2개의 UseCase를 사용하는 하나의 UseCase를 만들어서 사용하는 예제입니다. 두번째 케이스는 하나의 UseCase에서 두개의 Repository를 사용하는 예제입니다.

2가지 모두 사용 했을때 두번째 케이스가 확장성과 가독성에 있어서 더 좋다고 생각 했습니다. 그 이유는 Flow는 zip으로 묶게 되면 2개까지만 사용 가능하고 여러개를 사용 하려면 직접 만들어야 하고 에러 예외 처리도 쉽지 않아서 두번째 케이스가 더 사용하기 적합하다고 판단해서 두번째 케이스로 사용하고 있습니다.


사입앱에 클린 아키텍쳐 적용 후 느낀 장단점

CleanArchitecture 적용 후 느낀 장점

  • 의존성 분리 의도하지 않은 의존성을 가지고 있는 부분들을 모듈화 작업을 통해 의존성을 분리 시킬 수 있었습니다.

  • ViewModel 역할 감소 ViewModel 에서만 담당하던 역할을 UseCase를 통해 나눌수 있습니다. ViewModel 생성자에서 어떤 UseCase를 사용하는지 확인 가능 하기 때문에 어떤 기능들을 사용하는지 바로 알 수 있습니다.

  • 모듈 역할 분담 모듈별로 역할 분담이 잘 되어 있어서 CleanArchitecture를 이해 하고 있는 사람이라면 다른 사람이 투입 되어도 이해하기 쉽습니다.

  • Nullable DTO DTO 모델을 모두 Nullable Field로 사용하면서 서버측에서 실수로 Null 로 내려주더라도 신경 쓰지 않고 Model로 매핑 작업 시 NotNull 처리를 하기 때문에 안전성 또한 더 올라간 것을 느낄 수 있었습니다.

  • BuildSpeedUp 모듈 분리를 통한 병렬 빌드가 이루어 지기 때문에 빌드 속도가 향상됩니다.

  • 재사용성 증가 UseCase가 각 하나의 역할만 하기 때문에 다른 곳에서도 재사용하기 쉽습니다.

CleanArchitecture 적용 후 느낀 단점

  • Learning Curve 아키텍쳐 관점에서 바라보는 구조적 이해가 필요했으며, 이는 높은 러닝 커브를 요구합니다. 또한 멀티 모듈에 대한 이해도가 필요했습니다. 그리고 명확한 Best case의 클린 아키텍쳐를 찾기 어려웠습니다.

  • Kotlin 파일 수량 증가 생각보다 많은 클래스를 생성 해야 합니다.(특히 도메인 영역)


김영훈

딜리셔스 안드로이드 개발자

"기록하고 기억하자."