Google Play In-App Review API 연결하기
Google Play In-App Review API
Google Play In-App Review API를 사용하면 앱 또는 게임을 종료하는 불편함 없이 Play 스토어 평점 및 리뷰를 제출하도록 요청하는 메시지를 사용자에게 표시할 수 있습니다.
서비스 중인 Ear Alarm 앱은 Play Store에 출시되어 있습니다.
2022.4.14. 에 출시를 시작한 서비스로 다운로드 수는 2500을 넘어가지만, 그에 비해 사용자 리뷰 수는 굉장히 적었습니다.
사용자가 앱을 사용하는 동안 인앱 리뷰 흐름을 통해서 쉽게 리뷰를 등록할 수 있도록 개선하고 적용하는 과정을 정리하였습니다.
인앱 리뷰 요청 시기
공식 문서에서는 다음과 같은 가이드라인을 통해 인앱 리뷰를 요청하는 시기를 알려주고 있습니다.
사용자가 앱 또는 게임을 충분히 사용해 유용한 의견을 제공할 수 있을 때 인앱 리뷰 흐름을 트리거합니다.
사용자에게 리뷰를 요청하는 메시지를 과도하게 표시해서는 안 됩니다. 이 접근 방식을 사용하면 사용자 불만을 최소화하고 API 사용을 제한할 수 있습니다.
우수한 사용자 환경을 제공하기 위해 Google Play는 사용자에게 리뷰 대화상자를 표시할 수 있는 빈도에 관한 시간제한 할당량을 적용합니다. 이 할당량으로 인해 짧은 기간(예: 1개월 미만) `launchReviewFlow` 메서드를 두 번 이상 호출할 경우 대화상자가 표시되지 않을 수도 있습니다.
할당량은 변경될 수 있으므로 자체 로직을 적용하고 리뷰를 요청하는 최적의 순간을 타겟팅하는 것이 중요합니다. 예를 들어
API를 트리거하는 클릭 유도 문구 옵션 (예: 버튼)이 없어야 합니다. 사용자가 이미 할당량을 초과했을 수 있고 흐름이 표시되지 않아 사용자에게 중단된 환경이 표시될 수 있기 때문입니다. 이 사용 사례의 경우 사용자를 대신 Play 스토어로 리디렉션합니다.
앱은 사용자에게 평점 버튼 또는 카드를 표시하기 전이나 표시하는 동안 사용자 의견 관련 질문 (예: '앱이 마음에 드십니까?') 또는 예측 질문 (예: '이 앱을 별 5개로 평가하시겠습니까?')을 포함하여 어떤 질문도 해서는 안 됩니다.
간단하게 정리하면 다음과 같습니다.
- 앱을 충분히 사용한 시점에서 API를 호출해야 한다.
- 할당량이 정해져 있기 때문에 과도하게 API를 호출하면 안 된다. 할당량을 초과하면 리뷰 화면이 표시되지 않는다.
- 자체적으로 로직을 적용하여 리뷰 요청을 최적화해야 한다.
- 버튼이나 문구를 통해서 API를 트리거하면 안 된다. 버튼으로 리뷰 요청을 할 경우는 Play 스토어로 리디렉션 하도록 해야 한다.
- 리뷰의 높은 점수를 위해 사용자의 의견과 관련된 질문이나 예측 질문과 같은 어떤 질문도 하면 안 된다.
할당량이라는 부분이 중요한데, 구글에서는 할당량이 정확이 어느 정도인지 구체적으로 밝히고 있지 않습니다.
1개월 미만을 예시로 명시해 둔 부분을 참고했을 때, 1개월에 1번 API를 호출하는 로직이라면 문제없이 동작하는데 충분하다고 생각하면 될 것 같습니다.
개발 환경 설정
dependencies {
implementation("com.google.android.play:review:2.0.2")
implementation("com.google.android.play:review-ktx:2.0.2")
}
현재 프로젝트는 코틀린을 사용하기 때문에 review-ktx 만 추가해도 In-App Review API를 사용할 수 있습니다.
리뷰 요청이 필요한 module에 추가하였습니다.
기본 구현
공식문서의 내용을 살펴보면 간단한 코드와 로직을 통해서 API를 호출할 수 있습니다.
val manager = ReviewManagerFactory.create(context)
ReviewManagerFactory를 통해 ReviewManager를 생성하여 인앱 리뷰 Flow를 시작할 준비를 합니다.
val request = manager.requestReviewFlow()
request.addOnCompleteListener { task ->
if (task.isSuccessful) {
val reviewInfo = task.result
} else {
@ReviewErrorCode val reviewErrorCode = (task.getException() as ReviewException).errorCode
}
}
ReviewManager에서 requestReviewFlow을 생성하고, ReviewInfo 객체를 요청 및 반환합니다.
이때 ReviewInfo 객체는 제한된 시간 동안만 유효하기 때문에, 인앱 리뷰를 호출한다고 확신하는 경우에만 요청해야 합니다.
val flow = manager.launchReviewFlow(activity, reviewInfo)
flow.addOnCompleteListener { _ ->
// The flow has finished. The API does not indicate whether the user
// reviewed or not, or even whether the review dialog was shown. Thus, no
// matter the result, we continue our app flow.
}
ReviewInfo의 정보를 바탕으로 launchReviewFlow를 통해 In-App Review API를 호출할 수 있습니다.
addOnCompleteListener를 추가하여 인앱 리뷰 흐름이 완전히 종료되었을 때 처리해야 하는 로직을 추가로 설정할 수 있습니다.
단, addOnCompleteListener는 사용자가 리뷰를 남겼을 때 동작하는 로직이 아닙니다.
사용자가 실제로 리뷰를 남겼는지, 남기지 않았는지, 혹은 리뷰 화면이 사용자에게 보여졌는지와는 상관없이 인앱 리뷰 흐름이 완료되었을 때 수행되는 부분으로 리뷰 결과와 상관없이 앱은 계속 진행될 수 있도록 해야 합니다.
실제 구현
인앱 리뷰 요청이 이루어질 첫 화면에서 구현하였습니다.
Compose로 작성된 화면에서 Side Effect를 Composable로 분리하여 Screen과 InApp리뷰 화면을 분리하여 깔끔하게 구조를 만들 수 있도록 고민하였습니다.
@Composable
internal fun InAppReview(
lastReviewDate: ZonedDateTime?,
setLastReviewDate: () -> Unit,
) {
val context = LocalContext.current
val activity = context as? Activity
LaunchedEffect(lastReviewDate) {
val now = ZonedDateTime.now(ZoneOffset.UTC)
if (lastReviewDate == null) {
setLastReviewDate()
} else {
if (lastReviewDate.plusMonths(1).isBefore(now)) {
val manager = ReviewManagerFactory.create(context)
val request = manager.requestReviewFlow()
request.addOnCompleteListener { task ->
if (task.isSuccessful) {
val reviewInfo = task.result
activity?.let {
val flow = manager.launchReviewFlow(it, reviewInfo)
flow.addOnCompleteListener { _ ->
// log
}
}
setLastReviewDate()
} else {
@ReviewErrorCode val reviewErrorCode =
(task.exception as ReviewException).errorCode
val message = (task.exception as ReviewException).message
// log
}
}
}
}
}
}
DataStore에 lastReviewDate를 저장하는 `setLastReviewDate()`함수를 사용합니다.
앱 첫 실행 시 null값으로 저장되어 있는 lastReviewDate값을 현재 시간으로 초기화하게 됩니다.
이후에는 lastReviewDate값을 기준으로 한 달이 지났는지 판단합니다.
lastReviewDate값을 기준으로 한 달이 지난 상태에서 앱을 실행한다면, 인앱 리뷰 요청을 시작합니다.
reviewInfo를 가져오지 못한다면 ReviewError를 Firebase Crashlytics에 로그를 남기고,
성공적으로 가져온다면 reviewInfo를 기반으로 인앱 리뷰 흐름을 시작합니다.
인앱 리뷰 흐름이 성공적으로 완료되면, 리뷰 흐름이 성공적으로 종료되었음을 Firebase Analytics에 로그를 남깁니다.
또한 인앱 리뷰가 사용자에게 제공되었는지와는 상관없이 리뷰 요청이 수행될 때, 다시 lastReviewDate값을 갱신합니다.
@Composable
private fun MainScreenContent(
...
mainUiState: MainUiState,
setLastReviewDate: () -> Unit,
...
) {
...
InAppReview(
lastReviewDate = mainUiState.lastReviewDate,
setLastReviewDate = setLastReviewDate
)
...
}
MainScreenContent에 다음과 같이 적용하면서 첫 화면에 진입할 때 작성한 InAppReview 로직이 수행되어 한 달에 한번 인앱 리뷰 요청을 수행하게 됩니다.
추가적으로 PlayStore에 설치된 앱에서만 인앱 리뷰 요청을 수행할 수 있기 때문에, 다른 스토어에서 다운로드한 앱에서는 인앱 리뷰가 제대로 동작하지 않습니다. context의 packageManager의 함수 중 getInstallSourceInfo을 통해 설치 경로를 판별하여 다른 스토어에서 다운로드한 앱의 경우 인앱 리뷰 요청을 하지 않도록 분기할 수 있습니다.
테스트 & 할당량
인앱 리뷰 기능은 일반적으로 안드로이드 스튜디오에서 에뮬레이터나 디버그 기기에 설치하여 테스트하기에는 어렵습니다.
할당량이 정해져 있기 때문에 짧은 시간에 여러 번 호출하면 인앱 리뷰 화면이 보이지 않는 문제가 발생하기 때문입니다.
그 외에도 발생할 수 있는 문제를 공식문서에서는 다음과 같이 설명합니다.
위의 문제 중 하나라도 발생한다면 인앱 리뷰 요청이 일어나더라도, 리뷰 화면이 출력되지 않습니다.
FakeReviewManager
인앱 리뷰 테스트를 위해 공식문서에서는 FakeReviewManager를 사용하는 방법도 명시하고 있습니다.
하지만, FakeReviewManager를 사용하였을 때 실제 앱을 실행시켜 테스트하면 리뷰 화면은 보이지 않고 API를 모조한 것에 불과하기 때문에 단위 테스트나 통합 테스트용으로 사용해야 합니다.
내부 테스트
테스트를 하기 위해서는 내부 테스트와 내부 앱 공유를 통해 할당량에 제한 없이 테스트할 수 있습니다.
Play Console에서 테스트 및 출시 - 테스트 - 내부 테스트를 선택합니다.
테스터 목록을 만들고 테스트할 이메일을 추가 후, 스마트폰 기기에서 테스트 참여 링크를 실행합니다.
테스트 참여 링크에서 테스트 초대를 받습니다.
내부 테스트에서 서명된 앱 번들을 업로드합니다.
Play Store에 해당 테스트 이메일로 접속하여 확인하면, 내부 테스터임으로 지정되어 있음을 알 수 있고, 리뷰도 개발자에게 비공개 의견을 전달하는 것으로 변경됩니다.
단, 테스터에 등록되었다고 바로 바뀌는 것은 아니라 새로운 버전의 내부테스트가 업로드될 때까지 3분~10분 정도 반영되는 시간이 있었던 것 같습니다.
앱을 다운로드하고 인앱 리뷰 테스트를 해보면 인앱 리뷰가 정상적으로 동작하는 것을 확인할 수 있었습니다.
코드를 수정해서 1달에 한번 리뷰 요청을 하는 것이 아닌, 30초에 한 번 요청하는 것으로 수정하여 테스트를 진행하여도 할당량에 제한 없이 계속 리뷰 요청이 정상적으로 이루어지는 것을 확인할 수 있었습니다.
위에서 알아본 것과 같이, 리뷰가 등록된 상태에서 다시 리뷰 요청이 이루어지면, 리뷰 요청 화면이 나오지 않습니다.
그렇기 때문에 리뷰가 등록되어 있는 상태에서 한 달에 한번 리뷰 요청을 수행해도 중복해서 리뷰 요청 화면이 나오지 않기 때문에 문제가 없음을 알 수 있습니다.
리뷰가 등록된 상태에서 리뷰 화면이 뜨지 않는 것을 테스트하고, 계속 테스트를 진행하기 위해서는 등록된 리뷰를 삭제한 후 다시 동작을 확인하면 리뷰 요청 화면이 제대로 다시 표시되는 것을 확인할 수 있습니다.
내부 앱 공유
내부 앱 공유를 통해서도 할당량에 제한 없이 테스트할 수 있습니다.
Play Console에서 테스트 및 출시 - 테스트 - 내부 앱 공유를 선택합니다.
내부테스트와 비슷하게 테스터 목록을 만들고 테스트 할 이메일을 추가 후 하단에 테스트 참여 대상 관리를 설정합니다.
업로더 관리 부분의 링크에 접속하여 앱을 업로드하고, 업로드된 앱의 링크를 스마트폰 기기에서 접속합니다.
이런 화면으로 연결되며 내부 앱 공유를 통해 업로드한 버전을 설치할 수 있습니다.
이것도 마찬가지로 업로드되고, 실제로 다운로드할 수 있는 상태가 되기까지 충분한 시간이 걸렸습니다. 하지만 내부 테스트보다는 빨리 다운로드할 수 있었던 것 같습니다.
내부 앱 공유로 앱을 설치하고, 리뷰 요청을 테스트해 보면 마찬가지로 정상적으로 동작하는 것을 확인할 수 있었습니다.
단, 내부 앱 공유로 앱을 설치했을 경우, 실제로 리뷰를 제출하는 것까지는 테스트할 수 없습니다. 화면에서도 제출 버튼이 비활성화되어 있는 것을 확인할 수 있습니다.
공식 문서에서도 이를 명시하고 있으며, 다른 테스트 트랙에서 발생하는 인증 일부를 건너뛰면서 변경사항을 빠르게 테스트할 수 있다고 알려주고 있습니다.
결과적으로 내부 앱 공유를 통해 인앱 리뷰를 개발하고 빠르게 디버깅하며 테스트해 보고, 기능 개발 완료 단계에 도달했을 때 내부 테스트를 활용하여 최종 테스트를 진행하면 좋을 것 같습니다.
할당량 저장
빠르게 개발을 진행하다 보니, 내부 테스트, 내부 앱 공유 업로드 과정에서 Play Store의 동작이 이상해져서 Play Store의 앱 데이터를 삭제하고 다시 실행한 후, 안드로이드 스튜디오에서 앱을 설치하는 등 여러 가지 상황이 꼬이다 보니 할당량이 갱신되는 경우가 있었습니다.
이와 관련해서 비슷한 사례가 있는 블로그 글을 찾아보니 할당량 데이터는 Google Play Store의 로컬 데이터에 저장되어 있는 것을 알 수 있었습니다.
내부 테스트, 내부 앱 공유를 사용하지 않고 Play Store의 로컬 데이터를 지워가면서 테스트를 할 수도 있겠지만, 이 방법보다는 확실하게 내부 테스트와 내부 앱 공유를 사용하여 테스트하는 것이 훨씬 더 효율적일 것 같습니다.
마무리
리뷰 요청을 앱 내에서 할 수 있다는 것은 장점도 있고, 단점도 있는 것 같습니다.앱이 완성도가 떨어진 상태에서 리뷰 요청을 하면 오히려 낮은 평점을 받을 확률이 높아지게 될지도 모릅니다.
또한, 리뷰를 요청하는 것 자체가 사용자에게 부정적인 느낌을 줄 수도 있습니다.
그 외에도 UI를 새롭게 추가하여 PlayStore로 리디렉션 하는 방법도 있겠지만 이 방법 역시 중간에 사용자가 이탈할 가능성이 높다는 문제가 가장 큰 것 같습니다.
인앱 리뷰는 여러 가지 제약이 있지만, 그럼에도 불구하고 앱을 종료하지 않고 흐름을 유지하면서 앱 리뷰를 받을 수 있다는 점에서 단점을 다 커버할 정도로 매력적인 기능임에 틀림없다고 생각합니다.
물론, 인앱 리뷰 기능을 효과적으로 활용하기 위해서는 앞서 언급한 단점들을 최소화하려는 노력이 반드시 동반되어야 합니다. 사용자에게 충분한 가치를 제공하고 긍정적인 경험을 쌓은 후에, 적절한 빈도로 리뷰를 요청하는 것이 중요하며, 만족스러운 경험을 제공한 직후에 리뷰 요청을 하는 등의 요청 방식도 고려할 수 있을 것 같습니다.
결국 인앱 리뷰는 어떻게 사용하느냐에 따라 그 효과가 극명하게 달라질 수 있는 양날의 검과 비슷한 것 같습니다. 사용자와 긍정적인 소통의 창구로 활용한다면, 분명 앱의 성장과 발전에 큰 도움이 될 것입니다.
인앱 리뷰를 도입한 후 리뷰에 어떤 변화가 발생하였는지는 추후에 새로운 글을 작성해 볼 생각입니다.
전체 코드는 여기를 참고해 주세요.
참고자료
https://developer.android.com/guide/playcore/in-app-review?hl=ko
Google Play In-App Review API | Android Developers
이 페이지는 Cloud Translation API를 통해 번역되었습니다. Google Play In-App Review API 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Google Play In-App Review API를 사용하
developer.android.com
https://devocean.sk.com/blog/techBoardDetail.do?ID=166873&boardType=techBlog
에이닷에 인앱리뷰 적용하고 별점 앞자리가 달라졌다?!
devocean.sk.com
https://velog.io/@kk_jang93/Android-In-App-Review
[Android] Android In-App Review
2024.05.20 문서 작성 공식 가이드 문서에 의한 기능 흐름 순서도사용자 의견 요청 API 호출 ( 성공 or 실패 )사용자 의견 요청 팝업 표시평가 및 리뷰 작성완료Google 공식 문서에 따른 In-App Review 개발
velog.io
[Android/IOS] 인앱 리뷰 기능 추가하기
요약 리뷰 API를 호출하더라도, 할당량에 따라 호출되지 않을 수 있음 (Android 1달에 2번, IOS 1년에 3번) 사용자가 앱을 충분히 경험한 후 & 경험이 단절되지 않는 위치에 적절히 넣어야 함 리뷰를 무
lamerry.tistory.com