안녕하세요. 스포카 제품팀의 안드로이드 개발자 황재우입니다.

스포카는 최근 정비 기간을 통해서 개발팀 내에 많은 변화를 이루어냈습니다.
마찬가지로 안드로이드 챕터도 큰 변화를 가져왔는데요.
이 글은 도도 카트 앱을 왜 개선해야 했는지, 무엇을 어떻게 진행했는지에 관해 이야기합니다.

어떤 변화가 필요했을까요?

왜

도도 카트 안드로이드 앱은 당시 복잡한 모습을 보여주고 있었습니다.

이해하기 어려워진 프로젝트 구조

구조를 파악하기 어려웠습니다.
도도 카트 안드로이드 앱은 그동안 2년간의 개발 기간을 거치면서 여러 가지 구조와 형태가 프로젝트 내에 섞였습니다. 몇몇 사용하지 않거나 중복된 코드가 존재하면서 이해하기 어렵게 됐습니다.

라이브러리 버전 관리

빌드 warning이 30~40개 정도 있었습니다.
대부분 Android Gradle Plugin에서 3rd party 라이브러리까지 오래된 버전들이 남아 있었습니다.

파악하기 어려운 이슈

배포 시 맵핑 파일이 같이 올라가지 않아서, 발생한 이슈에서 호출 스택을 봐도 정확한 위치를 찾기 어려웠습니다. 또한 Firebase와 Sentry는 라이브러리 버전이 구 버전인 상태라서 가이드에 맞춰 다시 연동해야 했습니다.

Super Class

너무 많은 기능을 하고 있는 Super Class가 존재했습니다.
기능적인 면에서 분리되어야 했을 법한 로직들이 단일 클래스에 뭉쳐 있다는 것은 강력한 Coupling 이 프로젝트 내에 존재함을 뜻합니다.

cry

어떻게 준비했을까?

준비

도도 카트 팀은 이전부터 정비기간이 필요할 것 같다는 공감대가 형성되어 논의 중이었습니다.
시기적으로 마침 안드로이드 앱 구조 개선에 대해 논의하면서 더불어 팀 내 정비 기간에 대한 논의가 본격적으로 시작됩니다. 결론적으로 저희는 정비 기간을 가지고 천하를 도모할 수 있게 되었죠!!
처음에는 리팩토링과 기능 개발을 병행하려던 생각이었으나 오로지 개선에 집중할 수 있게 되면서 작업에 유리한 환경이 조성됩니다.
이에 따라 최종 일정을 세우고 개선을 진행하기 위해서 내용을 한 번 더 정리했습니다.

목표 설정

일단 목표를 설정합니다. 왜 하는지 당위성도 포함됩니다.

goal

진행 방향과 작업 정의

모듈을 분리하고, 각 모듈은 클린 아키텍처를 표방합니다. 의존성 처리와 테스트 코드는 필요에 따라 들어갑니다.

def

마음가짐

do your best

가장 중요한 부분입니다. 작업 일정이 오래 지속되면 매우 힘들고 지치고 부담스럽거나, 더 잘하고 싶어서 욕심을 부리고 과다하게 일을 진행하게 됩니다. 그렇기에 내가 어떻게 하리라는 것을 정의하는 것은 매우 중요한 일입니다.

이것은 두 가지 효과를 볼 수 있는데요.
첫째, 나 자신에게 거는 주문입니다.
무엇이 중요하고 무엇이 후순인지 명확히 하면, 의사 결정에 있어서 주저함이 없습니다. 그것이 코드이든 아키텍처이든 또는 그 외에 비 개발적인 것들도 포함해서요.
둘째, 내가 하는 작업의 그라운드 룰을 선포하는 의미가 있습니다.
나의 작업은 룰이 존재하고 그 룰안에서 이루어지리라는 것입니다라는 것을 대내외적으로 선포하는 것입니다. 이를 통해서 팀 내 구성원 모두에게 이 작업이 가지는 의미를 공표하는 효과를 가집니다.

mindset

이렇게 마음 먹고 시작했다!

작업 사항과 일정 산출

작업 사항을 나열하고 일정을 산출합니다. 이때 주요한 작업과 기본 작업으로 분리된 상황이므로 주요 작업과 기본 작업에 대해서 일정을 적절하게 나눴습니다.

task

어떻게 개선했을까?

어떻게

빌드 구조 변경

Kotlin DSL을 활용한 build.gradle.kts로 모두 전환했습니다.
gradle buildSrc를 활용해서 Gradle에서 라이브러리 의존성을 관리하고
buildsrc Gradle 구성 설정도 공용화했습니다.
depend

모듈 분리

