Conversation
WalkthroughFirebase Performance Monitoring을 앱에 추가하고, 이미지 로딩 및 홈 데이터 로드 작업에 성능 추적 기능을 구현했습니다. 앱 버전을 업그레이드하고(1.1.4 → 1.1.6), Gradle 빌드 및 매니페스트 설정을 업데이트했습니다. Changes
Possibly related issues
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeReportedAnimalCard.kt (1)
52-76:HomeProtectAnimalCard와 동일한 이슈: 성공 시status속성 누락 및 trace 재사용 리스크.
HomeProtectAnimalCard에서 언급한 것과 동일한 패턴이므로 같은 수정이 필요합니다:
onSuccess에trace.putAttribute("status", "success")추가remember블록 내 trace 생성의 재사용 리스크 검토또한 두 카드 컴포넌트의 이미지 트레이싱 로직이 거의 동일하므로, 공통 유틸 함수로 추출하면 중복을 줄일 수 있습니다.
♻️ 공통 함수 추출 예시
// e.g., in a shared util file fun buildTracedImageRequest( context: Context, url: String, traceName: String, logTag: String, logLabel: String, ): ImageRequest { val trace = FirebasePerformance.getInstance().newTrace(traceName) var startTime = 0L return ImageRequest.Builder(context) .data(url) .listener( onStart = { startTime = SystemClock.elapsedRealtime() trace.start() }, onSuccess = { _, _ -> val duration = SystemClock.elapsedRealtime() - startTime Log.d(logTag, "$logLabel 로딩 완료: ${duration}ms | $url") trace.putAttribute("status", "success") trace.stop() }, onError = { _, _ -> val duration = SystemClock.elapsedRealtime() - startTime Log.e(logTag, "$logLabel 로딩 실패: ${duration}ms | $url") trace.putAttribute("status", "error") trace.stop() } ) .build() },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeReportedAnimalCard.kt` around lines 52 - 76, In HomeReportedAnimalCard update the imageRequest built inside remember: ensure the Firebase trace created via FirebasePerformance.getInstance().newTrace(...) has its status set to "success" in the onSuccess listener (add trace.putAttribute("status", "success") before trace.stop()), and avoid trace reuse across recompositions by creating a fresh trace per image load (i.e., don't cache a single Trace instance in a long-lived remember when using start/stop); finally, to remove duplication with HomeProtectAnimalCard extract the image tracing logic into a shared helper (e.g., buildTracedImageRequest) and use that helper from HomeReportedAnimalCard and HomeProtectAnimalCard.
🧹 Nitpick comments (3)
app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt (1)
183-208:refreshData()에는 Firebase trace가 없습니다.
loadHomeData()와 동일하게 홈 데이터를 가져오는refreshData()에는 trace가 적용되지 않았습니다. 새로고침 성능도 측정하려는 의도라면 여기에도 trace를 추가하는 것을 고려해 보세요. 의도적으로 제외한 거라면 무시해 주세요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt` around lines 183 - 208, The refreshData() function is missing the Firebase Performance trace that loadHomeData() uses; wrap the coroutine work inside a Firebase Performance Trace (start the trace at the beginning of refreshData() and stop it after the homeUseCase() fold completes) so that refresh performance is measured similarly to loadHomeData(); ensure the trace is started before calling homeUseCase() and stopped in both onSuccess and onFailure branches (or finally) and reference the same trace name used in loadHomeData() for consistency.app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeProtectAnimalCard.kt (1)
58-81: Trace 성공 시status속성이 누락되어 ViewModel trace와 일관성이 없습니다.
onError에서는trace.putAttribute("status", "error")를 설정하지만,onSuccess에서는 status 속성을 설정하지 않습니다.HomeViewModel의 trace에서는 성공 시"status"="success"를 설정하고 있으므로, 여기서도 동일하게 맞춰주면 Firebase Console에서 필터링할 때 일관적일 것입니다.🔧 제안하는 수정
onSuccess = { _, _ -> val duration = SystemClock.elapsedRealtime() - startTime Log.d("ImagePerf", "보호동물 이미지 로딩 완료: ${duration}ms | ${animal.thumbnailImageUrl}") + trace.putAttribute("status", "success") trace.stop() },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeProtectAnimalCard.kt` around lines 58 - 81, The Firebase trace created in the imageRequest listener doesn't set the "status" attribute on success, causing inconsistency with HomeViewModel traces; update the ImageRequest.Builder listener (the onSuccess lambda for imageRequest) to call trace.putAttribute("status", "success") before trace.stop() so the trace attribute matches the onError case and the ViewModel's "status"="success".app/src/main/AndroidManifest.xml (1)
51-54:firebase_performance_logcat_enabled가 릴리스 빌드에서도 활성화됩니다.이 설정이
true로 하드코딩되어 있어 릴리스 빌드에서도 Firebase Performance 관련 로그가 Logcat에 출력됩니다. 디버깅 목적이라면debug빌드 매니페스트에만 넣거나, 릴리스 전에 제거하는 것이 좋습니다. 큰 문제는 아니지만 불필요한 로그 노이즈를 줄일 수 있어요.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/AndroidManifest.xml` around lines 51 - 54, The meta-data entry "firebase_performance_logcat_enabled" is hardcoded true in the manifest causing Firebase Performance logs to appear in release builds; remove or override it for release by moving the meta-data into the debug-only manifest (src/debug/AndroidManifest.xml) or set it to false for release via buildType-specific manifest placeholders or Gradle config so only debug builds enable Logcat output; update the manifest entry referenced by "firebase_performance_logcat_enabled" accordingly and verify release build no longer contains that meta-data.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeProtectAnimalCard.kt`:
- Around line 57-81: The ImageRequest currently creates a Firebase Performance
Trace inside the remember(animal.thumbnailImageUrl) block (see imageRequest and
the Trace created via
FirebasePerformance.getInstance().newTrace("home_protect_image_load")), which
can cause start() to be called on a stopped trace when the composable is
recycled; move trace creation into the listener callbacks so each request gets a
fresh trace (e.g., create the trace in onStart or create a new Trace instance at
the beginning of onStart and call trace.start(), then stop it in
onSuccess/onError), or alternatively make the trace nullable and instantiate a
new Trace inside onStart and only call stop() if that trace was created—update
the listener in ImageRequest.Builder(...) (onStart/onSuccess/onError) and remove
trace construction from the remember block to ensure a new trace per request.
In
`@app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt`:
- Around line 141-148: The Firebase trace started via
FirebasePerformance.getInstance().newTrace("home_data_load") may not be stopped
if the coroutine launched by homeUseCase() is cancelled; ensure trace.stop() is
called in all termination paths by wrapping the homeUseCase() call and
subsequent trace attribute/metric calls in a try { ... } finally { trace.stop()
} or attach trace.stop() in the coroutine's completion handler (e.g.,
invokeOnCompletion) so that the trace is always stopped even on
CancellationException or other failures; update the HomeViewModel code around
the trace creation and the homeUseCase() invocation to guarantee trace.stop() is
executed.
---
Duplicate comments:
In
`@app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeReportedAnimalCard.kt`:
- Around line 52-76: In HomeReportedAnimalCard update the imageRequest built
inside remember: ensure the Firebase trace created via
FirebasePerformance.getInstance().newTrace(...) has its status set to "success"
in the onSuccess listener (add trace.putAttribute("status", "success") before
trace.stop()), and avoid trace reuse across recompositions by creating a fresh
trace per image load (i.e., don't cache a single Trace instance in a long-lived
remember when using start/stop); finally, to remove duplication with
HomeProtectAnimalCard extract the image tracing logic into a shared helper
(e.g., buildTracedImageRequest) and use that helper from HomeReportedAnimalCard
and HomeProtectAnimalCard.
---
Nitpick comments:
In `@app/src/main/AndroidManifest.xml`:
- Around line 51-54: The meta-data entry "firebase_performance_logcat_enabled"
is hardcoded true in the manifest causing Firebase Performance logs to appear in
release builds; remove or override it for release by moving the meta-data into
the debug-only manifest (src/debug/AndroidManifest.xml) or set it to false for
release via buildType-specific manifest placeholders or Gradle config so only
debug builds enable Logcat output; update the manifest entry referenced by
"firebase_performance_logcat_enabled" accordingly and verify release build no
longer contains that meta-data.
In
`@app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeProtectAnimalCard.kt`:
- Around line 58-81: The Firebase trace created in the imageRequest listener
doesn't set the "status" attribute on success, causing inconsistency with
HomeViewModel traces; update the ImageRequest.Builder listener (the onSuccess
lambda for imageRequest) to call trace.putAttribute("status", "success") before
trace.stop() so the trace attribute matches the onError case and the ViewModel's
"status"="success".
In
`@app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt`:
- Around line 183-208: The refreshData() function is missing the Firebase
Performance trace that loadHomeData() uses; wrap the coroutine work inside a
Firebase Performance Trace (start the trace at the beginning of refreshData()
and stop it after the homeUseCase() fold completes) so that refresh performance
is measured similarly to loadHomeData(); ensure the trace is started before
calling homeUseCase() and stopped in both onSuccess and onFailure branches (or
finally) and reference the same trace name used in loadHomeData() for
consistency.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
app/build.gradle.ktsapp/src/main/AndroidManifest.xmlapp/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeProtectAnimalCard.ktapp/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeReportedAnimalCard.ktapp/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.ktbuild.gradle.ktsgradle/libs.versions.toml
| val context = LocalContext.current | ||
| val imageRequest = remember(animal.thumbnailImageUrl) { | ||
| val trace = FirebasePerformance.getInstance().newTrace("home_protect_image_load") | ||
| var startTime = 0L | ||
| ImageRequest.Builder(context) | ||
| .data(animal.thumbnailImageUrl) | ||
| .listener( | ||
| onStart = { | ||
| startTime = SystemClock.elapsedRealtime() | ||
| trace.start() | ||
| }, | ||
| onSuccess = { _, _ -> | ||
| val duration = SystemClock.elapsedRealtime() - startTime | ||
| Log.d("ImagePerf", "보호동물 이미지 로딩 완료: ${duration}ms | ${animal.thumbnailImageUrl}") | ||
| trace.stop() | ||
| }, | ||
| onError = { _, _ -> | ||
| val duration = SystemClock.elapsedRealtime() - startTime | ||
| Log.e("ImagePerf", "보호동물 이미지 로딩 실패: ${duration}ms | ${animal.thumbnailImageUrl}") | ||
| trace.putAttribute("status", "error") | ||
| trace.stop() | ||
| } | ||
| ) | ||
| .build() | ||
| } |
There was a problem hiding this comment.
remember 블록 안에서 trace를 생성하면 재사용 시 문제가 될 수 있습니다.
remember(animal.thumbnailImageUrl) 블록 안에서 trace 객체가 생성됩니다. Coil이 캐시에서 이미지를 로드하면 onStart → onSuccess가 빠르게 호출되어 정상 동작하지만, 만약 같은 composable이 LazyList에서 재활용되면서 동일 URL로 재구성될 경우, remember가 캐시된 ImageRequest를 반환하므로 이미 stop()된 trace에 대해 다시 start()가 호출될 수 있습니다.
대부분의 경우 문제가 없겠지만, 안전하게 하려면 trace 생성을 listener 내부로 이동하거나, 매 요청마다 새 trace를 생성하는 구조를 고려해 보세요.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeProtectAnimalCard.kt`
around lines 57 - 81, The ImageRequest currently creates a Firebase Performance
Trace inside the remember(animal.thumbnailImageUrl) block (see imageRequest and
the Trace created via
FirebasePerformance.getInstance().newTrace("home_protect_image_load")), which
can cause start() to be called on a stopped trace when the composable is
recycled; move trace creation into the listener callbacks so each request gets a
fresh trace (e.g., create the trace in onStart or create a new Trace instance at
the beginning of onStart and call trace.start(), then stop it in
onSuccess/onError), or alternatively make the trace nullable and instantiate a
new Trace inside onStart and only call stop() if that trace was created—update
the listener in ImageRequest.Builder(...) (onStart/onSuccess/onError) and remove
trace construction from the remember block to ensure a new trace per request.
| val trace = FirebasePerformance.getInstance().newTrace("home_data_load") | ||
| trace.start() | ||
| homeUseCase().fold( | ||
| onSuccess = { data -> | ||
| trace.putAttribute("status", "success") | ||
| trace.putMetric("protect_animal_count", data.protectAnimalCards.size.toLong()) | ||
| trace.putMetric("report_animal_count", data.reportAnimalCards.size.toLong()) | ||
| trace.stop() |
There was a problem hiding this comment.
코루틴 취소 시 trace가 stop되지 않을 수 있습니다.
viewModelScope가 취소되면 (예: ViewModel이 clear될 때) homeUseCase() 호출이 CancellationException으로 중단되면서 trace.stop()이 호출되지 않습니다. try-finally 또는 invokeOnCompletion으로 trace 종료를 보장하면 더 안전합니다.
🛡️ 제안하는 수정
_uiState.update { it.copy(loadState = LoadState.Loading) }
val trace = FirebasePerformance.getInstance().newTrace("home_data_load")
trace.start()
- homeUseCase().fold(
- onSuccess = { data ->
- trace.putAttribute("status", "success")
- trace.putMetric("protect_animal_count", data.protectAnimalCards.size.toLong())
- trace.putMetric("report_animal_count", data.reportAnimalCards.size.toLong())
- trace.stop()
+ try {
+ homeUseCase().fold(
+ onSuccess = { data ->
+ trace.putAttribute("status", "success")
+ trace.putMetric("protect_animal_count", data.protectAnimalCards.size.toLong())
+ trace.putMetric("report_animal_count", data.reportAnimalCards.size.toLong())
+ trace.stop()
...
- onFailure = { error ->
- trace.putAttribute("status", "failure")
- trace.stop()
+ onFailure = { error ->
+ trace.putAttribute("status", "failure")
+ trace.stop()
...
- )
+ )
+ } catch (e: Exception) {
+ trace.putAttribute("status", "cancelled")
+ trace.stop()
+ throw e
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt`
around lines 141 - 148, The Firebase trace started via
FirebasePerformance.getInstance().newTrace("home_data_load") may not be stopped
if the coroutine launched by homeUseCase() is cancelled; ensure trace.stop() is
called in all termination paths by wrapping the homeUseCase() call and
subsequent trace attribute/metric calls in a try { ... } finally { trace.stop()
} or attach trace.stop() in the coroutine's completion handler (e.g.,
invokeOnCompletion) so that the trace is always stopped even on
CancellationException or other failures; update the HomeViewModel code around
the trace creation and the homeUseCase() invocation to guarantee trace.stop() is
executed.
Summary
Test plan
ImagePerf태그로 이미지 로딩 시간 확인Summary by CodeRabbit
릴리스 노트