App Bundle의 Dynamic Delivery로 국가별 모듈 제공하기

한로니
당근 테크 블로그
16 min readAug 31, 2020

App Bundle의 여러 특징 중 하나를 꼽자면 단연 Dynamic Delivery가 아닐까 싶습니다. App Bundle 게시 모델이 존재하기 이전에는 이런 게 가능하리라는 상상을 해본적이 없었기 때문에 이 기능이 소개되었을 때 ‘와! 멋지다. 그런데 내가 이걸 써볼 일은 없을거야’ 정도의 감상이었습니다. 왜냐하면 정말 이런 게 필요하다.라고 느껴본적이 없었거든요. 그런데 말입니다. 당근마켓이 2019년 하반기 해외 진출에 출사표를 던지고, 당근마켓 안드로이드 앱에서 이전에 없던 몇가지 기술적 고민이 생기기 시작했습니다. 그 중 하나는 어떻게 하면 한국 사용자들을 한국 마켓으로 입장시키고, 영국 사용자들을 영국 마켓으로 자연스럽게 입장시킬 수 있을까? 였습니다. 이 포스트에서는 App Bundle의 Dynamic Delivery를 이용해 당근마켓 안드로이드 앱에서 이 문제를 어떻게 해결했는지에 대해서 이야기 합니다.

시작하며

세계적 커피 체인인 스타벅스는 국가별로 앱이 존재합니다. 스타벅스 한국, 스타벅스 싱가포르 앱은 패키지명이 서로 다르기 때문에 한 기기에 세계 각국의 스타벅스 앱을 설치하는 게 가능합니다. 반면 아마존은 국가와 상관없이 아마존 쇼핑이라는 단일 앱만이 존재하고, 사용자는 앱 내에서 국가를 선택해 나라별 페이지로 입장하는 방식입니다. 스타벅스와 아마존 앱은 태생적인 간극이 있는 셈이죠. 당시 당근마켓에서도 어떤 전략을 취할지에 대한 의사결정이 필요한 시점이었고, 이런저런 의견들이 오갔습니다. 결론은 스타벅스와 같은 방식은 출시 국가가 늘어날수록 개발 및 유지보수 비용이 지수적으로 증가할 가능성이 높기 때문에 선택하기 어려운 카드였습니다. 그래서 후자의 방식, 하나의 당근마켓 앱으로 한국을 비롯한 다른 국가의 사용자들이 사용할 수 있는 유니버설 앱의 형태로 방향을 정했고, 앞서 얘기한 사용자가 최초 앱 실행 시 불필요한 동작 없이 해당 국가의 마켓으로 입장하게 하려면 어떻게 해야할까?라는 고민이 시작 되었습니다.

사용자의 국가를 판단할 수 있는 방법들

당근마켓 앱은 최초 실행 시 간단한 앱 소개 화면이 보여지고, 시작하기를 클릭하면 내 동네를 선택하게 되어있습니다. 당근마켓 서버는 한국과 영국이 완전히 분리되어 있기 때문에 두번째 화면인 동네 리스트 API를 호출하기 전에 사용자가 어떤 국가의 사용자인지를 반드시 알아야 합니다. 왜냐하면 한국 사용자라면 API 호스트를 kr.karrotmarket.com으로 UK 사용자라면 uk.karrotmarket.com와 같은 형태로 호출해야 하기 때문에 사용자의 국가가 런타임에 클라이언트에서 결정될 필요가 있는 것이죠.

최초 실행 시 사용자가 동네를 검색하고 선택하는 화면

아마존 쇼핑은 최초 앱을 실행할 때 사용자에게 상품을 쇼핑 할 국가/지역을 입력 받습니다. 이렇게 하는 이유는 일본 사용자가 미국 아마존에서 물건을 구매해 국제 배송을 받는 게 가능하기 때문이겠죠?

아마존 쇼핑 앱의 국가/지역 설정 화면

그런데 당근마켓은 슬세권에 있는 이웃들과의 직거래와 연결을 지향하기 때문에 한국 사용자가 영국 중고거래 피드를 봐야할 개연성이 굉장히 낮습니다. 그래서 최초 앱을 실행했을 때 국가를 선택하는 화면이 필수요소로 들어가게 되면 자연스럽지 못한 느낌을 줄 수 있습니다. 한국에 거주하고 있는 사용자라면 마땅히 한국 마켓으로 접속하는 게 사용자의 기대라 볼 수 있는데, 만약 국가를 선택하게 만든다면 불필요한 행위를 유도하게 될 가능성이 높은거죠.

이 문제를 해결하기 위해 기술적인 접근을 했을 때, 떠오르는 질문은 ‘앱을 실행했을 때 사용자의 국가 정보를 얻으려면 어떻게 해야할까?’ 였습니다. 머리를 스쳐지나가는 몇가지 방법이 떠오르실거에요. GPS, USIM, IP, 타임존, 로케일등등… 이 중 몇몇은 반드시 권한 동의가 필요하거나, 일시적으로 정보를 받을 수 없는 경우가 왕왕 발생한다거나, 꽤 시간이 소요될 수 있다는 점, 혹은 기기 설정에서 정보를 쉽게 변경할 수 있기 때문에 신뢰성이 떨어진다는 저마다의 제약을 갖고 있습니다. 그래서 최초 앱 실행 시점에 사용자의 국가 정보를 판단하기에는 조금씩 부족한 면면들 때문에 국가를 판단하는 주요 요소로 사용해야겠다.라는 결정을 쉽게 내릴 수가 없었습니다. 그래서 사용자 권한 동의 없이, 센서나 네트워크를 사용하지 않으면서도 빠르고 신뢰할 수 있는 결과를 얻을 수 있는 대안을 찾기 시작했습니다.

App Bundle의 Dynamic Delivery

구글 I/O 19의 Customizable Delivery with the App Bundle and Easy Sharing of Test Builds 세션에서 앱 번들을 이용해 특정 국가의 사용자에게 원하는 모듈이 담긴 apk를 내려줄 수 있다는 내용이 (놀랍게도) 제 기억 속에 있었고, 이 영상을 다시 보기 시작했습니다. 이 기술의 배경은 이런 것이었습니다. 앱 번들로 게시된 앱은 구글 플레이에서 사용자가 앱을 설치할 때 기기에 최적화된 APK들을 전달할 수 있는 기술적 기반을 갖고 있습니다. 앱 번들 이전에는 하나의 앱은 하나의 APK로 구성된다.라는 게 일반적인 개념이었지만 앱 번들 시대에는 하나의 앱은 여러개의 APK로 구성될 수 있고, 사용자 환경에 최적화 된 여러 APK들이 기기로 전달된다.라는 패러다임의 전환이 있었습니다. (앱 번들과 관련된 좀 더 자세한 내용은 이 전 글인 더 작은 APK를 위한 Android App Bundle에 대해서에서 확인해보세요)

앱 번들은 앱 실행에 필수 요소인 기본 APK(base APK)와 ABI, 언어, 화면 밀도와 같은 기기 설정에 최적화 된 설정 APK(configuration APKs), 동적 기능을 위한 APK(dynamic feature APKs)로 구성될 수 있습니다. 당근마켓 앱을 설치한 사용자의 국가 판정을 위해 중요한 요소가 바로 앱 번들의 동적 기능(Dynamic feature)입니다. 앱은 다양한 기능들로 구성되어 있습니다. 앱 번들을 사용하더라도 동적 기능을 사용하지 않는다면 앱의 모든 기능은 기본 APK(base APK)로만 제공됩니다. 하지만 동적 기능을 이용한다면 기능 별로 APK를 사용자에게 제공할 수 있습니다. 예를 들면 당근마켓에는 채팅을 통해 상대방과 거래 약속을 설정할 수 있는 기능이 있고, 이런 기능은 앱 실행에 필수 요소가 아니기 때문에 base APK로부터 분리해서 사용자가 필요로 할 때 제공하기 좋은 예가 되겠죠?

당근마켓의 일부 사용자들이 유용하게 사용하는 거래약속 기능

동적 기능 모듈은 다음과 같은 방식을 통해 사용자에게 제공할 수 있습니다.

  • Install-time delivery
  • On demand delivery
  • Conditional delivery
  • Instant delivery

이 중 Conditional delivery는 앱을 설치할 때 사용자의 국가, 기기 하드웨어 기능, API 레벨을 조건으로 동적 모듈을 사용자 기기로 전달 할 수 있습니다. 사용자의 국가로 분기를 할 수 있다는 부분이 중요한 대목인데요. 이 정보는 어디에서 오는 걸까요? 아쉽게도 이 부분에 대한 정보가 투명하게 공개되어 있지는 않습니다. 구글 I/O 영상에 의하면 구글 플레이에 등록된 사용자의 결제 주소와 기타 정보를 사용한다 정도로 언급되어 있습니다.

본격적으로 코드를 만들어 볼까요?

저의 전략은 이러했습니다. 한국과 영국에 설치될 countrycodekr, countrycodegb 동적 모듈을 각각 만들고, 이 모듈을 앱 설치 시점에 사용자의 국가를 기반으로 조건부 제공을 합니다. 사용자가 앱을 설치할 때 구글 플레이는 프로파일링 된 사용자 정보를 기반으로 기본 APK, 설정 APK, 동적 기능 APK들을 기기로 전달하게 됩니다. 이 때 사용자의 거주 국가가 한국이라면 동적 기능 contorycodekr APK를, 영국 사용자라면 countrycodegb APK를 제공하게 되겠죠. 이제 남은 작업은 사용자가 처음 앱을 실행했을 때 contorycodekr과 countrycodegb 중 어떤 APK가 설치됐는지를 파악만 하면 되겠다.라는 그림이 그려졌습니다.

동적 모듈의 특징 중 하나는 코드를 포함할 수도 포함하지 않아도 된다는 점입니다. 후자의 경우는 용량이 큰 리소스들을 별도의 모듈로 제공하는 게 가능하다는 의미이기도 합니다. 당근마켓의 국가 판정 동적 모듈에는 어떠한 코드도, 리소스도 존재하지 않습니다. 단지 국가 판정을 위한 APK가 기기에 설치되어 있는지를 판단하는 용도이기 때문에 다음과 같이 AndroidManifest.xml 파일만 존재할 뿐입니다.

한국, 영국 국가 판정을 위한 동적 모듈

동적 모듈의 AndroidManifest는 delivery를 위한 특별한 속성들을 갖고 있습니다. 자세한 내용은 이 문서를 확인해주세요. 당근마켓에서는 앱 설치 시점에 모듈을 제공하기 위해 dist:install-time 요소를, 영국(또는 한국)에만 전달하기 위해 dist:user-countries를 사용합니다. (아래는 countrycodegb의 AndroidManifest.xml)

<dist:delivery> 
<dist:install-time>
<dist:conditions>
<dist:user-countries dist:exclude="false">
<dist:country dist:code="GB" />
</dist:user-countries>
</dist:conditions>
</dist:install-time>
</dist:delivery>

만약 프로젝트의 minSdk가 4.4라면 조금 난감한 상황이 생깁니다. 4.4에서는 dynamic delivery가 지원되지 않고, multi-APKs 모드로 동작하기 때문인데요. 이 때는 dist:fusing을 설정해줘야 합니다. dist:include 속성을 true로 설정할 경우, 안드로이드 4.4 기기에 해당 동적 모듈을 항상 전달한다는 의미가 되고, false는 전달하지 않는다가 됩니다. 따라서 이 동적 모듈이 4.4 기기에서 필요한지 아닌지를 개발자가 수동으로 설정해야 합니다.

<dist:fusing dist:include="false" />

달리 말하면 사용자의 OS가 4.4라면 사용자의 국가를 조건으로 동적 모듈을 자동으로 제공할 수 없다는 이야기입니다. 당근마켓의 minSdk 4.4이기 때문에 이 사용자들의 지분이 어느정도인지 파악하는 게 필요했는데 2019년 10월 당시 정리한 자료에서 당근마켓 사용자 중 킷캣 비율은 0.6%였고, 안드로이드 전체에서 킷캣 비중은 6.9%였습니다.

19년 5월의 안드로이드 시장 점유율

한국만 놓고 보면 고려하지 않아도 될 수준이기는 했지만 전체 시장에서는 점유율이 6%대여서 조금 부담스러운 수치기는 했습니다. 하지만 시간이 지남에 따라 킷캣은 페이드아웃 될 것이 자명하기 때문에, 이 부분이 기술적 방향을 바꿀 정도로 대세에 영향을 줄 문제는 아니라고 판단했습니다. 이런 사용자에게는 다음 화면처럼 “Get started” 버튼 아래에 국가를 선택할 수 있는 폴백 UI를 제공하기로 했습니다. 스피너의 디폴트 값은 기기의 타임존과 언어를 보조 수단으로 사용해 최대한 사용자의 국가를 자동으로 추정할 수 있도록 말이죠. OS 5.0 이상에서 조건부 제공이 잘 동작한다는 가정을 하면 당근마켓의 minSdk가 4.4이기 때문에 최대 6% 정도는 국가 선택 UI를 볼 수도 있겠다는 예상을 할 수 있었는데, 이 결과는 글의 후반부를 확인해주세요. 🤓

국가를 알 수 없는 사용자를 위한 국가 선택 스피너

다시 코드로 돌아와서, 이제 남은 작업은 base Apk에 포함될 국가 코드를 판단하는 부분인데요. com.google.android.play:core의 SplitInstallManager를 사용해서 현재 기기에 설치된 동적 모듈을 확인할 수 있습니다. 바로 이런 형태로요.

fun getDeliveredCountryCode(): CountryCode { 
val installedModules = installManager.installedModules
return when {
installedModules.contains(DELIVERED_COUNTRY_CODE_GB) -> CountryCode.GB
installedModules.contains(DELIVERED_COUNTRY_CODE_KR) -> CountryCode.KR else -> CountryCode.NONE
}
}

새로운 기술에는 항상 리스크가 따르기 마련입니다. 제가 걱정했던 부분은 Conditional delivery의 정확도가 얼마나 높을까? 고려하지 못한 예외적인 상황이 발생하지는 않을까? 그럴때는 플랜 B를 어떻게 가져가야 할까? 당시 이런 데이터를 확인할 수 있을만큼 앱 번들에 대한 경험이 안드로이드 생태계에 쌓여있는 상태가 아니었기 때문에 직접 시행착오를 겪으면서 문제를 해결할 수 밖에 없을 것 같다는 각오와 약간의 비장함?이 필요했습니다. 만약 정확도가 떨어지거나 에러율이 높아서 기대와 다르게 동작한다면 빠르게 후속 조치를 해야 했기 때문에 다이나믹 딜리버리를 이용해서 문제를 해결했다.라는 안도감 보다는 걱정이 더 앞선 것도 이런 이유에서였습니다.

변경사항이 큰 배포를 앞두고 필드 테스트까지 해야 하는 상황, 안전벨트 잘 매주세요.라며 구구절절

이런 걱정은 Conditional delivery 테스트가 쉽지 않다는 점에서 비롯되었습니다. 사용자의 거주 국가를 조건으로 apk를 전달하는 처리는 전적으로 구글 플레이가 담당하기 때문에 로컬에서 countrycodegb 모듈을 내려받는 환경을 구성하기가 쉽지 않습니다. 즉, 외부 환경과 상호 작용하는 부분만 테스트가 가능하기 때문에 구글 플레이가 어떻게 동작하느냐가 사실 성패에 결정적 영향을 주는 것이죠. 그래서 초기에는 영국에 거주하고 있는 테스터 몇분을 섭외해서 구글 플레이의 내부 공유 링크를 전달하고 결과를 수집하는 형태로 테스트를 진행했습니다. 내부 공유 링크로 앱을 설치하면 다음과 같이 어떤 모듈들을 설치 했는지 파악할 수 있습니다.

한국 사용자에게 countrycodekr 모듈이 설치된 모습

참고, 구글 계정의 결제 주소를 영국으로 변경한다거나 하는 방식은 제대로 동작하지 않았습니다. 스토어의 국가를 바꾸는 것도 고려해볼 수 있는데요. 앱스토어와 달리 구글 플레이는 국가 변경을 1년에 한 번만 할 수 있기 때문에 테스트 방법으로 적합하지 않습니다.

그래서 플랜 B가 실행되었을까요?

결론부터 이야기하면 플랜 B는 실행되지 않았습니다. 하지만 전혀 예상치 못한 상황이 있기는 했습니다. Conditional delivery가 잘 동작한다는 데이터를 자체적으로 수집하기 전까지는 이 방법이 사용자의 국가를 알아낼 수 있는 완전한 대안이라고 단언할 수 없었기 때문에 영국을 지원하는 최초 출시 버전에서는 이 부분을 알아내는데 집중했습니다. 소수의 영국 베타 사용자들을 통해서 얻은 결과로 이 코드를 프로덕션에 배포해도 문제가 없겠다.라는 초기 판단의 근거를 마련했고, 결과적으로는 빅쿼리로 수집한 로그를 통해서 아래와 같은 데이터를 얻을 수 있었습니다.

영국 사용자들에게 countrycodegb 모듈이 잘 전달된 걸 확인할 수 있는 로그

어느날의 일부 데이터를 분석해본 결과 3,175,204명 중 Conditional delivery에 의해 자동으로 국가가 판정이 된 사용자는 3,170,493이었고, 이 중 국가 코드를 인식하지 못한 사용자는 4,710명이었습니다. 4,710명 중 Conditional delivery을 사용할 수 없는 킷캣 버전 사용자 4,638명을 제외하면 3,170,493 중 72명이 국가를 자동으로 인식하지 못했다는 것을 알 수 있습니다. 백분율로 나타내면 99.99773%는 자동으로 국가 판정을 하게 된 것인데, ‘이게 제대로 동작할까?’라는 막연함에서 오는 불안을 종식시킬 수 있는 수치였습니다. 하지만 전혀 예상하지 못한 데이터가 들어오는 경우가 있었는데요. 바로 이런 상황이었습니다.

몇몇 사용자에게는 countrycodekr과 countrycodegb가 동시에 전달된 걸 확인할 수 있는 로그

첫번째 컬럼인 delivered_country_code를 보면 어떤 사용자들에게는 KR, GB가 모두 로깅 되어있는 것을 볼 수 있습니다. 사용자의 국가가 한국이면서 동시에 영국이라고 판단하는 논리에 맞지 않는 예외가 발생하는 상황이었습니다. 이런 사용자가 564명이었는데 핫픽스를 통해 내부적으로는 delivered_country_code가 없는 사용자와 같은 방식으로 처리하도록 수정되었습니다. 정리하면 이전의 72명에 564명을 더해 총 636명은 당근마켓 앱을 처음 실행했을 때 국가 선택 스피너를 보게 되는 셈이었습니다. 여전히 99.98%의 사용자는 앱 번들의 Conditional delivery를 통해서 본인의 국가가 자동으로 판정되었다고 볼 수 있기 때문에 이정도의 에러율은 허용할 수 있는 수치라고 판단했습니다. 그리고 이런 에러 사용자는 국가 선택 스피너를 노출하는 형태로 우회 처리할 수 있기 때문에 에러의 경중을 따지자면 중요도가 낮은 문제로 격하되었다고 볼 수 있습니다.

오히려 조금 더 까다로운 상황이 있었습니다. 파이어베이스의 자동 수집 이벤트인 사용자 IP 기반의 국가 정보와 delivered_country_code의 국가가 일치하지 않는 경우입니다. IP 기준으로는 한국인데, Conditional delivery는 영국 사용자로 판단하는 상황인데요. 데이터에 의하면 이런 사용자가 0.01% 미만이기는 했지만 유럽과 같이 국가들이 지리적으로 인접해있고, 국가간 이동이 자유로운 곳으로 당근마켓이 진출하게 되면 에러율이 조금 더 높아질 수 있지 않을까?라는 예상을 해봅니다. 그런 경우, 앱 내 설정 어딘가에 국가를 변경할 수 있는 옵션을 제공하는 게 자연스러울 것 같다는 생각도 드네요.

마치며

개발자에게 새로운 기술만큼이나 흥미와 호기심을 자극하는 게 있을까 싶습니다만 MAU가 900만(MAU 자랑하고 싶었어요🥳) 정도되는 실세계 앱에 필드 테스트까지 해야 하는 상황에 놓이게 되면 피상적인 감상만으로는 접근하기 쉽지 않은 형편이 생기기 마련입니다. 더군다나 Trial and error를 통해 결과를 예상하고 분석하고 코드를 수정하고 다시 결과를 기다리는 비교적 긴 과정이 의지를 갉아먹기 쉽기도 하고요. 그런데 이런 문제를 해결했을 때의 성취감이 또 대단하기는 해서 개발의 즐거움이라면 수십가지를 얘기할 수 있겠지만 이번 포스트에서 만큼은 이런 목적을 달성했을 때의 느낌을 줄곧 떠올리며 글을 작성했습니다. 당근마켓은 작년 11월 영국의 사우샘프턴을 시작으로 해외 거점 도시를 만들기 위해 새로운 도전을 하고 있습니다. 그리고 함께 할 안드로이드 개발자도 찾고 있어요. 이 글을 읽고 있는 누군가와 함께 일하게 될 상상을 하며 이번 글을 마칩니다.

--

--

한로니
당근 테크 블로그

컴퓨터 앞에 앉아있는 시간이 많지만, 어째서인지 자전거를 타고 세계 곳곳을 여행하는 상상을 종종하는 괴발자 🤞