앱 모듈과 공통 모듈, 기능 모듈이 모여서 구성되는 형태로 바꿨습니다.

module

각 모듈은 대부분 UI를 포함한 기능 모듈이며, Clean Architecture를 표방하여 구조가 만들어졌습니다.

single_module

코드를 자세하게 다룰 수 없으나, Usecase와 Repository, DataSource로 이루어진 일련의 클래스들로 구성됩니다.
네 맞습니다. 안드로이드 개발자 사이트에서 안내하는 바로 그 앱 아키텍처를 기반으로 구현되었습니다.

app_arch

다이어그램은 쉬워 보인다...

이벤트 처리

도메인과 데이터 영역에서 발생하는 이벤트는 ViewModel에서 UI 이벤트로 소비되도록 처리되었습니다. 따라서 도메인이나 데이터 영역에서 ViewModel을 통하지 않고 UI 영역을 넘어가지 않습니다.

lifecycle 에 기반한 Coroutine 과 Flow

기존에 있었던 Flow와 Coroutine을 기반으로 만들어진 비동기 처리 로직을 개선했습니다. 적절한 사용처와 Lifecycle에 맞춰서 Kotlin Coroutine을 처리했습니다.
Flow를 통한 서버 통신 처리는 단순한 비동기 데이터 수신 처리를 넘어서 Flow 확장 함수를 활용하여 예외 처리와 데이터 이벤트 처리를 하고 이를 연계 된 ViewModel에서 UI 이벤트로 소비될 수 있게 처리되었습니다.

flow

물 흐르듯이 자연스럽게~

Koin → Hilt

Koin은 이해하기 쉽고 빠르게 적용할 수 있다는 큰 장점과 런타임에 오류와 오버헤드를 가지고 있다는 큰 단점이 존재합니다.
안드로이드 진영에서 Dagger를 대신해 선택되었던 라이브러리였지만 최근에 Hilt 가 나오면서 상황은 매우 달라졌습니다.
Hilt는 기존 Dagger를 활용해 안드로이드용으로 만들어진 의존성 주입 라이브러리입니다.
Koin과 달리 컴파일 타임에 오류가 검출되며, 적용하는 것이 어렵지 않습니다.
그래서 의존성 주입은 Hilt를 통해서 하기로 했습니다.
기존 의존성 처리를 걷어내고 Hilt로 새로 구성해 나갔습니다.

아쉬움과 남은 과제

큰 구조적인 변화를 가져왔고, 분리된 모듈로서 형태를 갖췄음에도 아쉬움은 여전히 남습니다.
하고 싶었지만 못했던 것들은 이렇습니다.

모델 분리

도도 카트 앱은 Graphql 을 사용하며 Graphql의 Data를 그대로 사용하고 있습니다. 이 의존성이 전 영역에 걸쳐서 뻗쳐 있어서 이걸 제거하고 VO와 DTO를 기반으로 하고 싶었습니다.

사용자 액션 객체화

사용자가 발생시키는 이벤트에 대해 데이터 객체를 생성해서 처리하고 싶었습니다.

비지니스 로직의 세분화

비즈니스 로직이 ViewModel에서 존재하지만 ViewModel 내에서도 너무 많은 코드가 들어갑니다. 단일 로직을 책임지고 결과를 줄 수 있는 객체로 변경하고 싶었습니다.

부족한 테스트 코드

테스트 코드가 전체 모듈에 다 적용되지 못했고 주요 모듈에서만 적용되었습니다. 앱의 기능적인 완성도와 안정성을 위해서 테스트 코드를 좀 더 많이 작성하고 싶었습니다.

마치면서

victory

드디어 끝냈다!

이번 개선 작업을 통해서 프로젝트 전반의 이해도를 높였고 앞으로 있을 여러 작업에 대한 만반의 준비를 했다고 자신합니다. 유지 보수와 관리가 용이한 구조로 바꾼 만큼 더 재밌게 일을 할 수 있게 되었습니다.

무엇보다도 이번 개선 과정에서 많은 코드 리뷰와 배려, 신뢰를 보여준 동료들에게 깊은 감사를 드립니다.

도도 카트 앱은 이제 시작입니다!
저와 함께 재밌게 안드로이드 개발을 하실 분들의 많은 지원 부탁드립니다!

지금까지 긴 글을 읽어주셔서 감사합니다.

스포카에서는 “매장과 세상을 세련되게 연결하다” 라는 슬로건 아래, 매장에 도움되는 여러 솔루션들을 개발하고 있습니다.
더 나은 제품으로 세상을 바꾸는 성장의 과정에 동참 하실 분들은 채용 정보 페이지를 확인해주세요!