Skip to content

[Feat] Firebase Performance Monitoring 추가#117

Open
ikseong00 wants to merge 4 commits intodevelopfrom
feat/firebase-performance
Open

[Feat] Firebase Performance Monitoring 추가#117
ikseong00 wants to merge 4 commits intodevelopfrom
feat/firebase-performance

Conversation

@ikseong00
Copy link
Collaborator

@ikseong00 ikseong00 commented Feb 25, 2026

Summary

  • Firebase Performance Monitoring SDK 및 플러그인 추가
  • 홈 화면 API 호출 시간 커스텀 트레이스 추가
  • 보호동물/제보동물 이미지 로딩 시간 측정 (Firebase 트레이스 + Logcat 로그)

Test plan

  • 앱 실행 후 Logcat ImagePerf 태그로 이미지 로딩 시간 확인
  • Firebase 콘솔에서 커스텀 트레이스 데이터 수집 확인

Summary by CodeRabbit

릴리스 노트

  • Chores
    • 앱 버전을 1.1.6으로 업데이트했습니다.
    • 성능 모니터링 기능을 추가하여 애플리케이션의 안정성과 성능을 개선했습니다.

- home_data_load: 홈 API 호출 소요 시간 및 데이터 개수 측정
- home_protect_image_load: 보호 동물 카드 이미지 로딩 시간 측정
- home_report_image_load: 제보 동물 카드 이미지 로딩 시간 측정

#116
- 보호동물/제보동물 이미지 로딩 소요 시간(ms) 로그 출력
- Logcat 태그: ImagePerf

#116
@coderabbitai
Copy link

coderabbitai bot commented Feb 25, 2026

Walkthrough

Firebase Performance Monitoring을 앱에 추가하고, 이미지 로딩 및 홈 데이터 로드 작업에 성능 추적 기능을 구현했습니다. 앱 버전을 업그레이드하고(1.1.4 → 1.1.6), Gradle 빌드 및 매니페스트 설정을 업데이트했습니다.

Changes

Cohort / File(s) Summary
Build & Dependencies
build.gradle.kts (root), build.gradle.kts (app), gradle/libs.versions.toml
Firebase Performance Monitoring 플러그인 추가 및 의존성 선언. 버전 카탈로그에서 firebase-perf 2.0.2 라이브러리 등록. 앱 버전 코드 19→21, 버전명 1.1.4→1.1.6으로 업그레이드.
Manifest Configuration
app/src/main/AndroidManifest.xml
Firebase Performance 로깅 활성화를 위한 메타데이터 추가. AD_ID 권한 선언 리플로우(의미상 변경 없음).
Image Loading Performance Tracing
app/src/main/java/.../HomeProtectAnimalCard.kt, app/src/main/java/.../HomeReportedAnimalCard.kt
AsyncImage의 ImageRequest에 Firebase Performance 트레이싱 적용. 이미지 로드 시작/성공/실패 시점에서 로그 및 추적 메트릭 기록.
ViewModel Performance Tracing
app/src/main/java/.../HomeViewModel.kt
홈 데이터 로드 작업에 Firebase Performance 트레이싱 추가. 성공/실패 상태 및 동물 개수 메트릭 기록.

Possibly related issues

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🔥 Firebase의 눈이 뜨여 밝아지고,
이미지는 속삭임으로 흐르며,
성능의 맥박을 읽고 있네—
매 순간 더 빠르게, 더 똑똑하게. ⚡

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive PR 설명이 주요 변경사항과 테스트 계획을 포함하고 있으나 저장소 템플릿의 필수 섹션(Related issue, Screenshot, Uncompleted Tasks, To Reviewers)이 누락되어 있습니다. 저장소 템플릿의 모든 섹션을 포함하여 작성하거나, 최소한 Related issue와 To Reviewers 섹션을 추가해주세요.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목이 Firebase Performance Monitoring 추가라는 주요 변경사항을 명확하고 간결하게 설명하고 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/firebase-performance

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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에서 언급한 것과 동일한 패턴이므로 같은 수정이 필요합니다:

  1. onSuccesstrace.putAttribute("status", "success") 추가
  2. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 336100f and 5e5733d.

📒 Files selected for processing (7)
  • app/build.gradle.kts
  • app/src/main/AndroidManifest.xml
  • app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeProtectAnimalCard.kt
  • app/src/main/java/com/kuit/findu/presentation/ui/home/component/HomeReportedAnimalCard.kt
  • app/src/main/java/com/kuit/findu/presentation/ui/home/viewmodel/HomeViewModel.kt
  • build.gradle.kts
  • gradle/libs.versions.toml

Comment on lines +57 to +81
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()
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

remember 블록 안에서 trace를 생성하면 재사용 시 문제가 될 수 있습니다.

remember(animal.thumbnailImageUrl) 블록 안에서 trace 객체가 생성됩니다. Coil이 캐시에서 이미지를 로드하면 onStartonSuccess가 빠르게 호출되어 정상 동작하지만, 만약 같은 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.

Comment on lines +141 to +148
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()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

코루틴 취소 시 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant