Android 앱 인앱 업데이트와 리뷰 요청 구현 — Play In-App Updates, In-App Review API와 버전 관리
Android 앱을 Google Play에 출시했다고 해서 개발이 끝나는 것은 아닙니다.
앱을 운영하다 보면 버그 수정, 기능 개선, 데이터 업데이트, 보안 패치 등을 위해 새로운 버전을 배포하게 됩니다.
그런데 사용자가 앱을 항상 최신 버전으로 유지하는 것은 아닙니다.
Google Play 자동 업데이트가 비활성화되어 있거나, 업데이트가 아직 설치되지 않았거나, 사용자가 직접 업데이트를 미루는 경우도 있습니다.
또한 앱을 사용하는 사람들에게 Google Play 리뷰를 요청하고 싶을 수도 있습니다.
이때 활용할 수 있는 기능이 다음 두 가지입니다.
Play In-App Updates: 사용자가 앱을 종료하고 Play 스토어를 직접 찾아가지 않아도 업데이트 흐름을 시작할 수 있도록 지원
Play In-App Review: 앱 내부에서 Google Play 리뷰 작성 흐름을 요청
두 기능은 모두 Google Play에서 제공하는 라이브러리를 통해 사용할 수 있습니다.
하지만 사용법을 잘못 이해하면 업데이트 안내가 반복되거나, 리뷰 요청 API를 호출했는데 아무 화면도 나타나지 않는 문제가 발생할 수 있습니다.
이번 글에서는 이러한 문제를 예방하면서 실제 앱에 적용하는 방법을 살펴보겠습니다.
작성 기준: 2026년 10월. Kotlin, XML, AndroidX Activity Result API, minSdk 24 이상을 기준으로 설명합니다. Play Core 라이브러리 버전과 Google Play 정책은 변경될 수 있으므로 배포 전 공식 문서를 확인해야 합니다.
1. 인앱 업데이트란?
인앱 업데이트는 사용자가 앱을 사용하는 도중 새로운 버전으로 업데이트하도록 안내하는 기능입니다.
일반적인 업데이트 흐름은 다음과 같습니다.
사용자 앱 실행
↓
Google Play에서 새 버전 확인
↓
Play 스토어로 이동
↓
업데이트 버튼 선택
↓
업데이트 설치
↓
앱 재실행인앱 업데이트를 사용하면 앱에서 업데이트 절차를 시작할 수 있습니다.
사용자 앱 실행
↓
업데이트 가능 여부 확인
↓
업데이트 유형 결정
↓
Google Play 업데이트 UI
↓
다운로드 및 설치중요한 점은 앱이 APK 파일을 직접 다운로드하고 설치하는 방식이 아니라 Google Play가 제공하는 업데이트 절차를 이용한다는 것입니다.
따라서 일반적인 Play 배포 앱에서 직접 APK 설치 로직을 구현할 필요가 없습니다.
2. Play In-App Updates의 두 가지 방식
Google Play는 크게 두 가지 업데이트 방식을 제공합니다.
| 구분 | Flexible Update | Immediate Update |
|---|---|---|
| 한국어 표현 | 유연한 업데이트 | 즉시 업데이트 |
| 다운로드 중 앱 사용 | 가능 | 일반적으로 업데이트 흐름이 화면을 차지 |
| 사용자 경험 | 비교적 자연스러움 | 업데이트를 강하게 유도 |
| 설치 시점 | 다운로드 완료 후 설치 요청 | Play 업데이트 흐름에서 처리 |
| 추천 상황 | 일반적인 기능 개선 | 중요한 수정이 필요한 상황 |
두 방식 중 어느 것이 적합한지는 업데이트의 중요도에 따라 결정해야 합니다.
Flexible Update
앱을 사용하면서 업데이트 파일을 다운로드할 수 있습니다.
다운로드가 완료되면 앱에서 설치를 진행하도록 안내할 수 있습니다.
Immediate Update
업데이트 과정이 앱 화면을 대신하는 형태로 진행됩니다.
보안 문제나 심각한 오류 수정처럼 빠른 업데이트가 중요한 경우 검토할 수 있습니다.
다만 즉시 업데이트도 사용자의 취소나 실패 가능성을 고려해야 합니다.
3. 어떤 업데이트 방식을 선택해야 할까?
모든 업데이트에 Immediate Update를 적용하는 것은 권장하지 않습니다.
예를 들어 다음과 같이 구분할 수 있습니다.
| 업데이트 내용 | 추천 방식 |
|---|---|
| UI 디자인 개선 | Flexible |
| 새로운 검색 기능 추가 | Flexible |
| 이미지 로딩 성능 개선 | Flexible |
| 일반적인 버그 수정 | Flexible |
| 데이터 손상 가능성 수정 | Immediate 검토 |
| 심각한 보안 취약점 수정 | Immediate 검토 |
| 서버 API 호환성 문제 | 상황에 따라 Immediate 검토 |
즉시 업데이트를 사용하더라도 업데이트가 반드시 완료된다고 보장할 수는 없습니다.
따라서 서버 API 호환성 문제처럼 이전 버전이 더 이상 정상 작동할 수 없는 상황에서는 서버 측 호환성 정책과 최소 지원 버전 관리도 함께 설계해야 합니다.
Play In-App Updates 구현
4. Gradle 의존성 추가
앱 모듈의 build.gradle.kts에 Play In-App Updates 라이브러리를 추가합니다.
dependencies {
implementation(
"com.google.android.play:app-update:<version>"
)
implementation(
"com.google.android.play:app-update-ktx:<version>"
)
}<version>에는 공식 문서에서 확인한 안정 버전을 입력합니다.
라이브러리 버전을 예제에서 임의로 고정하지 않은 이유는 Google Play SDK의 지원 버전과 Android targetSdk 요구사항이 변경될 수 있기 때문입니다.
또한 최신 Play 라이브러리와 프로젝트의 compileSdk, Kotlin 버전이 호환되는지 확인해야 합니다.
5. AppUpdateManager 생성
업데이트 상태를 확인하려면 AppUpdateManager를 사용합니다.
val appUpdateManager =
AppUpdateManagerFactory.create(
applicationContext
)이 객체를 이용해 업데이트 가능 여부를 조회할 수 있습니다.
appUpdateManager.appUpdateInfo
.addOnSuccessListener { appUpdateInfo ->
// 업데이트 상태 확인
}
.addOnFailureListener { exception ->
// 조회 실패 처리
}이때 조회 실패를 앱 실행 실패로 연결해서는 안 됩니다.
Google Play 서비스 상태나 네트워크 환경 등에 따라 업데이트 정보를 가져오지 못할 수 있습니다.
6. 업데이트 가능 여부 확인
업데이트 상태는 UpdateAvailability로 확인합니다.
appUpdateManager.appUpdateInfo
.addOnSuccessListener { info ->
if (
info.updateAvailability() ==
UpdateAvailability.UPDATE_AVAILABLE
) {
// 업데이트 가능
}
}하지만 업데이트가 가능하다는 사실만으로 원하는 업데이트 방식을 사용할 수 있는 것은 아닙니다.
다음 API도 확인해야 합니다.
info.isUpdateTypeAllowed(
AppUpdateType.FLEXIBLE
)즉시 업데이트는 다음과 같이 확인합니다.
info.isUpdateTypeAllowed(
AppUpdateType.IMMEDIATE
)Google Play에서 허용하는 업데이트 방식만 실행해야 합니다.
7. Activity Result API로 업데이트 실행하기
최신 구현에서는 Activity Result API를 활용할 수 있습니다.
먼저 업데이트 결과를 받을 런처를 등록합니다.
private val updateLauncher =
registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
when (result.resultCode) {
Activity.RESULT_OK -> {
// 업데이트 흐름의 결과 처리
}
Activity.RESULT_CANCELED -> {
// 사용자가 취소했을 수 있음
}
else -> {
// 실패 또는 기타 결과
}
}
}다만 이 런처는 IntentSenderRequest를 직접 실행하는 계약입니다.
Play In-App Updates에서 제공하는 업데이트 시작 API와 연결할 때는 라이브러리에서 지원하는 오버로드를 확인해야 합니다.
현재 Play In-App Updates API는 ActivityResultLauncher<IntentSenderRequest>를 받는 업데이트 시작 방식을 제공합니다.
8. Flexible Update 시작하기
유연한 업데이트를 시작하는 기본 구조는 다음과 같습니다.
private fun startFlexibleUpdate(
info: AppUpdateInfo
) {
val options =
AppUpdateOptions.newBuilder(
AppUpdateType.FLEXIBLE
).build()
try {
appUpdateManager.startUpdateFlowForResult(
info,
updateLauncher,
options
)
} catch (exception: IntentSender.SendIntentException) {
// 업데이트 시작 실패 처리
}
}위 코드는 appUpdateManager와 updateLauncher가 Activity에서 초기화되어 있다고 가정합니다.
사용하는 Play 라이브러리 버전에 따라 예외 및 메서드 시그니처를 확인해야 합니다.
업데이트 시작에 실패하더라도 앱 자체는 계속 사용할 수 있도록 처리하는 것이 좋습니다.
9. Immediate Update 시작하기
즉시 업데이트도 비슷한 방식으로 실행할 수 있습니다.
private fun startImmediateUpdate(
info: AppUpdateInfo
) {
val options =
AppUpdateOptions.newBuilder(
AppUpdateType.IMMEDIATE
).build()
appUpdateManager.startUpdateFlowForResult(
info,
updateLauncher,
options
)
}실제 구현에서는 업데이트 유형이 허용되는지 먼저 확인하고, 실패 상황을 처리해야 합니다.
또한 앱의 모든 화면에서 반복적으로 즉시 업데이트를 시작하지 않도록 주의해야 합니다.
10. AppUpdateInfo 객체 재사용 주의
업데이트를 시작할 때 사용하는 AppUpdateInfo는 한 번의 업데이트 흐름을 시작하는 데 사용하도록 설계되어 있습니다.
따라서 사용자가 업데이트를 취소한 뒤 다시 시작하려면 새로운 AppUpdateInfo를 요청하는 것이 안전합니다.
예를 들어 다음과 같은 흐름입니다.
업데이트 정보 요청
↓
AppUpdateInfo 수신
↓
업데이트 시작
↓
사용자 취소
↓
업데이트 정보 다시 요청
↓
재시도 여부 결정이전 객체를 계속 재사용하는 구조는 피하는 것이 좋습니다.
Flexible Update 완료 처리
11. Flexible Update의 중요한 특징
Flexible Update는 다운로드가 완료되었다고 해서 자동으로 앱 설치가 완료되는 것은 아닙니다.
일반적으로 다음 흐름을 처리해야 합니다.
업데이트 시작
↓
백그라운드 다운로드
↓
다운로드 완료
↓
사용자에게 설치 안내
↓
completeUpdate()
↓
앱 업데이트 완료따라서 다운로드 상태를 관찰하는 코드가 필요합니다.
12. InstallStateUpdatedListener 등록
업데이트 설치 상태를 관찰하려면 Listener를 사용할 수 있습니다.
private val installStateListener =
InstallStateUpdatedListener { state ->
when (state.installStatus()) {
InstallStatus.DOWNLOADING -> {
// 다운로드 진행 중
}
InstallStatus.DOWNLOADED -> {
showUpdateDownloadedMessage()
}
InstallStatus.FAILED -> {
// 다운로드 실패 처리
}
}
}Activity에서 Listener를 등록합니다.
appUpdateManager.registerListener(
installStateListener
)더 이상 필요하지 않을 때는 등록을 해제해야 합니다.
appUpdateManager.unregisterListener(
installStateListener
)Activity 생명주기와 Listener 등록·해제 시점을 함께 관리해야 합니다.
13. 다운로드 진행률 표시
다운로드 중에는 설치 상태에서 진행률을 계산할 수 있습니다.
val downloaded =
state.bytesDownloaded()
val total =
state.totalBytesToDownload()
if (total > 0L) {
val progress =
(downloaded * 100L / total).toInt()
binding.progressBar.progress = progress
}이 코드는 InstallStatus.DOWNLOADING 상태에서 실행하는 것을 가정합니다.
다운로드 크기를 확인할 수 없는 상황도 있으므로 total > 0L 조건을 확인하는 것이 좋습니다.
또한 진행률 표시가 반드시 필요한 것은 아닙니다.
일반적인 앱에서는 다운로드 완료 시점에 설치 안내를 제공하는 방식만으로도 충분할 수 있습니다.
14. 다운로드 완료 후 설치 안내
Flexible Update가 다운로드되면 사용자에게 설치를 안내할 수 있습니다.
예를 들어 Snackbar를 사용합니다.
Snackbar.make(
binding.root,
"업데이트 다운로드가 완료되었습니다.",
Snackbar.LENGTH_INDEFINITE
)
.setAction("설치") {
appUpdateManager.completeUpdate()
}
.show()completeUpdate()는 업데이트 설치 절차를 진행하도록 요청합니다.
앱이 전경에 있는 경우에는 업데이트 완료 과정에서 앱이 재시작될 수 있습니다.
따라서 사용자에게 작성 중인 데이터가 있다면 저장하도록 안내하는 것이 좋습니다.
15. 앱 재실행 후 다운로드 완료 상태 확인
사용자가 업데이트 다운로드를 완료했지만 설치하지 않은 상태에서 앱을 종료할 수 있습니다.
따라서 앱을 다시 실행했을 때 다운로드 완료 상태를 확인해야 합니다.
private fun checkDownloadedUpdate() {
appUpdateManager.appUpdateInfo
.addOnSuccessListener { info ->
if (
info.installStatus() ==
InstallStatus.DOWNLOADED
) {
showUpdateDownloadedMessage()
}
}
}이렇게 하면 사용자가 설치를 미룬 업데이트를 다시 안내할 수 있습니다.
다만 안내를 지나치게 자주 표시하지 않도록 사용자 경험을 고려해야 합니다.
Immediate Update 처리
16. 진행 중인 즉시 업데이트 재개
Immediate Update를 진행하던 중 앱이 종료되거나 업데이트 흐름이 중단될 수 있습니다.
이 경우 다음 상태를 확인할 수 있습니다.
appUpdateManager.appUpdateInfo
.addOnSuccessListener { info ->
if (
info.updateAvailability() ==
UpdateAvailability
.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS
) {
// 진행 중인 즉시 업데이트 재개 검토
}
}이 상태에서는 새로운 업데이트 정보를 이용해 즉시 업데이트 흐름을 다시 시작할 수 있습니다.
다만 사용자가 취소한 모든 상황에서 무조건 재실행하는 방식은 피해야 합니다.
17. Immediate Update를 강제로 적용할 수 있을까?
인앱 업데이트 API를 사용한다고 해서 앱 개발자가 Google Play 업데이트를 완전히 강제할 수 있는 것은 아닙니다.
사용자는 업데이트를 취소할 수 있으며, 네트워크나 기기 상태 때문에 설치가 실패할 수도 있습니다.
따라서 반드시 최신 버전이 필요한 서비스라면 서버와 앱 사이에 별도의 버전 호환성 정책을 설계해야 합니다.
예를 들어 서버에서 최소 지원 버전을 제공할 수 있습니다.
{
"minimumSupportedVersionCode": 120,
"recommendedVersionCode": 125
}앱은 현재 버전과 비교하여 안내 화면을 표시할 수 있습니다.
하지만 서버 응답만으로 APK 설치가 이루어지는 것은 아닙니다.
실제 업데이트는 Google Play를 통해 진행해야 합니다.
앱 버전 관리
18. versionCode와 versionName의 차이
Android 앱에는 대표적으로 두 가지 버전 정보가 있습니다.
defaultConfig {
versionCode = 125
versionName = "1.2.5"
}각 의미는 다음과 같습니다.
| 항목 | 설명 |
|---|---|
| versionCode | Android 및 Google Play가 버전 순서를 판단하는 정수 |
| versionName | 사용자에게 표시되는 버전 문자열 |
일반적으로 새로운 버전을 Google Play에 배포할 때는 versionCode를 증가시켜야 합니다.
반면 versionName은 사용자에게 보여주는 표현이므로 프로젝트의 버전 정책에 따라 관리할 수 있습니다.
19. 추천 버전 관리 방식
개인 개발자가 여러 앱을 운영한다면 버전 정책을 통일하는 것이 편리합니다.
예를 들어 다음과 같이 사용할 수 있습니다.
1.0.0 최초 출시
1.0.1 버그 수정
1.1.0 기능 추가
1.2.0 기능 개선
2.0.0 큰 구조 변경이 방식은 일반적인 Semantic Versioning 개념을 참고한 것입니다.
하지만 Android의 versionCode는 별도로 증가시켜야 합니다.
예를 들어 다음과 같습니다.
versionCode = 126
versionName = "1.2.6"Google Play는 버전 문자열이 아니라 버전 코드와 배포 조건을 기준으로 업데이트 가능 여부를 판단합니다.
20. Product Flavor별 버전 관리
여러 Product Flavor를 사용하는 프로젝트에서는 버전 관리에 주의해야 합니다.
예를 들어 다음과 같은 구조입니다.
flavorDimensions += "animal"
productFlavors {
create("bird") {
dimension = "animal"
applicationId = "com.example.koreanbirds"
}
create("fish") {
dimension = "animal"
applicationId = "com.example.koreanfish"
}
}각 Flavor가 서로 다른 applicationId를 사용한다면 Google Play에서는 별도 앱으로 관리됩니다.
따라서 버전 코드와 배포 일정도 앱별로 관리할 수 있습니다.
반대로 동일한 applicationId의 빌드 변형을 배포할 때는 버전 코드 충돌이 발생하지 않도록 주의해야 합니다.
업데이트 우선순위 관리
21. Google Play 업데이트 우선순위
Google Play Developer API를 이용한 배포 과정에서는 업데이트 우선순위를 지정할 수 있습니다.
인앱 업데이트 API에서는 다음 값을 확인할 수 있습니다.
info.updatePriority()업데이트 우선순위는 일반적으로 0부터 5까지의 값으로 표현됩니다.
| 값 | 의미 |
|---|---|
| 0 | 기본 우선순위 |
| 1~2 | 상대적으로 낮은 우선순위 |
| 3~4 | 높은 우선순위로 활용 가능 |
| 5 | 가장 높은 우선순위 |
다만 우선순위 값만으로 업데이트 방식이 자동 결정되는 것은 아닙니다.
앱에서 정책을 직접 구현해야 합니다.
22. 우선순위에 따라 업데이트 방식 선택
예를 들어 다음과 같이 구성할 수 있습니다.
private fun chooseUpdateType(
info: AppUpdateInfo
): Int? {
val priority = info.updatePriority()
return when {
priority >= 4 &&
info.isUpdateTypeAllowed(
AppUpdateType.IMMEDIATE
) -> AppUpdateType.IMMEDIATE
info.isUpdateTypeAllowed(
AppUpdateType.FLEXIBLE
) -> AppUpdateType.FLEXIBLE
else -> null
}
}이 코드는 예시 정책입니다.
실제 앱에서는 업데이트 내용, 사용자 작업 상태, 네트워크 환경 등을 함께 고려해야 합니다.
특히 Immediate Update를 실행하기 전에 사용자가 작성 중인 메모나 데이터가 있는지 확인하는 것이 좋습니다.
23. 업데이트가 가능한 지 며칠 지났는지 확인
Play In-App Updates에서는 업데이트가 가능해진 뒤 경과한 일수를 확인할 수 있습니다.
val days =
info.clientVersionStalenessDays()이 값은 nullable일 수 있습니다.
따라서 다음과 같이 처리해야 합니다.
if (days != null && days >= 7) {
// 업데이트 안내 강화 검토
}이 기능을 이용하면 업데이트가 출시된 직후에는 일반 안내를 표시하고, 오랫동안 업데이트하지 않은 사용자에게는 더 적극적으로 안내하는 정책을 만들 수 있습니다.
다만 업데이트 경과 일수와 우선순위는 별개의 정보입니다.
Play In-App Review 구현
24. In-App Review란?
In-App Review는 사용자가 앱을 사용하던 중 Google Play 리뷰 작성 흐름을 요청하는 기능입니다.
일반적으로 사용자가 Play 스토어를 직접 찾아가지 않고도 리뷰를 작성할 수 있도록 설계되었습니다.
하지만 중요한 제한이 있습니다.
In-App Review API를 호출했다고 해서 리뷰 작성 화면이 반드시 표시되는 것은 아닙니다.
Google Play는 내부 정책에 따라 리뷰 UI 표시 여부를 결정합니다.
따라서 API 호출 결과만으로 사용자가 리뷰를 작성했다고 판단해서는 안 됩니다.
25. Gradle 의존성 추가
앱 모듈에 Play Review 라이브러리를 추가합니다.
dependencies {
implementation(
"com.google.android.play:review:<version>"
)
implementation(
"com.google.android.play:review-ktx:<version>"
)
}<version>은 공식 문서에서 확인한 버전을 사용합니다.
In-App Updates와 In-App Review는 별도의 라이브러리입니다.
따라서 두 기능을 모두 사용한다면 각각 필요한 의존성을 추가해야 합니다.
26. ReviewManager 생성
리뷰 요청을 위해 ReviewManager를 생성합니다.
val reviewManager =
ReviewManagerFactory.create(
applicationContext
)그리고 리뷰 요청 정보를 가져옵니다.
reviewManager.requestReviewFlow()
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val reviewInfo = task.result
// 리뷰 흐름 실행
}
}리뷰 요청 정보 조회에 실패하더라도 앱 기능은 정상적으로 계속되어야 합니다.
27. 리뷰 작성 흐름 실행
리뷰 요청 정보를 받았다면 다음과 같이 실행할 수 있습니다.
private fun requestInAppReview() {
val manager =
ReviewManagerFactory.create(this)
manager.requestReviewFlow()
.addOnCompleteListener { requestTask ->
if (requestTask.isSuccessful) {
val reviewInfo =
requestTask.result
manager.launchReviewFlow(
this,
reviewInfo
).addOnCompleteListener {
// 리뷰 UI 표시 여부나
// 리뷰 작성 여부를 알 수 없음
}
}
}
}이 코드는 Activity 내부에서 실행하는 예제입니다.
가장 중요한 점은 launchReviewFlow()의 완료 콜백입니다.
이 콜백은 사용자가 리뷰를 작성했다는 의미가 아닙니다.
또한 리뷰 UI가 실제로 표시되었는지도 확인할 수 없습니다.
따라서 리뷰 작성 완료 메시지를 표시하거나 리뷰 작성 여부를 앱 내부에 저장하는 방식은 피해야 합니다.
28. 리뷰 요청은 언제 하는 것이 좋을까?
리뷰 요청은 사용자가 앱에서 긍정적인 경험을 한 뒤 자연스럽게 표시하는 것이 좋습니다.
예를 들어 다음과 같은 시점을 고려할 수 있습니다.
앱을 여러 차례 사용한 이후
주요 기능을 정상적으로 완료한 이후
사용자가 원하는 정보를 성공적으로 찾은 이후
앱에서 의미 있는 작업을 완료한 이후
반대로 다음 상황에서는 리뷰 요청을 피하는 것이 좋습니다.
앱 최초 실행 직후
로그인이나 권한 요청 직후
오류가 발생한 직후
사용자가 중요한 작업을 진행 중일 때
업데이트 설치 직후 사용 경험이 충분하지 않을 때
리뷰 요청의 핵심은 사용자의 작업을 방해하지 않는 것입니다.
29. 리뷰 요청 횟수를 앱에서 제한하기
Google Play 자체적으로 리뷰 UI 표시 빈도를 제한할 수 있습니다.
하지만 앱에서도 요청 시점을 관리하는 것이 좋습니다.
예를 들어 DataStore에 다음과 같은 정보를 저장할 수 있습니다.
review_prompt_last_requested_at그리고 일정 기간 동안 반복적으로 리뷰 요청 API를 호출하지 않도록 할 수 있습니다.
다만 이 값은 리뷰를 작성한 날짜가 아니라 앱이 리뷰 흐름을 요청한 날짜입니다.
Google Play API는 실제 리뷰 작성 여부를 앱에 알려주지 않기 때문입니다.
30. DataStore를 이용한 리뷰 요청 정책
예를 들어 다음과 같은 정책을 생각할 수 있습니다.
앱 실행 횟수 5회 이상
+
주요 기능 사용 경험 있음
+
최근 30일 이내 리뷰 요청 없음
↓
리뷰 요청 검토이 정책은 예시이며 Google Play의 공식 표시 기준은 아닙니다.
실제 구현에서는 다음과 같은 데이터가 필요할 수 있습니다.
data class ReviewPromptState(
val launchCount: Int = 0,
val lastRequestedAt: Long = 0L
)하지만 단순히 앱 실행 횟수만으로 리뷰 요청을 결정하는 것은 적절하지 않을 수 있습니다.
사용자가 앱에서 실제로 가치 있는 경험을 했는지도 함께 고려하는 것이 좋습니다.
31. 리뷰 요청 전에 별점 선택 Dialog를 보여줘도 될까?
일부 앱에서는 리뷰 요청 전에 다음과 같은 화면을 표시합니다.
앱이 마음에 드시나요?
★★★★★
[리뷰 작성]하지만 긍정적인 평가를 할 사용자만 Google Play 리뷰로 보내고, 부정적인 평가를 할 사용자는 다른 화면으로 보내는 방식은 리뷰 유도 정책 측면에서 문제가 될 수 있습니다.
따라서 사용자를 평가 성향에 따라 차별적으로 분기시키는 방식은 피하는 것이 좋습니다.
리뷰 요청은 중립적으로 제공하고, 앱 내 피드백 기능은 별도로 구성하는 것이 바람직합니다.
32. 리뷰 요청이 표시되지 않는 이유
In-App Review API를 호출했는데 아무 화면도 나타나지 않을 수 있습니다.
대표적인 이유는 다음과 같습니다.
Google Play의 내부 표시 빈도 제한
테스트 환경의 제한
앱 설치 출처
Google Play 계정 및 앱 상태
기기 환경
API 호출 시점
중요한 점은 리뷰 UI가 표시되지 않는 것이 반드시 오류는 아니라는 것입니다.
따라서 리뷰 화면이 나타나지 않았다고 해서 무한 재시도하는 로직을 구현해서는 안 됩니다.
33. In-App Review 테스트 방법
Google Play에서는 In-App Review 테스트를 위한 방법을 제공합니다.
대표적으로 다음 환경을 사용할 수 있습니다.
Google Play 내부 테스트 트랙
내부 앱 공유
FakeReviewManager를 이용한 테스트
다만 FakeReviewManager는 실제 Google Play 리뷰 UI를 표시하는 도구가 아닙니다.
앱의 리뷰 요청 코드 흐름을 테스트하기 위한 용도입니다.
따라서 실제 사용자에게 리뷰 UI가 어떻게 표시되는지 확인하려면 Google Play 테스트 환경에서 별도로 검증해야 합니다.
Google Play 테스트
34. In-App Updates 테스트 조건
Play In-App Updates는 일반적인 로컬 Debug APK 설치만으로 실제 업데이트 흐름을 완전히 테스트하기 어렵습니다.
테스트에는 Google Play가 관리하는 설치 및 업데이트 환경이 필요합니다.
대표적으로 다음과 같은 방식이 있습니다.
낮은 versionCode의 앱을 테스트 트랙에 배포
테스트 계정으로 앱 설치
더 높은 versionCode의 앱을 배포
업데이트가 제공되는 상태 확인
앱에서 In-App Updates 실행
실제 업데이트가 제공되기까지는 Google Play의 배포 및 반영 시간이 필요할 수 있습니다.
또한 테스트 계정, 앱 설치 출처, 서명 상태 등이 올바르게 구성되어야 합니다.
35. 내부 앱 공유를 이용한 테스트
Google Play 내부 앱 공유 기능을 이용하면 테스트용 빌드를 배포할 수 있습니다.
하지만 내부 앱 공유를 이용한 테스트는 일반적인 프로덕션 배포와 동작 조건이 다를 수 있습니다.
따라서 최종 배포 전에 내부 테스트 트랙 등 실제 Play 배포 흐름에 가까운 환경에서도 검증하는 것이 좋습니다.
특히 다음 항목을 확인해야 합니다.
applicationId 일치
서명 호환성
versionCode 증가
Google Play 설치 출처
업데이트 제공 여부
업데이트 유형 허용 여부
운영 중 발생하는 문제
36. 업데이트가 감지되지 않는 경우
다음 사항을 확인합니다.
| 증상 | 확인할 내용 |
|---|---|
| UPDATE_NOT_AVAILABLE | 실제로 더 높은 버전이 제공되는지 |
| 업데이트 조회 실패 | Play 환경과 네트워크 상태 |
| Debug 앱에서 동작하지 않음 | 설치 출처와 서명 |
| 테스트 계정에서만 실패 | 테스트 트랙 참여 상태 |
| 일부 기기에서만 실패 | 기기 호환성과 배포 대상 |
| 업데이트 UI가 열리지 않음 | isUpdateTypeAllowed 결과 |
| 업데이트 후 이전 화면이 남음 | Activity 재생성 및 프로세스 상태 |
업데이트 기능을 테스트할 때는 Google Play Console의 배포 상태를 함께 확인해야 합니다.
37. 업데이트 중 사용자 데이터 보호
앱 업데이트 과정에서는 Activity가 종료되거나 프로세스가 다시 시작될 수 있습니다.
따라서 사용자 데이터는 적절한 시점에 저장해야 합니다.
예를 들어 조류 도감 앱에서 메모를 작성하고 있다면 다음 사항을 고려해야 합니다.
메모 임시 저장
SQLite 트랜잭션 처리
ViewModel 상태 관리
SavedStateHandle 활용
업데이트 완료 후 데이터 유지 확인
특히 Flexible Update에서 설치를 시작하기 전에 작성 중인 데이터를 저장하도록 안내하는 것이 좋습니다.
업데이트 자체가 사용자 데이터를 삭제하는 것은 일반적인 동작이 아니지만, 앱의 데이터 마이그레이션 코드에 문제가 있다면 업데이트 이후 데이터 손상이 발생할 수 있습니다.
38. SQLite 데이터베이스 버전과 앱 업데이트
앱을 업데이트하면서 SQLite 스키마를 변경하는 경우에는 데이터베이스 마이그레이션을 함께 고려해야 합니다.
예를 들어 다음과 같은 변경입니다.
ALTER TABLE bird_memos
ADD COLUMN updated_at INTEGER;기존 사용자의 메모 데이터를 유지하면서 새로운 컬럼을 추가해야 합니다.
이때 앱 업데이트와 SQLite 데이터베이스 업데이트는 서로 다른 개념입니다.
Google Play 앱 업데이트
↓
새로운 APK 설치
↓
앱 실행
↓
SQLiteOpenHelper 버전 확인
↓
필요한 DB 마이그레이션
↓
기존 사용자 데이터 유지따라서 인앱 업데이트 기능을 구현했다고 해서 SQLite 데이터 마이그레이션 문제가 자동으로 해결되는 것은 아닙니다.
39. 업데이트 기능을 어디에 배치할까?
일반적인 앱에서는 다음 위치를 고려할 수 있습니다.
MainActivity의 초기 진입 시점
설정 화면의 '업데이트 확인' 메뉴
중요한 업데이트가 있을 때 안내 화면
앱 재진입 시 다운로드 완료 상태 확인
하지만 앱 실행 시마다 여러 화면에서 동시에 업데이트 정보를 요청하는 것은 피해야 합니다.
추천 구조는 다음과 같습니다.
MainActivity
│
▼
UpdateCoordinator
│
▼
AppUpdateManager
│
▼
Google Play업데이트 흐름은 Activity와 연결되는 부분이 많으므로 일반적인 데이터 Repository와 동일하게 설계하기보다 UI 생명주기를 고려한 별도 Coordinator로 관리하는 것이 적합할 수 있습니다.
40. In-App Updates와 In-App Review를 함께 사용할 때
업데이트 안내와 리뷰 요청을 동시에 표시해서는 안 됩니다.
예를 들어 다음과 같은 흐름은 좋지 않습니다.
앱 실행
↓
업데이트 요청
↓
리뷰 요청
↓
알림 권한 요청사용자에게 여러 종류의 시스템 UI가 연속적으로 표시되면 앱 사용 경험이 나빠질 수 있습니다.
따라서 다음과 같은 우선순위를 고려할 수 있습니다.
앱 실행
↓
중요한 업데이트 여부 확인
↓
필요하면 업데이트 안내
↓
사용자 주요 기능 이용
↓
적절한 시점에 리뷰 요청 검토리뷰 요청은 업데이트 안내보다 우선순위를 낮게 두는 것이 일반적으로 자연스럽습니다.
41. 실제 앱 운영 체크리스트
인앱 업데이트
Play In-App Updates 라이브러리 추가
AppUpdateManager 생성
업데이트 가능 여부 확인
업데이트 유형 허용 여부 확인
Flexible Update 구현
Immediate Update 필요성 검토
InstallStateUpdatedListener 등록
Listener 해제 처리
다운로드 진행 상태 처리
다운로드 완료 후 설치 안내
앱 재실행 시 다운로드 완료 상태 확인
진행 중인 Immediate Update 재개 처리
업데이트 취소 및 실패 처리
versionCode 증가 확인
Google Play 테스트 트랙 검증
인앱 리뷰
Play Review 라이브러리 추가
ReviewManager 생성
requestReviewFlow() 구현
launchReviewFlow() 구현
리뷰 UI 표시 여부를 가정하지 않음
리뷰 작성 완료 여부를 추정하지 않음
리뷰 요청 시점 정책 수립
과도한 반복 요청 방지
별점에 따른 차별적 리뷰 유도 방지
FakeReviewManager로 코드 흐름 테스트
Google Play 테스트 환경 검증
42. 마무리
Android 앱을 출시한 이후에는 업데이트와 사용자 피드백 관리가 중요합니다.
Play In-App Updates를 이용하면 사용자가 앱 내부에서 업데이트 절차를 시작할 수 있습니다.
또한 Play In-App Review를 이용하면 앱 사용 중 Google Play 리뷰 작성 흐름을 요청할 수 있습니다.
하지만 두 기능 모두 Google Play가 제공하는 시스템 흐름을 사용하므로 앱 개발자가 모든 동작을 직접 제어할 수 있는 것은 아닙니다.
특히 다음 원칙을 기억하는 것이 좋습니다.
일반적인 업데이트에는 Flexible Update를 우선 고려합니다.
중요한 수정이 필요한 경우 Immediate Update를 검토합니다.
업데이트 가능 여부와 업데이트 유형 허용 여부를 모두 확인합니다.
Flexible Update에서는 다운로드 완료 후 설치 절차를 처리합니다.
업데이트 취소와 실패 상황에서도 앱이 안정적으로 동작해야 합니다.
versionCode를 올바르게 관리합니다.
리뷰 요청 API 호출이 실제 리뷰 화면 표시를 보장하지는 않습니다.
사용자가 리뷰를 작성했는지 앱에서 알 수 없습니다.
리뷰 요청은 사용자가 의미 있는 경험을 한 뒤 자연스럽게 진행합니다.
실제 Google Play 테스트 환경에서 최종 검증합니다.
여러 앱을 운영하는 개인 개발자라면 업데이트와 리뷰 요청 기능을 공통 구조로 정리해 두는 것도 좋은 방법입니다.
다만 업데이트 정책과 리뷰 요청 시점은 앱의 특성에 맞게 조정해야 합니다.
인앱 업데이트와 인앱 리뷰의 핵심은 사용자를 강제로 움직이게 만드는 것이 아니라, 필요한 순간에 자연스럽게 다음 행동을 할 수 있도록 돕는 것입니다.
참고 자료
Android Developers — In-App Updates
https://developer.android.com/guide/playcore/in-app-updatesAndroid Developers — Kotlin In-App Updates
https://developer.android.com/guide/playcore/in-app-updates/kotlin-javaAndroid Developers — Test In-App Updates
https://developer.android.com/guide/playcore/in-app-updates/testAndroid Developers — In-App Review
https://developer.android.com/guide/playcore/in-app-reviewAndroid Developers — In-App Review Integration
https://developer.android.com/guide/playcore/in-app-review/kotlin-javaAndroid Developers — Test In-App Review
https://developer.android.com/guide/playcore/in-app-review/testAndroid Developers — Version Your App
https://developer.android.com/studio/publish/versioningGoogle Play Console Help
https://support.google.com/googleplay/android-developer/
태그: Android, Kotlin, Google Play, In-App Updates, In-App Review, Flexible Update, Immediate Update, AppUpdateManager, ReviewManager, versionCode, versionName, Google Play Console, Android 앱 업데이트, Android 앱 리뷰, Android 앱 운영