Ted Park
PRND
Published in
14 min readDec 8, 2022

--

헤이딜러 안드로이드팀은 어떻게 일하나요?(3) — 프로젝트 구조

- 코틀린 + MVVM + Coroutine
- 클린 아키텍쳐 / 멀티모듈 / DI
-디자인 시스템, 라이브러리 모듈

이 포스팅은 아래 시리즈로 연재중입니다.

  1. 헤이딜러 안드로이드팀은 어떻게 일하나요?(1) — 팀문화
  2. 헤이딜러 안드로이드팀은 어떻게 일하나요?(2) — 업무방식
  3. 헤이딜러 안드로이드팀은 어떻게 일하나요?(3) — 프로젝트 구조

아직 1편, 2편을 읽지 않으셨다면, 이 연재 시리즈의 1편부터 시작해서 읽고 오시길 추천 드립니다.

이번 포스팅에서는 안드로이드팀 프로젝트 구조에 대해 소개합니다.🙋🏻‍♀️ 🙋🏻 🙋🏻‍♂️

4개의 앱 운영

피알앤디에서는 총 4개의 앱을 배포해서 운영하고 있습니다.

  • 헤이딜러 고객용
  • 헤이딜러 딜러용
  • 헤이딜러 평가사용
  • 헤이딜러 딜러 발신번호 표시

플랫폼 서비스를 운영하는 앱들은 보통 고객용앱 뿐만 아니라
제휴된 업체, 혹은 관리자용 앱을 만들어서 운영하고 있을겁니다.
(배민 사장님용, 배민 라이더용 등)

이 앱들의 프로젝트 구조에 대해서 공유드립니다.

Kotlin 100%

헤이딜러는 2014년에 출시된 앱 서비스입니다.

요즘 앱을 새로 만든다면 당연히 Kotlin언어만으로 앱을 만들겠지만
5년이상된 서비스들은 당연히 Java 언어로 앱을 만들기 시작했기 때문에
Java 코드가 존재할 수 밖에 없습니다.

헤이딜러 프로젝트도 처음에는 Java코드 100% 프로젝트였지만,
점진적인 Kotlin변환을 통해서 현재는 Kotlin 100% 프로젝트로 운영되고 있습니다. 💯 💯

1,000개가 넘는 Java파일들을 모두 Kotlin으로 변환하는일이 얼마나 힘든 과정인지 해보신분들은 잘 아실겁니다.

  • 단순히 Java -> Kotlin으로만 Convert하는게 아니라 Kotlin스럽게 만들어야 함
  • 변환과정에서 예상하지 못한 버그가 발생
  • 피쳐 개발이 아닌 리팩토링 작업을 위한 별도의 시간이 필요함

MVVM 100%

안드로이드에는 Java -> Kotlin만큼의 큰 변화가 또 있습니다.

예전에는 MVC(혹은 아무 아키텍쳐 없이), MVP를 많이 썼었지만,
요즘에는 거의 대부분 MVVM구조로 만들고 있습니다.

5년이상된 앱 서비스들은 이 역시도 MVC이거나 MVP구조로 만들어진 앱들이 많이 있을것입니다.

그래서 대부분의 프로젝트들을 보면 MVVM과 MVP, 심지어 MVC 가 모두 섞여있는 대혼돈의 멀티버스인 프로젝트들이 많이 있습니다. 😱

저희 프로젝트 또한 MVP와 MVVM이 뒤섞여있던 프로젝트였지만,
점진적인 변환을 통해서 이제는 모든 프로젝트가 MVVM을 사용하도록 설계되어 있습니다.

Hilt 100%

DI(Dependency Inject)를 구현하는데 대부분 hilt 혹은 koin를 사용하고 계실겁니다.
(요즘은 거의다 Hilt로 넘어간것 같음)

저희팀에서는 처음에는 koin을 사용해서 구현했었습니다.

그러다가 Hilt가 release 버전이 생기고 구글에서도 Hilt를 공식적으로 밀기 시작하면서
점점 Hilt로 넘어가기 시작했습니다.

개인프로젝트의 규모라면 한번에 koin을 모두 Hilt로 변경할 수 있겠지만
규모가 큰 헤이딜러 서비스의 경우는 한번에 마이그레이션 할 수 없었기에
점진적으로 koin -> Hilt 로의 코드변환을 진행했습니다.

2개의 개념을 모두 고려하면서 피쳐개발까지 해야 하다보니
고통스러운 나날을 보냈지만…😢

지금은 모든 코드가 Hilt로 이루어진 DI사용 프로젝트로 되어 있습니다.

Goodbye RxJava, LiveData / Hello Coroutines

저희팀에서도 이전에는 RxJava와 LiveData를 사용해서 효율적인 코드를 작성했었습니다.

하지만 이제는 코루틴으로 모든것을 해결하고 있습니다.

Observable, Single, Completable, Maybe들을 Flow나 suspend function으로 작성해서 사용하고 있습니다.
요즘은 대부분의 3rd party 라이브러리들의 Flow, suspend function을 지원하고 있기 때문에 전혀 문제가 없습니다.

사실 RxJava -> 코루틴 으로의 변화는 코드를 작성하는데 큰 개념의 변화입니다.

  • RxJava: 자바스크립트 ES6에서 소개된 Promise패턴
  • Coroutines: 자바스크립트 ES7에서 소개된 Async/Await패턴

각각의 사용방식에 대한 비교와 발전과정은 아래 포스팅을 참고해보시면 됩니다.

LiveData 를 StateFlow로 대체해서 사용하고 있습니다.

사실 이전부터 StateFlow를 LiveData로 대체해서 사용하고 싶었지만,
Android Studio에서 databinding을 지원해주지 않아서 사용하지 못했었습니다.

그러던중 Arctic Fox | 2020.3.1 버전부터 data binding에서 StateFlow를 지원하도록 업데이트 되었습니다.

https://developer.android.com/studio/releases/past-releases#state-flow-data-binding

LiveData를 사용하고 계신다면, 단순히 StateFlow로 변경하시기만해도 바로 적용해보실 수 있습니다.
(default값 넣어주는건 보너스 작업)

UI에서 일어나는 이벤트(클릭처리 등)들을 ViewModel에서 받아서 SingleLiveData를 활용하던 방식도
SharedFlow(EventFlow)로 사용하도록 변경되었습니다.

이와 관련된 내용은 헤이딜러에서 LiveData -> SingleLiveData -> SharedFlow -> EventFlow로
이벤트 처리 방법을 변화 하기까지 과정을 소개한 포스팅을 참고하시면 도움이 됩니다.

클린 아키텍쳐

앱을 개발한다면 클린 아키텍쳐와 위의 그림은 한번쯤은 꼭 보셨을겁니다.

저희팀에서는 모든 코드를 클릭 아키텍쳐의 설계로 작성하고 있습니다.

밥 아저씨의 클린 아키텍쳐 vs Google의 앱 아키텍쳐

Google의 안드로이드 문서에서 소개되고 있는 앱 아키텍쳐는 클린 아키텍쳐가 아닙니다.

밥 아저씨의 클린 아키텍쳐와 Google의 앱아키텍쳐는 서로 다른 구조입니다.

회사에 제출되는 채용과제들을 보면 위 2개의 아키텍쳐 구분없이 섞어서 구현하거나 잘못알고 구현하는 경우가 많이 있습니다. 🤔

여러분이 알고 계시는 클린 아키텍쳐를 다시한번 생각해보시고 아래 질문에 대해서 O/X 로 답해보시기 바랍니다.

  1. Presentation layer는 Domain layer를 알고있다? (O) / (X)
  2. Domain layer는 Data layer를 알고 있다? (O) / (X)
  3. Data layer는 Domain layer를 알고 있다? (O) / (X)

Google의 앱 아키텍쳐

  1. O
  2. O
  3. X

밥아저씨의 클린 아키텍쳐

  1. O
  2. X
  3. O

Google의 앱아키텍쳐에서는 ‘domain layer가 data layer를 알고 있다’는 개념으로 출발합니다. 😨

https://developer.android.com/topic/architecture/domain-layer

실제 google의 예제 앱에서 domain의 코드를 살펴보면 data layer의 코드를 의존해서 사용하고 있는것을 확인할 수 있습니다.

구글이 말하는 앱 아키텍쳐는
우리 팀에서 생각하는 클린 아키텍쳐가 아니었습니다.

  • Domain layer는 아무도 몰라야 한다
  • Presentation layer, Data layer가 Domain layer에 의존해야 한다

헤이딜러의 클린 아키텍쳐

  • Domain: UseCase, Repository, …
  • Presentation: ViewModel, UI
  • Data: RepositoryImpl, DataSource
  • Remote: DataSourceImpl, ApiService
  • Local: DataSourceImpl, DB, Preference

모델 구조

  • Model 클래스는 모든 layer에 존재함
  • 5개의 layer라면 5개의 Model 클래스가 존재

클린 아키텍쳐를 사용했을때의 장점들

  • UI개선작업이 생기면 UseCase, ViewModel 등의 변경없이 UI 영역만 변경하면 됨
  • 서버에서 우리가 생각하는 필드이름, 필드구조로 주지 않아도 domain 모델구조는 변경되지 않음
  • 차량 정보를 불러올때 local에서 먼저 가져온뒤 remote로 fetch해서 지연시간 없앰
  • API구현이 되어 있지 않은 상황에서도 remote만 Mock해서 구현
  • 테스트 코드 작성 용이
  • KMP로 iOS팀과의 대화합을 꿈꿀 수 있다..?

저희 팀의 더 많은 Architecture관련 규칙은 공개되어 있는 문서에서 확인하실 수 있습니다.

기회가 된다면 클린 아키텍와 관련된 자세한 포스팅으로 작성해보겠습니다.

멀티 모듈

클린 아키텍쳐에서 사용하기 위한 목적뿐만 아니라 상호 의존성을 낮추고 효율적인 코드 작성을 위해 멀티 모듈로 프로젝트를 구성하고 있습니다.

모듈을 많이 나눠놓는다면 레고블럭을 조합해서 앱을 만들듯이 필요한 모듈들만 가져다가 프로젝트를 구성할 수 있습니다.
또한, 라이브러리 자체에도 여러 모듈이 있어 각각의 앱에서는 필요한 모듈만을 가져다 쓰고 있습니다.

헤이딜러 프로젝트에도 예전에는 1개의 라이브러리 모듈에 다양한 기능과 개념들이 모두 모여있었지만
요즘에는 최대한 모듈을 많이 나누려고 노력하고 있습니다.

1군데에 보여있는 코드를 다양한 개념의 모듈로 나누는 작업은
지금 이순간에도 계속 되고 있습니다..(쭈욱…)🤯

라이브러리 모듈

고객/업체 등을 연결해주는 플랫폼 서비스를 운영하고 계신다면
각각의 프로젝트에 대한 공통 코드 관리에 대한 고민이 있으실거라 생각합니다.

헤이딜러도 여러 앱을 운영하고 관리하고 있다보니,
당연히 공통으로 사용하는 코드나 Base코드등에 대한 고민이 있었습니다.

저희는 git의 submodule을 활용해서
1개의 라이브러리 Repository를 각각의 앱들이 갖다 쓰는 방식으로 해결했습니다.

submodule을 사용하게 된다면 라이브러리 폴더가 local에 있는것처럼 참조해서 사용하는것처럼 보이게 되지만
실제로는 별도의 Repository에 있는걸 사용하게 되는 원리여서
공통으로 사용하는 코드에 대한 관리를 아주 효율적으로 할 수 있습니다.

또한 라이브러리 모듈에서 release branch를 만들면서 버전관리를 하고 있습니다.

만약 submodule에서 버전 관리 없이 라이브러리 모듈을 참조하기만 한다면
항상 최신 커밋 코드만 참조되므로 문제가 발생합니다.

문제상황 예시

  1. 앱 1.3.1로 Hotfix를 해야할 상황이 생김
  2. 앱 1.3.0을 배포한뒤에 라이브러리 모듈에 여러 코드들이 추가된 상
  3. Hotfix를 할때는 1.3.0을 배포하던 시점의 코드를 기반으로 작업을 해야 하는데
  4. 항상 최신 커밋 코드만 참조하게 되므로 그사이에 추가된 코드들까지 의도치 않게 같이 반영되어 버림 ( 1.3.0 때의 코드를 참조할 수 없음)

디자인 시스템

헤이딜러의 디자인팀에서는 Figma를 사용하고 있으며 디자인 시스템도 정의하고 있습니다.

안드로이드 팀에서는 별도의 design모듈을 만들어서
해당 모듈에 디자인 시스템을 코드로 구현해놓고 있습니다.

HeyDealerTextView, HeyDealerButton 등 Figma 디자인시스템과 일치하는 컴포넌트를 만들어 둡니다.

Figma의 Typo에 대한 디자인시스템을 HeyDealerTextView로의 구현은 아래와 같습니다.

또한 구현된 TextView가 실제 디자인시스템의 화면과 동일하게 보여지는지 확인하면서 구현합니다.

HeyDealerButton또한 미리 컴포넌트로 만들어두고 필요할때 type에 맞게 사용하도록 하고 있습니다.

이렇게 만들어진 컴포넌트들을 확인할 수 있도록
별도의 catalog앱을 만들어서 여러 컴포넌트들이 어떻게 보여지는지 확인할 수도 있습니다.

애니메이션

헤이딜러 앱을 구현하면서 애니메이션과 사용자 친화적 UI/UX를 구현하는데도 많은 신경을 씁니다.

  • MotionLayout
  • Shared element transition
  • Animation, Transition 등

지금까지 헤이딜러 안드로이드팀의 프로젝트 구조에 대해 소개해드렸습니다.

여러분의 회사에서 사용하시는 프로젝트는 어떻게 구성되어 있나요?
헤이딜러에서 사용하는 것들과 많이 닮아 있나요?

본인의 회사에서 더 좋은 프로젝트 구조나 기능을 사용중이신 것들이 있으시다면 댓글로 공유해주세요.

다음에도 더 좋은 컨텐츠로 찾아뵙도록 하겠습니다.

--

--