-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat] 케어콜 푸시알림 설정 #110 #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…oid into feat/fcm-alarm-#110 # Conflicts: # app/build.gradle.kts
WalkthroughFCM 통합과 관련된 빌드·의존성 업데이트, 암호화된 DataStore 직렬화기 및 저장소 구현, Hilt 바인딩, FirebaseMessagingService 구현, 알림 채널/권한 UI, 리소스 및 CI 확장 작업이 추가되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant App as App.onCreate()
participant FcmRepo as FcmRepository (impl)
participant DataStore as Encrypted DataStore
participant FCM as FirebaseMessaging
participant Service as FcmService
participant Notif as NotificationManager
App->>App: createNotificationChannel()
App->>FcmRepo: fetchAndStoreFcmToken()
FcmRepo->>FCM: requestToken()
FCM-->>FcmRepo: token
FcmRepo->>DataStore: saveFcmToken(encrypt → base64)
rect rgb(240,250,255)
Note over FCM,Service: 푸시 수신 흐름
FCM->>Service: onMessageReceived(remoteMessage)
Service->>FcmRepo: getFcmToken()
FcmRepo->>DataStore: readFcmToken()
DataStore-->>FcmRepo: token(decrypted)
Service->>Notif: showNotification(if POST_NOTIFICATIONS granted)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (11)
app/src/main/res/drawable/ic_medi_app.xml (2)
9-11: 하드코드 흰색(fillColor) 사용 — 테마/다크모드 대응 옵션알림 smallIcon에서는 어차피 알파만 사용되므로 문제는 없지만, 앱 내 다른 곳에서 쓸 경우 다크모드에서 배경과 겹쳐 보이지 않을 수 있습니다.
옵션:
- 사용처(ImageView/Compose)에서 tint를 적용해 제어
- 또는 별도 tint 래퍼 drawable을 만들어 테마 색(?attr/colorOnSurface 등)로 틴트된 버전을 제공
브레이킹은 아니므로 선택사항으로 봅니다.
1-12: 알림 전용 아이콘 리소스 분리 권장ic_medi_app이 알림 smallIcon으로 사용됩니다 (app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt:66). 알림 전용 리소스(예: ic_notification_medi_app)로 분리해 알림용 패딩/스케일을 적용한 뒤 FcmService의 setSmallIcon 참조를 새 리소스로 변경하세요.
app/src/main/res/drawable/ic_alarm_logo.xml (1)
16-40: 다중 색상/스트로크 정리 제안
#FDD4D8,#DBF3E7등 컬러와strokeColor="#000000"이 포함되어 있어 알림 컨텍스트에서는 비일관적입니다. 알림용 자산은:
- small icon: 단색(화이트) 알파 전용
- large icon: 다색 가능(필요 시
setLargeIcon로 지정)app/src/main/java/com/konkuk/medicarecall/App.kt (2)
27-31: 채널 이름/설명은 string 리소스로 이동 권장현지화/가독성 위해 문자열 하드코딩 대신
@string사용을 권합니다.- "FCM 알림명", + getString(R.string.fcm_channel_name), ... - description = "Firebase Cloud Messaging으로부터 수신된 알림을 표시합니다." + description = getString(R.string.fcm_channel_desc)
51-54: FCM 토큰 전체 로그 제거 권장디버그라도 전체 토큰 로그는 리스크가 큽니다. 마스킹 로그만 남기세요.
- Log.d(TAG, "FCM token(full)=$token") // 여기에서 서버에 토큰 넘겨주면 됨 + // 서버 전송은 네트워크 계층에서 처리하고, 로그는 마스킹만 유지app/src/main/java/com/konkuk/medicarecall/MainActivity.kt (1)
189-213: Rationale/설정 이동 처리 추가 제안
shouldShowRequestPermissionRationale와 ‘다시 묻지 않음’ 케이스(설정 이동)를 처리하면 UX가 좋아집니다.@Composable fun RequestNotificationPermission() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { val context = LocalContext.current val permission = android.Manifest.permission.POST_NOTIFICATIONS + val pm = android.content.pm.PackageManager.PERMISSION_GRANTED + val activity = (context as? android.app.Activity) val launcher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestPermission() ) { isGranted -> if (isGranted) { Toast.makeText(context, "알림 권한이 허용되었습니다.", Toast.LENGTH_SHORT).show() } else { - Toast.makeText(context, "알림 권한이 거부되었습니다.", Toast.LENGTH_SHORT).show() + val shouldShow = activity?.shouldShowRequestPermissionRationale(permission) ?: false + if (shouldShow) { + Toast.makeText(context, "알림 권한이 필요합니다.", Toast.LENGTH_SHORT).show() + } else { + // '다시 묻지 않음' 케이스: 설정으로 유도 + Toast.makeText(context, "설정에서 알림 권한을 허용해주세요.", Toast.LENGTH_SHORT).show() + val intent = android.content.Intent( + android.provider.Settings.ACTION_APP_NOTIFICATION_SETTINGS + ).apply { + putExtra(android.provider.Settings.EXTRA_APP_PACKAGE, context.packageName) + } + context.startActivity(intent) + } } } LaunchedEffect(Unit) { - if (ContextCompat.checkSelfPermission(context, permission) - != android.content.pm.PackageManager.PERMISSION_GRANTED - ) { + if (ContextCompat.checkSelfPermission(context, permission) != pm) { launcher.launch(permission) } } } }app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt (2)
76-86: 알림 권한 체크는 T+에서만 필요API 33 미만에서는 권한 없이
notify가능합니다. 분기 추가 권장.- if (ActivityCompat.checkSelfPermission( - this, - android.Manifest.permission.POST_NOTIFICATIONS - ) == android.content.pm.PackageManager.PERMISSION_GRANTED - ) { + val canNotify = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ActivityCompat.checkSelfPermission( + this, android.Manifest.permission.POST_NOTIFICATIONS + ) == android.content.pm.PackageManager.PERMISSION_GRANTED + } else true + if (canNotify) { val notificationId = (0..Int.MAX_VALUE).random() NotificationManagerCompat.from(this).notify(notificationId, builder.build()) } else { Log.w("FCM", "POST_NOTIFICATIONS permission not granted") }
32-34: 수신 로그는 디버그 빌드에서만PII 가능성이 있어 릴리스에서는 숨기세요.
- Log.d("FCM", "Message received: ${remoteMessage.data}") + if (com.konkuk.medicarecall.BuildConfig.DEBUG) { + Log.d("FCM", "Message received: ${remoteMessage.data}") + }gradle/libs.versions.toml (3)
60-63: Firebase BoM 사용 시 개별 버전 고정 제거BoM을 선언해두셨는데(firebase-bom), firebase-messaging에는 별도 버전(25.0.0)을 고정하고 있어 버전 정합성이 깨질 수 있습니다. BoM에 맞춰 firebase‑messaging의 버전 표기를 제거하는 쪽이 안전합니다.
아래처럼 정리 권장:
-[versions] -... -firebaseMessaging = "25.0.0" -... -[libraries] -... -firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebaseMessaging" } +[libraries] +# BoM이 버전을 관리하므로 개별 버전 명시 제거 +firebase-messaging = { module = "com.google.firebase:firebase-messaging" }Also applies to: 16-17
66-66: ui-tooling-preview alias 중복 정리동일 모듈(androidx.compose.ui:ui-tooling-preview)에 대해 두 개의 alias(androidx-ui-tooling-preview, ui-tooling-preview)가 공존합니다. 액세서가 달라(예: libs.androidx.ui.tooling.preview vs libs.ui.tooling.preview) 혼선을 줄 수 있어 하나로 통일 권장합니다. 아래 항목(라인 66) 제거 제안.
-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }Also applies to: 40-40
46-46: alias 명명 혼동 가능성: wear용 Material3androidx-compose-material3가 wear 전용 아티팩트(androidx.wear.compose:compose-material3)를 가리키고, 일반 Material3는 androidx-material3로 별도 존재합니다. alias 이름이 헷갈릴 수 있어 wear-compose-material3 같은 명확한 이름으로 변경을 추천합니다. (후속 PR에서 리팩터링해도 됩니다)
Also applies to: 43-43
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
app/build.gradle.kts(3 hunks)app/src/main/AndroidManifest.xml(3 hunks)app/src/main/java/com/konkuk/medicarecall/App.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/MainActivity.kt(8 hunks)app/src/main/java/com/konkuk/medicarecall/data/api/AppPreferences.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt(1 hunks)app/src/main/res/drawable/ic_alarm_logo.xml(1 hunks)app/src/main/res/drawable/ic_medi_app.xml(1 hunks)build.gradle.kts(1 hunks)gradle/libs.versions.toml(2 hunks)
🔇 Additional comments (22)
app/src/main/res/drawable/ic_medi_app.xml (2)
3-6: 정사각(24×24dp) 표시 vs 직사각 뷰포트(427×360) — 비율 불일치(수정 권고)파일: app/src/main/res/drawable/ic_medi_app.xml (줄 3–6)
24×24dp 캔버스에 viewportWidth=427, viewportHeight=360을 쓰면 X/Y 스케일이 달라 로고가 눌려 보일 수 있습니다.권장:
- 24×24 아트보드로 재내보내기(패딩 포함).
- 또는 viewportWidth/viewportHeight를 24/24로 변경하고 path를 스케일·센터 정렬.
- 임시: 표시 크기를 비율 유지(24 * 360/427 ≈ 20.24dp)로 조정.
머지 전 시각적 확인 필요.
12-12: 해결: 파일 끝에 불필요 문자 없음 확인됨
app/src/main/res/drawable/ic_medi_app.xml의 12행()이 마지막 비어있지 않은 라인이며 그 이후 잔여 문자가 없습니다.build.gradle.kts (2)
9-9: Google Services 플러그인 alias 추가 OK하위 모듈에서만 적용되도록
apply false처리 깔끔합니다. 앱 모듈에서 실제 적용 확인됐습니다.
1-12: 확인 완료 — google-services alias 매핑됨libs.plugins.google.services는 libs.versions.toml의 'google-services'(alias)로 매핑되어 있으며 ID=com.google.gms.google-services, version.ref=googleService로 정의되어 있습니다 (libs.versions.toml:75).
app/src/main/java/com/konkuk/medicarecall/App.kt (1)
25-35: 알림 채널 생성 로직 LGTMO+ 가드, 진동/배지/잠금화면 공개 설정까지 기본값 적절합니다.
app/src/main/AndroidManifest.xml (3)
8-8: POST_NOTIFICATIONS 권한 선언 OKtargetSdk 33+ 대응에 필요합니다.
26-28: 기본 채널 ID 일치성 확인Manifest의
fcm_alert가App.FCM_CHANNEL_ID와 동일합니다. 상수/문자열 간 분리로 오탈자 위험이 있으니 PR 내에서 두 군데 모두 변경 시 함께 검토해주세요.
40-46: FCM 서비스 선언 OK
exported="false"와MESSAGING_EVENT인텐트 필터 적절합니다.app/src/main/java/com/konkuk/medicarecall/data/api/AppPreferences.kt (1)
29-31: 민감 데이터 최소화 원칙
saveAccessToken/clearAccessToken사용 경로에서 로그/크래시 리포트로 토큰이 흘러가지 않도록 주의하세요.app/build.gradle.kts (9)
111-115: Firebase BOM + Messaging/Analytics 추가 OK의존성 정합성 확보에 유용합니다.
59-62: buildConfig 활성화 OK
BuildConfig.DEBUG사용과 일관됩니다.
65-75: Compose/AndroidX 추가 OK
foundation추가 문제 없습니다.
116-118: Detekt formatting 플러그인 추가 OK스타일 체크 일관성에 도움 됩니다.
97-102: Retrofit/OkHttp 버전 동기화 및 업그레이드 검토Retrofit 3.0.0 및 com.squareup.retrofit2:converter-kotlinx-serialization:3.0.0이 출시되어 있습니다 — 현재 app/build.gradle.kts(97–102)에는 retrofit:2.12.0 / converter-kotlinx-serialization:2.12.0 / okhttp:4.10.0가 사용중입니다. 브레이킹 변경 여부를 확인하고 버전 동기화(또는 의도적 고정이면 이유 주석 추가) 및 OkHttp 상향을 진행하세요.
82-82: play-services-vision-common 사용 여부 확인 필요 (app/build.gradle.kts: 라인 82)implementation(libs.play.services.vision.common)rg 검색(패턴: com.google.android.gms.(vision|mlkit)) 결과가 비어 있어 사용 여부를 확정할 수 없습니다. 미사용이면 빌드 크기/DEX 감소를 위해 제거 권장. 코드베이스에서 해당 패키지/클래스 참조를 수동으로 검색하거나 모듈별 런타임 호출을 확인하세요.
1-118: 보안 스토리지 의존성 추가 — androidx.security:security-crypto-ktx:1.1.0 권장AppPreferences 암호화를 위해 아래 의존성 추가 권장. 최신 안정판 확인: 1.1.0 (출시일: 2025-07-30).
dependencies { + implementation("androidx.security:security-crypto-ktx:1.1.0") // 최신 안정판 확인 요망 }
11-12: google-services 플러그인 적용 OK — google-services.json 존재 확인 필요카탈로그(alias) 기반 적용은 일관됨. 리포지토리에서 google-services.json이 검색되지 않음. 루트와 android/app 경로에서 아래 명령으로 확인 후 경로(또는 부재) 보고.
fd -H --exact-depth 2 google-services.json | sed 's/^/FOUND: /' || true
find . -maxdepth 4 -type f -iname google-services.json -print | sed 's/^/FOUND: /' || true
93-95: 확인: kotlinx-serialization-json 1.8.0 ↔ Retrofit 컨버터 호환성 및 의존성 추가// Kotlin Serialization implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
- 조치 (app/build.gradle.kts — 줄 93–95): Retrofit용 컨버터 의존성 추가 — implementation("com.squareup.retrofit2:converter-kotlinx-serialization:<최신-2.x-버전>") (Maven Central의 최신 2.x 사용).
- 검증: 통합·회귀 테스트 실행. 런타임/API/바이너리 호환성 문제 발견 시 kotlinx-serialization 런타임 또는 컨버터 버전을 Kotlin 툴체인에 맞춰 정렬.
gradle/libs.versions.toml (4)
64-64: kotlinx-metadata-jvm 의존성 사용 여부 재확인 필요rg가 --type=kt 옵션을 인식하지 못해 검색이 실패했습니다 (unrecognized file type: kt). gradle/libs.versions.toml (line 64)에 선언된 kotlinx-metadata-jvm은 코드에서 직접(import kotlinx.metadata.*) 사용하지 않으면 앱 모듈에 둘 필요가 없습니다. 프로젝트 루트에서 아래 명령을 실행해 사용 여부 결과를 첨부하세요:
#!/bin/bash set -euo pipefail rg -nP '\bkotlinx\.metadata\b' -g '!**/build/**' || echo "No usage detected." rg -n 'kotlinx-metadata-jvm|kotlinxMetadataJvm' -g '!**/build/**' || echo "No dependency reference detected."
18-18: 'play-services-vision-common' 사용 여부 재검증 필요gradle/libs.versions.toml(예: gradle/libs.versions.toml, playServicesVisionCommon = "19.1.3")에 항목이 있습니다. FCM 전용이라면 vision-common은 대개 불필요해 APK/AAB 크기만 늘리므로 미사용이면 제거 권장.
검증 결과: 전달하신 스크립트가 rg의 'kt' 파일 타입을 인식하지 않아 검색이 정상 수행되지 않았습니다(출력: "unrecognized file type: kt"). 아래 명령으로 코드·빌드스크립트 전역을 다시 검색하세요:
#!/bin/bash set -euo pipefail # 코드 내 직접 사용 여부 확인 rg -nP -C2 '\bcom\.google\.android\.gms\.vision\b' -S -g '!**/build/**' -g '*.kt' -g '*.kts' -g '*.java' -g '*.xml' || echo "No direct usage found." # 의존성/버전 카탈로그 선언 확인 rg -nP -C2 'play-services-vision-common|com\.google\.android\.gms:play-services-vision-common' -S -g '!**/build/**' -g '*.gradle' -g '*.kts' -g '*.toml' || echo "No dependency declaration found."검색 결과가 없다면 gradle/libs.versions.toml의 해당 항목과(및) 빌드스크립트의 참조를 제거/정리하세요.
21-21: Hilt 컴파일러: KSP로 일관성 확인됨app/build.gradle.kts:109에서 ksp(libs.hilt.compiler)를 사용 중이며, 프로젝트 전체에서 id("org.jetbrains.kotlin.kapt")가 발견되지 않았습니다. 혼용 이슈 없음 — 조치 불필요.
2-2: AGP(8.13.0) ↔ Gradle Wrapper(8.13) ↔ google-services(4.4.3) 호환성 수동 확인 필요
libs.versions.toml에 AGP 버전을 Gradle 래퍼(8.13) 버전과 동일하게 8.13.0으로 설정하셨는데, Android Gradle Plugin 8.13.0이 실제로 존재하는지 및 google-services(4.4.3) 플러그인과 함께 빌드/동기화에 문제 없는지 직접 검증 부탁드립니다.
| private val sharedPreferences: SharedPreferences = | ||
| context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
액세스 토큰 평문 저장 — 암호화 스토리지로 교체 권장
액세스 토큰은 EncryptedSharedPreferences로 저장하세요.
- private val sharedPreferences: SharedPreferences =
- context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
+ private val sharedPreferences: SharedPreferences by lazy {
+ val masterKey = androidx.security.crypto.MasterKey.Builder(context)
+ .setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme.AES256_GCM)
+ .build()
+ androidx.security.crypto.EncryptedSharedPreferences.create(
+ context,
+ PREFS_NAME,
+ masterKey,
+ androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
+ )
+ }추가로 app/build.gradle.kts에 androidx.security:security-crypto-ktx 의존성 추가 필요합니다(버전은 최신 안정판 확인 요망).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| private val sharedPreferences: SharedPreferences = | |
| context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE) | |
| private val sharedPreferences: SharedPreferences by lazy { | |
| val masterKey = androidx.security.crypto.MasterKey.Builder(context) | |
| .setKeyScheme(androidx.security.crypto.MasterKey.KeyScheme.AES256_GCM) | |
| .build() | |
| androidx.security.crypto.EncryptedSharedPreferences.create( | |
| context, | |
| PREFS_NAME, | |
| masterKey, | |
| androidx.security.crypto.EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, | |
| androidx.security.crypto.EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM | |
| ) | |
| } |
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/api/AppPreferences.kt around
lines 14 to 16, the access token is being stored in plain SharedPreferences;
replace this with EncryptedSharedPreferences by adding the
androidx.security:security-crypto-ktx dependency in app/build.gradle.kts, create
or obtain a MasterKey (using the recommended scheme), instantiate
EncryptedSharedPreferences with that MasterKey and the same PREFS_NAME, and
update all get/set operations to use the encrypted prefs so tokens are stored
encrypted at rest; also handle migration of any existing plaintext values if
needed.
app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt
Outdated
Show resolved
Hide resolved
|
|
||
| // 알림 빌더 | ||
| val builder = NotificationCompat.Builder(this, channelId) | ||
| .setSmallIcon(R.drawable.ic_medi_app) // 반드시 존재하는 리소스여야 함 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ Verification inconclusive
small icon은 단색 알파 전용 리소스로
ic_medi_app가 컬러/런처 아이콘이면 부적절합니다. 단색 벡터(ic_stat_medi)로 교체하세요.
- .setSmallIcon(R.drawable.ic_medi_app) // 반드시 존재하는 리소스여야 함
+ .setSmallIcon(R.drawable.ic_stat_medi) // 단색 알파 전용작업 필요 — 알림 smallIcon을 단색(알파) 전용 아이콘으로 교체
R.drawable.ic_medi_app가 컬러/런처 아이콘이면 알림에서 색이 깨지므로 단색 벡터 리소스(ic_stat_medi)로 교체.
- .setSmallIcon(R.drawable.ic_medi_app) // 반드시 존재하는 리소스여야 함
+ .setSmallIcon(R.drawable.ic_stat_medi) // 단색 알파 전용📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .setSmallIcon(R.drawable.ic_medi_app) // 반드시 존재하는 리소스여야 함 | |
| .setSmallIcon(R.drawable.ic_stat_medi) // 단색 알파 전용 |
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt around line
66, the notification smallIcon currently uses R.drawable.ic_medi_app which is a
colored/launcher asset; replace it with a single-color (alpha-only) vector
drawable R.drawable.ic_stat_medi, ensure the ic_stat_medi resource exists as a
monochrome vector (transparent background, single solid path) and update any
resource names or imports accordingly so setSmallIcon(R.drawable.ic_stat_medi)
is used for notifications.
| .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) | ||
| .setContentIntent(contentPi) | ||
| .setAutoCancel(true) | ||
| .setFullScreenIntent(contentPi, true) // 화면 꺼져있을 때 팝업 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Full-screen intent 남용 주의 (Android 14 정책)
긴급 케이스에만 사용하고, 사용자 동의 플래그(예: criticalPopupEnabled)로 게이트하세요.
- .setFullScreenIntent(contentPi, true) // 화면 꺼져있을 때 팝업
+ .apply {
+ if (appPreferences.criticalPopupEnabled()) {
+ setFullScreenIntent(contentPi, true)
+ }
+ }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt around line
74, the call to .setFullScreenIntent(contentPi, true) must be gated by an
app-level user consent / emergency flag; modify the code to check a persisted
boolean (e.g., criticalPopupEnabled) and the notification urgency before calling
setFullScreenIntent, only invoking it when criticalPopupEnabled is true and the
message is an emergency, and ensure the flag defaults to false and is
configurable from user settings so full-screen intents are not used unless
explicitly enabled.
| // 알림 권한 요청 | ||
| RequestNotificationPermission() | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
앱 시작 직후 권한 요청 UX 재고 필요
런치 즉시 권한 요청은 거부율을 올립니다. 교육용 UI/트리거 시점(예: 알림 기능 진입 시)로 미루는 것을 고려하세요.
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/MainActivity.kt around lines 90 to
92, the app currently requests notification permission immediately on launch
(RequestNotificationPermission()), which increases denial rates; remove that
immediate call from startup and instead invoke the permission flow at a
contextual trigger (e.g., when the user first navigates to or enables a
notification-related feature). Implement a lightweight educational UI/rationale
(dialog or in-app screen) before calling the permission API, and use
shouldShowRequestPermissionRationale to show the rationale on subsequent
attempts; ensure the startup path no longer calls
RequestNotificationPermission().
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||
| android:width="42dp" | ||
| android:height="42dp" | ||
| android:viewportWidth="42" | ||
| android:viewportHeight="42"> | ||
| <group> | ||
| <clip-path | ||
| android:pathData="M10.12,0L31.879,0A10.12,10.12 0,0 1,42 10.12L42,31.879A10.12,10.12 0,0 1,31.879 42L10.12,42A10.12,10.12 0,0 1,0 31.879L0,10.12A10.12,10.12 0,0 1,10.12 0z"/> | ||
| <path | ||
| android:pathData="M10.12,0L31.879,0A10.12,10.12 0,0 1,42 10.12L42,31.879A10.12,10.12 0,0 1,31.879 42L10.12,42A10.12,10.12 0,0 1,0 31.879L0,10.12A10.12,10.12 0,0 1,10.12 0z" | ||
| android:fillColor="#00B859"/> | ||
| <group> | ||
| <clip-path | ||
| android:pathData="M5,16.016l41.025,-10.016l8.464,34.669l-41.025,10.016z"/> | ||
| <path | ||
| android:pathData="M46.484,22.159C45.678,21.713 44.858,21.378 44.058,21.153C40.94,13.755 33.221,9.432 25.649,11.281C18.078,13.129 13.251,20.475 13.854,28.447C13.207,29.037 12.593,29.749 12.05,30.566C9.881,33.834 9.689,37.53 11.621,38.82C12.625,39.491 14.007,39.377 15.407,38.656L15.408,38.66C16.84,44.527 22.745,48.124 28.596,46.696C29.762,46.411 30.596,45.479 30.82,44.374C31.753,44.329 32.692,44.194 33.628,43.966C34.564,43.737 35.38,43.451 36.194,43.085C36.904,43.948 38.066,44.382 39.222,44.1C45.075,42.671 48.656,36.757 47.224,30.891L47.222,30.885C48.802,30.882 50.084,30.346 50.667,29.286C51.787,27.25 49.914,24.06 46.482,22.158L46.484,22.159Z" | ||
| android:fillColor="#ffffff"/> | ||
| <path | ||
| android:pathData="M38.436,29.158C40.704,28.604 42.093,26.311 41.538,24.037C40.982,21.763 38.694,20.368 36.425,20.922C34.157,21.476 32.769,23.768 33.324,26.043C33.879,28.317 36.168,29.711 38.436,29.158Z" | ||
| android:fillColor="#FDD4D8"/> | ||
| <path | ||
| android:pathData="M29.368,27.01C29.74,26.919 30.06,26.685 30.259,26.358C30.417,26.097 30.487,25.795 30.463,25.495C30.463,25.49 30.462,25.488 30.461,25.486C30.46,25.484 30.456,25.481 30.452,25.479C30.442,25.472 30.426,25.468 30.407,25.472L27.738,26.124C27.719,26.129 27.706,26.14 27.701,26.15C27.698,26.155 27.697,26.159 27.696,26.161C27.696,26.163 27.697,26.166 27.698,26.17C27.816,26.447 28.017,26.683 28.278,26.842C28.604,27.04 28.997,27.1 29.368,27.01Z" | ||
| android:strokeWidth="0.388534" | ||
| android:fillColor="#FF7272" | ||
| android:strokeColor="#000000"/> | ||
| <path | ||
| android:pathData="M22.375,33.079C24.643,32.526 26.031,30.233 25.476,27.959C24.921,25.685 22.632,24.29 20.364,24.844C18.096,25.398 16.707,27.69 17.262,29.965C17.817,32.239 20.106,33.633 22.375,33.079Z" | ||
| android:fillColor="#FDD4D8"/> | ||
| <path | ||
| android:pathData="M24.286,26.076C24.966,25.91 25.38,25.211 25.21,24.515C25.04,23.819 24.351,23.389 23.671,23.555C22.991,23.721 22.577,24.42 22.747,25.116C22.917,25.812 23.606,26.242 24.286,26.076Z" | ||
| android:fillColor="#231815"/> | ||
| <path | ||
| android:pathData="M33.544,23.816C34.225,23.65 34.638,22.951 34.468,22.255C34.298,21.559 33.609,21.129 32.929,21.296C32.249,21.462 31.836,22.16 32.005,22.856C32.175,23.552 32.864,23.982 33.544,23.816Z" | ||
| android:fillColor="#231815"/> | ||
| <path | ||
| android:pathData="M38.877,41.692C38.305,42.272 36.854,42.875 36.2,43.103C37.28,44.602 39.366,44.258 40.47,43.713C38.667,44.208 38.657,42.572 38.877,41.692Z" | ||
| android:fillColor="#DBF3E7"/> | ||
| <path | ||
| android:pathData="M14.508,34.751L15.47,38.692C14.682,39.174 13.455,39.274 12.939,39.264C14.008,38.749 14.43,36.041 14.508,34.751Z" | ||
| android:fillColor="#DBF3E7"/> | ||
| </group> | ||
| </group> | ||
| </vector> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 리소스를 상태바 small icon으로 쓰면 안 됩니다
상태바 small icon은 알파 전용(단색/투명)이어야 합니다. 이 벡터는 다중 색상/스트로크를 포함하므로 small icon로 쓰면 틴팅/가시성 문제가 생깁니다. small icon은 단색 벡터(ic_stat_*)로 별도 준비하고, 이 리소스는 필요 시 large icon/UI용으로만 사용하세요.
🤖 Prompt for AI Agents
app/src/main/res/drawable/ic_alarm_logo.xml lines 1-43: the reviewer says this
multi-color vector must not be used as a status-bar small icon (status icons
must be monochrome/alpha-only). Fix by creating a separate single-color,
alpha-only vector resource named with the ic_stat_* convention for the status
bar (remove multi-color fills and strokes, keep only shapes filled with a single
solid color or use themeable tint attribute), and leave ic_alarm_logo.xml for
large/UI use only; update any references (notifications, manifests) to point to
the new ic_stat_* drawable for small/status-bar icon usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
gradle/libs.versions.toml (4)
16-17: Firebase BOM 사용 시 개별 아티팩트 버전 제거 권장.BOM을 쓰면 하위 모듈에는 버전을 지정하지 않는 게 안전합니다.
firebase-messaging에 명시 버전이 있어 충돌/동결 위험이 있습니다. BOM만으로 정렬하도록 아래처럼 정리해 주세요.적용 diff:
- firebaseMessaging = "25.0.0" ... - firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebaseMessaging" } + # (버전 키 제거) ... + firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging" }추가로, 실제 모듈에서 반드시
implementation(platform(libs.firebase.bom))또는dependencies { implementation(platform(...)) }가 먼저 선언돼 있어야 합니다.Also applies to: 59-62
21-21: Hilt + KSP 전환 시 체크리스트.
hilt-compiler를 KSP로 사용하려면 각 모듈에서ksp(libs.hilt.compiler)를 쓰고, 기존kapt의존성/플러그인은 제거해야 합니다.ksp플러그인 추가는 맞습니다. 빌드 캐시/증분 빌드 동작도 확인 권장.예시(app/build.gradle.kts):
plugins { alias(libs.plugins.hilt) alias(libs.plugins.ksp) } dependencies { implementation(libs.hilt.android) ksp(libs.hilt.compiler) // kapt(...) 제거 }Also applies to: 52-52, 72-73
65-65: 동일 모듈에 대한 중복 alias 정리.이미
androidx-ui-tooling-preview(Line 40)가 존재합니다. 별칭ui-tooling-preview는 혼선을 줄 수 있으니 하나로 통일하세요.적용 diff:
- ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
59-60: Firebase BOM/Analytics 추가는 방향성 OK.BOM 도입과 Analytics 무버전 선언은 정석입니다. 메시징도 동일 패턴으로 맞추면 의존성 정렬이 더 안정적입니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app/build.gradle.kts(3 hunks)gradle/libs.versions.toml(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/build.gradle.kts
🔇 Additional comments (5)
gradle/libs.versions.toml (5)
74-74: Google Services 플러그인 버전 고정 LGTM.버전 카탈로그로 고정한 점 좋습니다. 모듈 적용 위치가 app 모듈에 한정(루트 빌드스크립트가 아닌)되어 있는지도 확인만 부탁드립니다. 또한
google-services.json이 모든 빌드 변형/플레이버에 존재하는지 점검해 주세요.
18-18: play-services-vision-common 제거 검토 필요gradle/libs.versions.toml에 playServicesVisionCommon = "19.1.3"가 등록되어 있으나 repo 검색(rg) 결과 Vision 관련 식별자(com.google.android.gms.vision 또는 'Vision')가 발견되지 않았습니다. 제거 전 실제 사용처(레거시 ML Vision API, 외부 모듈/플러그인, CI 스크립트 등)를 수동으로 확인하고, 사용처가 없다면 제거하세요.
위치: gradle/libs.versions.toml (라인 18) — playServicesVisionCommon = "19.1.3"
2-2: 확인: Gradle 래퍼(8.13)가 AGP 8.13.0 요구사항을 충족합니다
gradle/wrapper/gradle-wrapper.properties — distributionUrl=…/gradle-8.13-bin.zip: AGP 8.13.0의 최소 요구 Gradle 8.13을 만족합니다.
23-23: KSP 패치 버전 업데이트 권장 — Kotlin 2.0.21용 최신 패치: 2.0.21-1.0.28Maven Central 및 KSP 릴리스 자료에 따르면 Kotlin 2.0.21에 빌드된 최신 KSP 패치는 2.0.21-1.0.28입니다.
- ksp = "2.0.21-1.0.25" + ksp = "2.0.21-1.0.28"공식 릴리스 노트로 호환성/변경사항 확인 후 적용하세요.
8-8: kotlinx-metadata-jvm 사용 여부 재확인 필요제공한 rg 명령이 출력 없이 종료됐습니다 — 사용되지 않는다는 증거가 아니므로 제거 전 아래 추가 검증을 실행하고 출력 결과를 코멘트로 올려라.
- 코드베이스 전체에서 패키지/아티팩트명 검색:
rg -nP -C2 'org\.jetbrains\.kotlinx:kotlinx-metadata-jvm|kotlinx-metadata-jvm|kotlinx\.metadata' || true
- Gradle 의존성 리포트에서 전이적 포함 확인:
./gradlew dependencies --no-daemon | rg -n 'kotlinx-metadata|kotlinx.metadata' || true
- 모듈별로 확인이 필요하면(예시):
./gradlew :<module>:dependencies --configuration runtimeClasspath | rg -n 'kotlinx-metadata|kotlinx.metadata' || true위치: gradle/libs.versions.toml (line 8) — kotlinxMetadataJvm = "0.9.0"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (6)
app/build.gradle.kts (3)
75-80: Material3 중복/혼선 가능성: 두 아티팩트 동시 의존 확인
libs.androidx.material3(표준 Compose M3)와libs.androidx.compose.material3(toml에서 wear compose 계열로 보임)를 동시에 추가했습니다. 실제로 wear용이 필요 없다면 하나로 정리하세요. wear용이 필요하다면 toml 좌표가 정확한지 재검(아래 toml 코멘트 참조).
94-96: kotlinx-serialization 버전 하드코딩 지양, 버전 카탈로그로 이관 권장Kotlin(2.0.21)과의 호환성 관리 측면에서 toml에 버전/라이브러리를 정의해 사용하세요.
다음 diff로 정리 제안(toml 변경 포함):
- implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0") + implementation(libs.kotlinx.serialization.json)추가로 toml 변경은 해당 파일 코멘트를 참고해주세요.
112-116: Firebase BOM 사용은 좋습니다. toml에서 모듈 버전 제거 필요BOM을 쓸 때 개별 모듈(예: firebase-messaging)은 버전을 명시하지 않는 것이 정석입니다. 현재 toml에 버전이 있어 충돌/예상치 못한 업그레이드가 날 수 있으니 아래 toml 코멘트를 적용해 주세요.
gradle/libs.versions.toml (3)
65-68: 중복 별칭 정리: ui-tooling-preview가 두 곳에 정의됨
androidx-ui-tooling-preview와 별도로ui-tooling-preview가 동일 모듈을 가리킵니다. 하나로 통일하세요.제안(diff: 보조 별칭 삭제):
-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
61-63: Analytics 포함 의도 확인개인정보/동의 정책 영향이 있으니 애널리틱스 사용 범위를 명확히 하시고, 필요 없다면 제거를 고려하세요.
61-61: 버전 카탈로그에 serialization 라이브러리 추가 제안app 모듈에서 하드코딩하지 않도록 아래 항목을 추가하면 좋습니다.
적용 diff:
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.8.0" }필요 시 최신 안정판으로 조정 가능합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app/build.gradle.kts(4 hunks)gradle/libs.versions.toml(2 hunks)
🔇 Additional comments (9)
app/build.gradle.kts (5)
59-62: buildConfig 활성화 LGTM
BuildConfig.BASE_URL사용 전제의 설정 일관성이 좋아졌습니다. 추가 조치 없음.
117-118: Detekt formatting 플러그인 연동 LGTM팀 컨벤션 정합성 향상에 도움됩니다.
11-12: 조치 필요 — settings.gradle(.kts) pluginManagement에 google() 등록 및 app/google-services.json 확인app/build.gradle.kts에서 plugins DSL로 google services 플러그인(alias(libs.plugins.google.services)) 적용은 맞습니다.
스크립트 실행 결과: settings.gradle(.kts)에서 pluginManagement.repositories에 google() 항목이 검색되지 않았고, app/google-services.json 파일이 발견되지 않았습니다.
- settings.gradle(.kts)에 pluginManagement { repositories { google() ... } } 를 추가하거나(또는 CI/루트 설정에서 google()이 제공되는지 확인).
- app/google-services.json을 app/에 배치하거나(또는 Firebase 사용이 아니라면 플러그인 제거/대체).
파일: app/build.gradle.kts (Lines 11-12) — 해당 플러그인 적용부 확인.
97-103: OkHttp 업그레이드 권장 — 4.10.0 → 5.1.0 (보안/버그 픽스)app/build.gradle.kts(97–103)에서 okhttp:4.10.0 사용 중; 최신 안정판은 OkHttp 5.1.0(2025-07-07), 최소 Android API는 21입니다. 메이저 업그레이드(4→5)이니 Retrofit 컨버터·logging-interceptor 등 호환성 확인 및 전체 테스트 후 버전 업데이트 PR 제출하세요.
83-84: play-services-vision-common 사용 여부 확인 — 제거 검토
app/build.gradle.kts (줄 83–84): implementation(libs.play.services.vision.common). .kt/.java 파일에서 'com.google.android.gms.vision' 또는 'com.google.android.libraries.vision'을 검색했으나 사용처가 발견되지 않았습니다. FCM/푸시에는 불필요하므로 실제 사용처가 없다면 제거해 메서드 수와 APK 크기를 줄이세요.gradle/libs.versions.toml (4)
24-25: KSP 버전 호환성 확인
ksp = "2.0.21-1.0.25"는 Kotlin 2.0.21과 매칭되지만, AGP/Gradle 조합에 따라 마이너 업데이트가 필요할 수 있습니다. CI에서 annotation processing 관련 경고가 없는지 확인 부탁드립니다.
69-77: google-services 플러그인 선언 LGTM버전 카탈로그로 관리되어 일관성이 좋습니다.
8-8: kotlinx-metadata-jvm 추가 목적 확인 — 현재 레포에서 참조 없음레포 전역 검색 결과 코드·빌드스크립트에서 사용 흔적이 없고 gradle/libs.versions.toml에만 선언되어 있습니다 (gradle/libs.versions.toml:8, 65). 사용 목적이 없으면 버전 카탈로그에서 제거하고, 사용 중이면 사용 위치(파일)와 이유를 PR에 명시하세요.
2-2: AGP 8.13 호환성 확인 — 별도 조치 불필요gradle/wrapper/gradle-wrapper.properties가 Gradle 8.13을 가리키고, CI(.github/workflows/android-ci.yml)에서 JDK 17을 사용하도록 설정되어 있어 AGP 8.13의 최소 요구사항(Gradle 8.13, JDK 17)을 충족합니다. (developer.android.com)
확인 위치: gradle/wrapper/gradle-wrapper.properties (distributionUrl=gradle-8.13-bin.zip), .github/workflows/android-ci.yml (actions/setup-java: java-version '17').
| ksp(libs.hilt.manager) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
중요: ksp(libs.hilt.manager)는 빌드 실패 가능성이 큼(안드로이드X Hilt Compiler는 KSP 미지원)
androidx.hilt:hilt-compiler는 현재 KAPT만 지원합니다. KSP로 연결하면 빌드가 깨질 확률이 높습니다. 실제로 필요 없다면 제거, 필요하다면 KAPT로 전환하세요.
적용 diff(불필요 시 제거):
- ksp(libs.hilt.manager)만약 WorkManager 등의 AndroidX Hilt 확장에 컴파일러가 필요하면 다음처럼 전환:
// plugins 블록에 추가
plugins {
id("org.jetbrains.kotlin.kapt")
}
// dependencies 블록
kapt(libs.hilt.manager)또는 해당 확장을 쓰지 않는다면 toml의 hilt-manager 항목 자체를 삭제하세요(아래 toml 코멘트 참조).
🤖 Prompt for AI Agents
In app/build.gradle.kts around lines 119-120, the dependency uses
ksp(libs.hilt.manager) which will likely break the build because
androidx.hilt:hilt-compiler only supports KAPT; either remove the dependency if
unused, or switch to KAPT: add the Kotlin KAPT plugin
(id("org.jetbrains.kotlin.kapt")) to the plugins block and replace
ksp(libs.hilt.manager) with kapt(libs.hilt.manager); alternatively, if you truly
don’t use the Hilt WorkManager/AndroidX extensions, delete the hilt-manager
entry from libs.versions.toml instead.
| androidx-compose-material3 = { group = "androidx.wear.compose", name = "compose-material3", version.ref = "composeMaterial3" } | ||
|
|
||
| androidx-foundation = { group = "androidx.compose.foundation", name = "foundation" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wear Compose Material3 좌표 의심됨 또는 불필요한 중복
androidx-compose-material3 = { group = "androidx.wear.compose", name = "compose-material3", ... }는 실제 아티팩트 명이 material3로 알려져 있습니다. 의도적으로 Wear용을 쓰는 게 아니라면 삭제, 필요하다면 name을 material3로 수정하세요. 또한 app 모듈에서 표준 Material3도 함께 쓰고 있어 중복입니다.
제안(diff 1: 삭제):
-androidx-compose-material3 = { group = "androidx.wear.compose", name = "compose-material3", version.ref = "composeMaterial3" }제안(diff 2: Wear 필요 시 수정):
-androidx-compose-material3 = { group = "androidx.wear.compose", name = "compose-material3", version.ref = "composeMaterial3" }
+androidx-compose-material3 = { group = "androidx.wear.compose", name = "material3", version.ref = "composeMaterial3" }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| androidx-compose-material3 = { group = "androidx.wear.compose", name = "compose-material3", version.ref = "composeMaterial3" } | |
| androidx-foundation = { group = "androidx.compose.foundation", name = "foundation" } | |
| androidx-foundation = { group = "androidx.compose.foundation", name = "foundation" } |
🤖 Prompt for AI Agents
In gradle/libs.versions.toml around lines 47 to 48, the dependency entry uses
the Wear Compose artifact coordinates (group = "androidx.wear.compose", name =
"compose-material3") which is likely incorrect or redundant with the standard
Material3 dependency used by the app; either remove this Wear-specific entry if
not needed, or change the name to the correct artifact name ("material3") and/or
group to the standard compose material3 coordinates so it matches the actual
artifact you intend to use and avoid duplicate Material3 dependencies across
modules.
| hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } | ||
| hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } | ||
| hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } | ||
| hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" } | ||
| hilt-manager = {group = "androidx.hilt" , name = "hilt-compiler", version.ref = "hiltManager"} | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
안드로이드X Hilt 컴파일러 항목(hilt-manager) 문제: 좌표/버전/도구 체인
hilt-manager라이브러리 이름이 혼동을 유발하고, 좌표가androidx.hilt:hilt-compiler로 보이지만 버전 키(hiltManager = "1.0.0")가 오래되었을 수 있음.- 해당 컴파일러는 KSP 미지원(KAPT 필요). 현재 app 모듈에서
ksp(libs.hilt.manager)로 사용 중이라 빌드 실패 가능.
불필요하면 완전 제거, 필요하면 KAPT 전환과 최신 버전 확인을 권장합니다.
제안(diff: 제거):
-hilt-manager = {group = "androidx.hilt" , name = "hilt-compiler", version.ref = "hiltManager"}필요 시 별도 이슈로 최신 호환 버전 검증 도와드릴게요.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } | |
| hilt-android-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } | |
| hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } | |
| hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" } | |
| hilt-manager = {group = "androidx.hilt" , name = "hilt-compiler", version.ref = "hiltManager"} | |
| hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } | |
| hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } | |
| hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version.ref = "hiltNavigationCompose" } |
🤖 Prompt for AI Agents
In gradle/libs.versions.toml around lines 52-56, the hilt-manager entry is
incorrect/problematic: it declares androidx.hilt:hilt-compiler under a confusing
name, likely points to an old version that requires KAPT (not KSP) and your app
currently uses ksp(libs.hilt.manager), which will cause build failures. Fix by
either removing the hilt-manager entry if unused, or update the dependency to
the correct Hilt compiler artifact and version key and switch the app to use
kapt(...) instead of ksp(...); after changing, verify and update the version.ref
to a current compatible release.
| firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } | ||
| google-firebase-analytics = { module = "com.google.firebase:firebase-analytics" } | ||
| firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebaseMessaging" } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Firebase BOM 사용 시 개별 모듈 버전 제거
firebase-bom을 사용하므로 firebase-messaging의 version.ref는 제거해야 일관적으로 BOM에 의해 버전이 고정됩니다.
적용 diff:
-firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebaseMessaging" }
+firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging" }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } | |
| google-firebase-analytics = { module = "com.google.firebase:firebase-analytics" } | |
| firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging", version.ref = "firebaseMessaging" } | |
| firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } | |
| google-firebase-analytics = { module = "com.google.firebase:firebase-analytics" } | |
| firebase-messaging = { group = "com.google.firebase", name = "firebase-messaging" } |
🤖 Prompt for AI Agents
In gradle/libs.versions.toml around lines 61 to 64, the firebase-messaging entry
includes a version.ref which conflicts with using firebase-bom; remove the
version.ref from the firebase-messaging declaration so its version is governed
by firebase-bom (leave the group/name keys or alternatively convert the entry to
the same module shorthand as google-firebase-analytics), ensuring consistency
with the BOM.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
.github/workflows/android-ci.yml (3)
4-7: 브랜치 트리거 구성은 적절합니다. 수동 실행·중복 실행 취소·권한 최소화만 추가해요.develop 대상 push/PR만 타게팅한 점 좋습니다. 운영 편의·보안 위해 workflow_dispatch, concurrency(cancel-in-progress), permissions(contents: read) 추가를 추천합니다.
on: push: branches: [ "develop" ] pull_request: branches: [ "develop" ] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true
26-35: Gradle 캐시가 이중화되어 캐시 미스/쓰래시 가능성 있습니다.setup-java@v4의 cache: gradle를 이미 사용 중이므로 별도의 actions/cache는 제거하세요.
- - name: Cache Gradle packages - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/buildSrc/**/*.kt') }} - restore-keys: | - ${{ runner.os }}-gradle-
36-39: local.properties 작성은 >> 대신 안전한 heredoc으로 덮어쓰기 권장여러 번 실행 시 누적을 막고, 멀티라인/특수문자 보존을 위해 heredoc이 안전합니다.
- - name: Generate local.properties - run: | - echo '${{ secrets.LOCAL_PROPERTIES }}' >> ./local.properties + - name: Generate local.properties + run: | + cat > local.properties <<'EOF' + ${{ secrets.LOCAL_PROPERTIES }} + EOF
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/android-ci.yml(2 hunks)
🔇 Additional comments (2)
.github/workflows/android-ci.yml (2)
9-9: jobs 블록 구조 변경 사항 문제 없습니다.구조·명명 컨벤션 무난합니다.
40-57: 더미 google-services.json: 패키지명 검증 및 파일 존재 시 생성 건너뛰기 추가 권장검증 결과: 제공된 스크립트 실행에서 app/google-services.json 없음; build.gradle / AndroidManifest에서 applicationId/package 미검출 — 패키지명 확인 불가.
권장 조치: 아래 diff 적용하고 PACKAGE_NAME에 실제 applicationId를 설정하거나 워크플로우에서 동적으로 추출하도록 변경하세요.- - name: Create dummy google-services.json - run: | - cat <<EOF > app/google-services.json + - name: Create dummy google-services.json (if missing) + env: + PACKAGE_NAME: com.konkuk.medicarecally + run: | + if [ -f app/google-services.json ]; then + echo "google-services.json already present; skipping dummy creation." + exit 0 + fi + cat > app/google-services.json <<EOF { "project_info": { "project_number": "000000000000", "project_id": "dummy-project" }, "client": [{ "client_info": { "mobilesdk_app_id": "1:000000000000:android:0000000000000000", "android_client_info": { - "package_name": "com.konkuk.medicarecally" + "package_name": "${PACKAGE_NAME}" } } }] } EOF
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (4)
.github/workflows/android-ci.yml (4)
40-57: 더미 google-services.json은 debug 소스셋에만 두세요루트 모듈(app/google-services.json)에 쓰면 향후 release 빌드에도 섞일 리스크가 있습니다.
app/src/debug/google-services.json로 한정하면 안전합니다. 디렉토리 생성을 포함해 아래처럼 수정 권장합니다.- - name: Create dummy google-services.json - run: | - cat <<EOF > app/google-services.json + - name: Create dummy google-services.json (debug only) + run: | + mkdir -p app/src/debug + cat <<'EOF' > app/src/debug/google-services.json { "project_info": { "project_number": "000000000000", "project_id": "dummy-project" }, "client": [{ "client_info": { "mobilesdk_app_id": "1:000000000000:android:0000000000000000", "android_client_info": { "package_name": "com.konkuk.medicarecall" } } }] } EOF참고:
- productFlavor를 쓰는 경우에는
app/src/<flavorDebug>/google-services.json로 더 좁힐지 검토 부탁드립니다.- 사용 중인
com.google.gms.google-services플러그인 버전에 따라api_key필드가 필요할 수 있습니다. 현재 스키마로 빌드가 통과하는지 확인해 주세요.
4-7: 트리거 유연성/소음 감소 옵션운영에 도움될 수 있는 두 가지 제안입니다.
- 수동 실행 허용:
on: push: branches: [ "develop" ] pull_request: branches: [ "develop" ] + workflow_dispatch:
- 문서만 변경 시 빌드 스킵(원하면):
pull_request: - branches: [ "develop" ] + branches: [ "develop" ] + paths-ignore: + - '**/*.md'
9-13: 권한 최소화·Wrapper 검증·중복 캐시 정리몇 가지 CI 위생 개선 제안입니다.
- Job 권한 최소화:
jobs: ci-build: runs-on: ubuntu-latest + permissions: + contents: read
- Gradle Wrapper 서명 검증 추가(서드파티 감염 방지):
- uses: actions/checkout@v4 + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v2
- 캐시 중복 제거:
actions/setup-java@v4의cache: gradle과 별도의actions/cache@v4가 동일 경로를 이중 캐싱하고 있습니다. 하나로 통일하세요. 권장안은 Gradle 공식 액션입니다.- - name: Cache Gradle packages - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/buildSrc/**/*.kt') }} - restore-keys: | - ${{ runner.os }}-gradle- + - name: Gradle Build Action (caching) + uses: gradle/gradle-build-action@v3검증 부탁드리는 점:
- 위 변경 후 캐시 적중률 및 빌드 시간 비교.
36-39: local.properties 주입: 덮어쓰기, 비밀 노출, 포크 PR 호환성현재
>>추가 모드와echo사용은 재시도 시 중복/개행 이슈가 있고, 포크 PR에서는 시크릿이 비어 빌드가 깨질 수 있습니다. 또한 명령 로그에 문자열이 노출될 소지가 있습니다(마스킹되더라도 보수적으로 처리 권장).아래처럼 조건부·덮어쓰기·표준출력 노출 최소화로 바꿔주세요:
- - name: Generate local.properties - run: | - echo '${{ secrets.LOCAL_PROPERTIES }}' >> ./local.properties + - name: Generate local.properties + if: ${{ secrets.LOCAL_PROPERTIES != '' }} + env: + LOCAL_PROPERTIES: ${{ secrets.LOCAL_PROPERTIES }} + run: | + printf "%s" "$LOCAL_PROPERTIES" > local.properties추가 확인:
- LOCAL_PROPERTIES에
sdk.dir=가 포함돼 있으면 GitHub Runner 경로와 불일치로 빌드가 실패할 수 있습니다. 포함되어 있다면 제거하거나 CI에서 설정하지 않도록 해주세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/android-ci.yml(2 hunks)
🔇 Additional comments (1)
.github/workflows/android-ci.yml (1)
59-60: assembleDebug로 교체 권장 — buildDebug는 표준 태스크가 아닙니다AGP 표준 태스크는 assembleDebug/bundleDebug입니다. buildDebug는 기본 제공되지 않아 CI가 실패할 수 있습니다.
다음으로 교체해 주세요:
- run: ./gradlew buildDebug --stacktrace + run: ./gradlew assembleDebug --stacktraceAAB를 빌드하려면
bundleDebug를 사용하세요.
ikseong00
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
레퍼런스 파일 첨부 좋습니다!
ci 파일 체크만 부탁드려요~
.github/workflows/android-ci.yml
Outdated
| - name: Create dummy google-services.json | ||
| run: | | ||
| cat <<EOF > app/google-services.json | ||
| { | ||
| "project_info": { | ||
| "project_number": "000000000000", | ||
| "project_id": "dummy-project" | ||
| }, | ||
| "client": [{ | ||
| "client_info": { | ||
| "mobilesdk_app_id": "1:000000000000:android:0000000000000000", | ||
| "android_client_info": { | ||
| "package_name": "com.konkuk.medicarecall" | ||
| } | ||
| } | ||
| }], | ||
| "api_key": [{ | ||
| "current_key": "DUMMY_API_KEY" | ||
| }] | ||
| } | ||
| EOF |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
요 부분은 아래 ci 파일 참고해서 작성해주세요!
secret 으로 넘겨야합니다..
https://github.com/OurMenu/OurMenu-Android-V2/blob/develop/.github/workflows/android-ci.yml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
.github/workflows/android-ci.yml (3)
26-35: Gradle 캐시 이중 적용(중복 캐싱) — 하나만 사용하세요setup-java의 cache: gradle와 actions/cache가 둘 다 Gradle 캐시를 다뤄 중복입니다. 하나만 유지하세요(보통 setup-java만).
불필요한 캐시 스텝 제거:
- - name: Cache Gradle packages - uses: actions/cache@v4 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/buildSrc/**/*.kt') }} - restore-keys: | - ${{ runner.os }}-gradle-
36-39: local.properties 파일 쓰기 방식 개선(append → overwrite, 안전한 출력)는 중복 내용을 쌓습니다. 또한 echo보다 printf가 이스케이프에 안전합니다. 시크릿 가드도 함께 권장.
- - name: Generate local.properties - run: | - echo '${{ secrets.LOCAL_PROPERTIES }}' >> ./local.properties + - name: Generate local.properties + if: ${{ secrets.LOCAL_PROPERTIES != '' }} + run: | + printf '%s' "${{ secrets.LOCAL_PROPERTIES }}" > ./local.properties
41-43: google-services.json 쓰기 방식 개선 및 시크릿 가드같은 이유로 overwrite/printf 권장 + 시크릿 없을 때 스킵.
- - name: Generate google-services.json - run: | - echo '${{ secrets.GOOGLE_SERVICES }}' >> ./app/google-services.json + - name: Generate google-services.json + if: ${{ secrets.GOOGLE_SERVICES != '' }} + run: | + printf '%s' "${{ secrets.GOOGLE_SERVICES }}" > ./app/google-services.json옵션(로그 안전성 더 강화): 시크릿을 base64로 저장해 두고 CI에서 decode
- name: Generate google-services.json (base64) if: ${{ secrets.GOOGLE_SERVICES_B64 != '' }} run: | echo "${{ secrets.GOOGLE_SERVICES_B64 }}" | base64 -d > app/google-services.json
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.github/workflows/android-ci.yml(2 hunks)app/src/main/java/com/konkuk/medicarecall/MainActivity.kt(8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/konkuk/medicarecall/MainActivity.kt
🔇 Additional comments (1)
.github/workflows/android-ci.yml (1)
49-50: assembleDebug 전환 LGTM비표준 buildDebug → 표준 assembleDebug로 수정된 점 좋습니다.
| push: | ||
| branches: [ "develop" ] | ||
| pull_request: | ||
| branches: [ "develop" ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
포크 PR에서 시크릿 미제공 시 빌드 깨짐 위험 — 시크릿 의존 스텝 가드 필요
fork에서 오는 pull_request 이벤트엔 시크릿이 전달되지 않아 google-services.json/local.properties 생성 스텝이 빈 파일을 만들거나 실패 → 빌드 실패로 이어질 수 있습니다. 아래처럼 시크릿 존재 여부로 가드하세요.
추천 변경(해당 스텝 라인에서 if 추가):
- - name: Generate local.properties
- run: |
- echo '${{ secrets.LOCAL_PROPERTIES }}' >> ./local.properties
+ - name: Generate local.properties
+ if: ${{ secrets.LOCAL_PROPERTIES != '' }}
+ run: |
+ printf '%s' "${{ secrets.LOCAL_PROPERTIES }}" > ./local.properties
- - name: Generate google-services.json
- run: |
- echo '${{ secrets.GOOGLE_SERVICES }}' >> ./app/google-services.json
+ - name: Generate google-services.json
+ if: ${{ secrets.GOOGLE_SERVICES != '' }}
+ run: |
+ printf '%s' "${{ secrets.GOOGLE_SERVICES }}" > ./app/google-services.json옵션(포크 PR용 스텁 파일 생성 — 시크릿 없을 때만):
- name: Create stub google-services.json for fork PRs
if: ${{ github.event_name == 'pull_request' && secrets.GOOGLE_SERVICES == '' }}
run: |
printf '{ "project_info": {}, "client": [] }' > app/google-services.json옵션(중복 러너 낭비 방지):
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true🤖 Prompt for AI Agents
.github/workflows/android-ci.yml lines 4-7: pull_request workflows from forks
don't have repository secrets, so steps that rely on GOOGLE_SERVICES or other
secrets can create empty files or fail and break the build; add guards on those
steps to only run when the secret exists (e.g., if: github.event_name ==
'pull_request' && secrets.GOOGLE_SERVICES != '') or when running on push, and
optionally add a fallback step that creates a minimal stub
google-services.json/local.properties only when the secret is missing (if:
github.event_name == 'pull_request' && secrets.GOOGLE_SERVICES == '') so fork
PRs succeed; also consider adding a concurrency block to avoid duplicate runner
usage (concurrency group and cancel-in-progress: true).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
.github/workflows/android-ci.yml(1 hunks)
🧰 Additional context used
🪛 actionlint (1.7.7)
.github/workflows/android-ci.yml
28-28: the runner of "actions/cache@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue
(action)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-build
🔇 Additional comments (1)
.github/workflows/android-ci.yml (1)
17-24: 포크 PR에서도 빌드가 깨지지 않도록 시크릿 가드를 추가해주세요.포크에서 올라오는 pull_request 러너에는 시크릿이 비어 있어도 이 스텝이 그대로 실행돼 빈
local.properties/google-services.json이 생성되고, 이후 Gradle 빌드가 바로 실패합니다. 이전에도 동일 우려가 공유됐는데 아직 적용되지 않았습니다. 시크릿 존재 여부로 가드하고, 내용은printf로 그대로 써 주는 편이 안전합니다.- - name: Generate local.properties - run: | - echo "${{ secrets.LOCAL_PROPERTIES }}" > ./local.properties + - name: Generate local.properties + if: ${{ secrets.LOCAL_PROPERTIES != '' }} + run: | + printf '%s' "${{ secrets.LOCAL_PROPERTIES }}" > ./local.properties @@ - - name: Generate google-services.json - run: | - echo "${{ secrets.GOOGLE_SERVICES }}" > ./app/google-services.json + - name: Generate google-services.json + if: ${{ secrets.GOOGLE_SERVICES != '' }} + run: | + printf '%s' "${{ secrets.GOOGLE_SERVICES }}" > ./app/google-services.json
Updated the Android CI workflow to generate google-services.json using Base64 decoding and added comments for clarity.
librawish808
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
주석 덕분에 코드 흐름을 이해할 수 있었습니다👍
app/src/main/java/com/konkuk/medicarecall/data/api/AppPreferences.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/data/api/FcmService.kt
Outdated
Show resolved
Hide resolved
|
CI 아직도 실패했다고 뜨는데 혹시 해결 안된걸까요? 지민님이 말씀해주신대로 SharedPreferences -> DataStore로 바꾸는 게 좋을 것 같습니다 ! |
…oid into feat/fcm-alarm-#110
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 9
🧹 Nitpick comments (4)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (4)
37-37: serviceScope 생명주기 관리를 고려해보세요.
serviceScope가 생성되었지만onDestroy()에서 취소되지 않네요.FirebaseMessagingService는 시스템이 관리하는 서비스라 일반적으로 큰 문제는 없지만, 명시적으로 정리해주는 게 더 안전할 수 있어요.필요하다면 다음과 같이 추가할 수 있어요:
override fun onDestroy() { serviceScope.cancel() super.onDestroy() }
54-54: 서버 토큰 업데이트 로직 구현이 필요해요.TODO 주석이 남아있네요. FCM 토큰이 변경되면 서버에도 알려줘야 정상적으로 푸시를 받을 수 있어요.
이 부분 구현을 도와드릴까요? 예를 들어
FcmRepository나 별도의 API 인터페이스에updateFcmTokenToServer(token: String)같은 메서드를 추가하는 코드를 생성해드릴 수 있어요.
126-126: 알림 ID 관리 방식을 고려해보세요.랜덤 ID를 사용하면 나중에 특정 알림을 업데이트하거나 취소할 수 없어요. 케어콜 특성상 알림을 관리해야 할 수도 있으니, 의미있는 ID 체계를 고려해보는 게 좋을 것 같아요.
예를 들어:
remoteMessage.data["notification_id"]에서 ID를 가져오거나- 알림 타입별로 고정 ID를 사용하는 방식도 가능해요
118-118: Full screen intent 사용을 재검토해보세요.
setFullScreenIntent(contentPi, true)는 화면이 꺼져있을 때 앱을 전체화면으로 띄워요. 긴급 알림이 아니라면 사용자 경험 측면에서 다소 침해적일 수 있어요.케어콜의 알림 특성상 긴급성이 높다면 괜찮지만, 그렇지 않다면 제거하거나 특정 알림 타입에만 적용하는 걸 고려해보세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
app/src/main/java/com/konkuk/medicarecall/App.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/model/FcmToken.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repository/FcmRepository.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/util/FcmTokenEncryptor.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/util/FcmTokenSerializer.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/konkuk/medicarecall/data/repository/FcmRepository.kt
🧰 Additional context used
🪛 GitHub Actions: Android CI
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt
[error] 69-69: NoConsecutiveBlankLines: Needless blank line(s)
[error] 81-81: NoConsecutiveBlankLines: Needless blank line(s)
[error] 19-19: NoUnusedImports: Unused import
[error] 27-27: NoUnusedImports: Unused import
[error] 28-28: NoUnusedImports: Unused import
[error] 40-40: NoUnusedImports: Unused import
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
[error] 21-21: TrailingCommaOnDeclarationSite: Missing trailing comma before ")"
[error] 6-6: NoUnusedImports: Unused import
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt
[error] 41-41: BlockCommentInitialStarAlignment: Initial star should align with start of block comment
[error] 77-77: Indentation: Unexpected indentation (12) (should be 16) [Indentation]
[error] 78-78: Indentation: Unexpected indentation (16) (should be 20) [Indentation]
[error] 79-79: Indentation: Unexpected indentation (16) (should be 20) [Indentation]
[error] 80-80: Indentation: Unexpected indentation (16) (should be 20) [Indentation]
[error] 81-81: Indentation: Unexpected indentation (12) (should be 16) [Indentation]
[error] 82-82: Indentation: Unexpected indentation (16) (should be 20) [Indentation]
[error] 83-83: Indentation: Unexpected indentation (16) (should be 20) [Indentation]
[error] 84-84: Indentation: Unexpected indentation (12) (should be 16) [Indentation]
[error] 85-85: Indentation: Unexpected indentation (12) (should be 16) [Indentation]
🔇 Additional comments (1)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (1)
62-64: 좋아요!메시지 수신 시 로깅하고 알림을 표시하는 로직이 깔끔하네요.
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
Outdated
Show resolved
Hide resolved
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
Show resolved
Hide resolved
| val encryptedBytesDecoded = Base64.getDecoder().decode(raw) | ||
| val decryptedBytes = FcmTokenEncryptor.decrypt(encryptedBytesDecoded) | ||
| val decodedJsonString = decryptedBytes.decodeToString() | ||
| return Json.decodeFromString(decodedJsonString) | ||
| } | ||
|
|
||
| override suspend fun writeTo(t: FcmToken, output: OutputStream) { | ||
| val json = Json.Default.encodeToString(t) | ||
| val bytes = json.toByteArray() | ||
| val encryptedBytes = FcmTokenEncryptor.encrypt(bytes) | ||
| val encryptedBytesBase64 = Base64.getEncoder().encode(encryptedBytes) | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API 26 미만 기기에서 Base64 사용 시 크래시 발생
java.util.Base64 클래스는 Android API 26 이상에서만 제공됩니다. 우리 앱(minSdk 24 기준)처럼 26 미만 기기를 지원하면 DataStore를 읽거나 쓸 때 NoClassDefFoundError가 발생해 앱이 바로 죽습니다. android.util.Base64(NO_WRAP 플래그)로 대체해 주세요.
- import java.util.Base64
+import android.util.Base64
@@
- val encryptedBytesDecoded = Base64.getDecoder().decode(raw)
+ val encryptedBytesDecoded = Base64.decode(raw, Base64.NO_WRAP)
@@
- val encryptedBytesBase64 = Base64.getEncoder().encode(encryptedBytes)
+ val encryptedBytesBase64 = Base64.encode(encryptedBytes, Base64.NO_WRAP)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/util/FcmTokenSerializer.kt
around lines 24 to 35, the code uses java.util.Base64 which is only available on
API 26+ and will crash on minSdk 24; replace usages with android.util.Base64
(use Base64.decode(raw, Base64.NO_WRAP) to get bytes and
Base64.encode(encryptedBytes, Base64.NO_WRAP) to produce a byte[]/String as
needed), update imports to android.util.Base64, and ensure types match (convert
byte[] to String when necessary) so DataStore read/write works on API <26.
...rc/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt
Outdated
Show resolved
Hide resolved
...rc/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt
Outdated
Show resolved
Hide resolved
...rc/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt
Outdated
Show resolved
Hide resolved
Co-authored-by: alswlekk <[email protected]>
…ove unused import Co-authored-by: alswlekk <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt (1)
75-75: 불필요한 빈 줄 제거가 필요해요.CI가 여기서 걸리고 있네요. 라인 75의 빈 줄 때문에 린터 에러가 발생하고 있어요. 이 빈 줄만 제거하면 바로 통과할 거예요.
다음 diff를 적용해주세요:
} - @OptIn(ExperimentalFoundationApi::class) @Composable fun MedicineDetailLayout(
🧹 Nitpick comments (1)
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt (1)
31-35: clearToken 구현 패턴 확인이 필요해요.
saveFcmToken에서는it.copy(fcmToken = token)패턴을 사용하는데,clearToken에서는 새 인스턴스FcmToken(null)을 생성하고 있습니다.
FcmToken이 단일 필드만 가진다면 문제없지만, 일관성을 위해 다음과 같이 수정하는 것을 고려해보세요:override suspend fun clearToken() { context.fcmDataStore.updateData { - FcmToken(null) + it.copy(fcmToken = null) } }이렇게 하면
FcmToken에 나중에 필드가 추가되어도 다른 필드 값들이 보존됩니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt
🧰 Additional context used
🪛 GitHub Actions: Android CI
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt
[error] 75-75: Detekt: Needless blank line(s) [NoConsecutiveBlankLines]
🔇 Additional comments (5)
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt (1)
76-86: MedicineDetailLayout 추출은 좋은 개선이에요.UI 로직을 별도 함수로 분리하신 건 테스트 가능성과 재사용성 측면에서 좋은 선택이에요. 함수 시그니처도 깔끔하고 매개변수 구성도 적절하네요.
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt (4)
1-11: 임포트 정리 완료!과거 리뷰에서 지적된 불필요한 import가 깔끔하게 제거되었네요. 현재 모든 import가 실제로 사용되고 있습니다.
13-16: DataStore 설정 잘 되어있어요.
FcmTokenSerializer를 통한 암호화도 제대로 적용되었고, 파일명도 명확합니다. PR 목표대로 SharedPreferences에서 DataStore로 마이그레이션이 잘 이루어졌네요.
18-21: DI 구성 완벽합니다!과거 리뷰에서 지적된 trailing comma도 추가되었고,
@Singleton과@ApplicationContext사용이 적절합니다. Repository 패턴과 Hilt 설정이 깔끔하게 되어있네요.
22-24: 토큰 저장 로직 깔끔해요.
updateData와copy패턴을 사용해서 안전하게 업데이트하고 있습니다.
…ssues Fix Detekt style violations: indentation, blank lines, unused import
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (3)
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt (1)
18-21: 아직 trailing comma가 빠져있어요 (Pipeline 실패 중)이전 리뷰에서 지적된 부분인데 아직 수정이 안 된 것 같아요. 마지막 파라미터 뒤에 콤마를 추가해주세요.
class FcmRepositoryImpl @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, ) : FcmRepository {app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (2)
7-7: 사용하지 않는 import를 제거하세요
ContentValues.TAG를 import했지만 134번 라인에서 자체TAG를 정의하고 있어서 이 import는 불필요해요.-import android.content.ContentValues.TAG
39-41: 블록 주석 포맷을 수정해주세요 (Pipeline 실패 중)이전 리뷰에서 지적되었지만 아직 수정이 안 된 것 같아요. 별표 정렬을 맞춰주세요.
- /* FCM에서 해당 기기의 토큰 바꼈다고 알려줄 때 호출되는 콜백(앱 처음 설치, 앱데이터 삭제, FCM 내부 정책 변경 등) - FCM쪽에서 알아서 불러주는 콜백(개발자가 직접 호출 X,호출됐을 때 어떻게 저장할 지에 대한 코드) - * */ + /* + * FCM에서 해당 기기의 토큰 바꼈다고 알려줄 때 호출되는 콜백(앱 처음 설치, 앱데이터 삭제, FCM 내부 정책 변경 등) + * FCM쪽에서 알아서 불러주는 콜백(개발자가 직접 호출 X,호출됐을 때 어떻게 저장할 지에 대한 코드) + */
🧹 Nitpick comments (1)
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt (1)
31-35: 일관성을 위해copy사용을 권장해요
saveFcmToken에서는it.copy(fcmToken = ...)를 사용하는데, 여기서는 새 인스턴스를 생성하고 있어요. 일관성을 위해 같은 방식을 사용하는 게 좋을 것 같아요.override suspend fun clearToken() { context.fcmDataStore.updateData { - FcmToken(null) + it.copy(fcmToken = null) } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt
🧰 Additional context used
🪛 GitHub Actions: Android CI
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
[error] 20-20: Detekt: TrailingCommaOnDeclarationSite - Missing trailing comma before ")"
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt
[error] 41-41: Detekt: BlockCommentInitialStarAlignment - Initial star should align with start of block comment
🔇 Additional comments (9)
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt (3)
13-16: DataStore 설정 깔끔하네요!암호화된 serializer를 사용해서 FCM 토큰을 안전하게 저장하는 구조가 잘 구성되어 있어요.
22-24: 저장 로직 깔끔해요
updateData와copy를 사용한 간결한 구현이 좋습니다.
26-29: 조회 로직도 잘 구현되어 있어요
data.first()로 현재 값을 가져오는 표준 패턴이네요.app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (6)
31-37: DI 설정과 코루틴 스코프가 잘 구성되어 있어요이전 리뷰에서 제안된
SupervisorJob사용이 적용되어 에러 전파를 방지하고 있네요.
42-59: 토큰 저장 로직이 잘 구현되어 있어요이전 리뷰에서 제안된 try-catch 에러 핸들링이 추가되어 있고, 로깅도 적절하네요. 54번 라인의 서버 API 호출 관련 주석도 나중에 구현할 때 도움이 될 것 같아요.
61-65: 메시지 수신 처리가 깔끔해요로깅 후
showNotification으로 위임하는 구조가 명확하네요.
72-87: 채널 생성 로직이 개선되었어요이전 리뷰에서 지적된 채널 재생성 문제가 해결되어 있네요. 이제 채널이 없을 때만 생성하도록 되어 있어요.
89-119: 알림 빌더 설정이 잘 되어 있어요PendingIntent 플래그, 제목/본문 fallback, full-screen intent 등이 모두 적절하게 구성되어 있네요. 락스크린에서도 잘 보일 것 같아요.
120-131: 권한 체크 후 알림 표시가 안전하게 구현되어 있어요POST_NOTIFICATIONS 권한을 확인하고, 없으면 로그를 남기는 방식이 좋아요. 126번 라인의 랜덤 ID는 각각 독립적인 알림을 표시하는 현재 목적에 적합하네요.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (2)
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt (1)
75-75: 불필요한 빈 줄을 제거해주세요.CI가 여기서 연속된 빈 줄이 있다고 지적하고 있네요. 함수 사이에 빈 줄 하나만 남기고 이 줄을 제거해주시면 됩니다.
다음 diff를 적용해주세요:
) } - @OptIn(ExperimentalFoundationApi::class)app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (1)
38-40: 블록 주석 포맷을 수정하세요 (Pipeline 실패)주석의 별표 정렬이 잘못되어 CI에서
BlockCommentInitialStarAlignment에러가 발생하고 있어요.이렇게 수정해주세요:
- /* FCM에서 해당 기기의 토큰 바꼈다고 알려줄 때 호출되는 콜백(앱 처음 설치, 앱데이터 삭제, FCM 내부 정책 변경 등) - FCM쪽에서 알아서 불러주는 콜백(개발자가 직접 호출 X,호출됐을 때 어떻게 저장할 지에 대한 코드) - * */ + /* + * FCM에서 해당 기기의 토큰 바꼈다고 알려줄 때 호출되는 콜백(앱 처음 설치, 앱데이터 삭제, FCM 내부 정책 변경 등) + * FCM쪽에서 알아서 불러주는 콜백(개발자가 직접 호출 X,호출됐을 때 어떻게 저장할 지에 대한 코드) + */
🧹 Nitpick comments (2)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (2)
53-53: TODO 주석을 이슈로 트래킹하면 좋을 것 같아요서버에 새 토큰을 알려주는 기능이 필요할 것 같은데, 나중에 잊지 않도록 이슈로 만들어두면 어떨까요?
원하시면 제가 이슈 생성을 도와드릴 수 있어요!
117-117: fullScreenIntent 사용을 재검토해보세요
setFullScreenIntent(contentPi, true)는 화면이 꺼져있을 때 전체화면으로 액티비티를 띄우는 기능이에요. 이건 알람이나 수신 전화 같은 매우 중요한 알림에만 사용하는게 일반적이에요. 일반 푸시 알림에 사용하면 사용자가 부담스러워할 수 있어요.일반 알림이라면 이 줄을 제거하는 걸 고려해보세요:
.setContentIntent(contentPi) .setAutoCancel(true) - .setFullScreenIntent(contentPi, true) // 화면 꺼져있을 때 팝업정말 중요한 알림(예: 긴급 케어콜)에만 사용하고 싶다면, 알림 종류를 구분해서 특정 케이스에만 적용하는 방법도 있어요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
🧰 Additional context used
🪛 GitHub Actions: Android CI
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt
[error] 75-75: Detekt: NoConsecutiveBlankLines - Needless blank line(s).
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt
[error] 40-40: Detekt: BlockCommentInitialStarAlignment - Initial star should align with start of block comment.
🔇 Additional comments (2)
app/src/main/java/com/konkuk/medicarecall/ui/feature/homedetail/medicine/screen/MedicineDetail.kt (1)
76-135: 좋은 리팩토링이네요!상태를 가진
MedicineDetail에서 순수한 프레젠테이션 레이어인MedicineDetailLayout을 분리한 것은 좋은 접근이에요. 테스트 가능성도 높아지고 재사용성도 좋아집니다. 파라미터 구조도 깔끔하고 UI 구성도 잘 되어 있네요.app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (1)
60-64: 깔끔한 구현이에요!메시지 수신 처리가 명확하게 분리되어 있고, 로깅도 적절해요.
| @Inject | ||
| lateinit var fcmRepository: FcmRepository | ||
|
|
||
| private val serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serviceScope 생명주기 관리 추가 필요
SupervisorJob을 사용한 건 좋은 개선이에요! 다만 서비스가 종료될 때 scope를 취소하지 않으면 메모리 누수가 발생할 수 있어요.
onDestroy()를 추가해서 scope를 정리해주세요:
override fun onDestroy() {
serviceScope.cancel()
super.onDestroy()
}🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt around
line 36, the CoroutineScope created with SupervisorJob + Dispatchers.IO isn't
cancelled on service teardown which can cause leaks; add an override of
onDestroy() that calls serviceScope.cancel() and then calls super.onDestroy() to
properly clean up the scope when the service is destroyed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (1)
36-36: serviceScope 생명주기 관리가 여전히 누락되어 있어요이전 리뷰에서 지적된 내용인데,
onDestroy()에서serviceScope.cancel()을 호출해야 메모리 누수를 방지할 수 있어요.다음과 같이
onDestroy()를 추가해주세요:+ override fun onDestroy() { + serviceScope.cancel() + super.onDestroy() + }
🧹 Nitpick comments (1)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (1)
55-55: 서버 토큰 동기화 구현이 필요하신가요?주석에 서버에 토큰을 전송하는 로직이 필요하다고 언급되어 있는데, 이 부분 구현을 도와드릴 수 있어요.
필요하시면 API 호출 코드를 생성하거나, 이슈를 열어서 나중에 작업하도록 할 수 있어요!
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-build
🔇 Additional comments (2)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (2)
48-59: 에러 처리가 잘 되어 있네요!try-catch로 토큰 저장 실패를 적절히 처리하고, 로깅도 명확하게 되어 있어서 디버깅하기 좋아 보여요.
122-131: 권한 체크 로직이 적절해요알림을 표시하기 전에
POST_NOTIFICATIONS권한을 확인하고, 권한이 없을 때는 로그를 남기는 방식이 좋아 보여요. 랜덤 notification ID 사용도 각 알림을 고유하게 표시하기에 적합해요.
| .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) | ||
| .setContentIntent(contentPi) | ||
| .setAutoCancel(true) | ||
| .setFullScreenIntent(contentPi, true) // 화면 꺼져있을 때 팝업 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Android 14+ 에서 전체 화면 인텐트 권한 확인이 필요해요
setFullScreenIntent를 사용하면 화면이 꺼져있을 때 알림을 팝업으로 표시할 수 있는데, Android 14(API 34) 이상에서는 USE_FULL_SCREEN_INTENT 권한이 필요해요.
다음 스크립트로 매니페스트에 권한이 선언되어 있는지 확인해주세요:
권한이 없다면 Android 14+ 기기에서 전체 화면 알림이 작동하지 않을 수 있어요.
🏁 Script executed:
#!/bin/bash
# AndroidManifest.xml에 USE_FULL_SCREEN_INTENT 권한이 있는지 확인
rg -n "USE_FULL_SCREEN_INTENT" app/src/main/AndroidManifest.xmlLength of output: 0
🏁 Script executed:
#!/bin/bash
# Find all AndroidManifest.xml files and check for USE_FULL_SCREEN_INTENT permission
echo "=== Finding AndroidManifest.xml files ==="
fd "AndroidManifest.xml" app/
echo -e "\n=== Checking all manifests for USE_FULL_SCREEN_INTENT ==="
fd "AndroidManifest.xml" app/ --exec cat {} \; | rg -n "USE_FULL_SCREEN_INTENT"
echo -e "\n=== Checking build.gradle for targetSdkVersion ==="
fd "build.gradle.kts|build.gradle" app/ -x grep -n "targetSdk\|targetSdkVersion" {}Length of output: 211
USE_FULL_SCREEN_INTENT 권한이 AndroidManifest.xml에 선언되어야 해요
코드에서 setFullScreenIntent()를 사용하고 있는데, 앱의 targetSdk가 36(Android 15)으로 설정되어 있네요. Android 12 이상에서는 USE_FULL_SCREEN_INTENT 권한을 반드시 선언해야 이 기능이 작동합니다.
현재 app/src/main/AndroidManifest.xml에 이 권한이 없으면, Android 12 이상의 기기에서 전체 화면 알림이 작동하지 않을 거예요.
해결 방법:
AndroidManifest.xml의 <manifest> 태그 내에 다음을 추가하세요:
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt around
line 119 the code calls setFullScreenIntent(contentPi, true) but the required
permission USE_FULL_SCREEN_INTENT is not declared; add the permission
declaration to AndroidManifest.xml by inserting the uses-permission entry for
android.permission.USE_FULL_SCREEN_INTENT inside the <manifest> tag so
full-screen notifications work on Android 12+ (targetSdk 36).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
app/build.gradle.kts (1)
122-122: 중요: KSP 기반 Hilt Manager는 빌드 실패 야기 가능 (과거 리뷰 지적 사항)AndroidX Hilt Compiler는 KAPT만 지원하므로,
ksp(libs.hilt.manager)는 빌드를 깨뜨릴 확률이 높습니다. 불필요하면 제거하거나, 필요하면 KAPT로 전환하세요.- ksp(libs.hilt.manager)WorkManager 등 AndroidX Hilt 확장을 사용하려면 KAPT 플러그인 추가 + kapt 의존성 전환이 필요합니다.
🧹 Nitpick comments (1)
app/build.gradle.kts (1)
84-86: 의존성 버전을 libs.versions.toml에 중앙화 권장DataStore, Coroutines, Kotlin Serialization, Retrofit, OkHttp 등의 버전이 하드코딩되어 있습니다. 버전 관리 일관성을 위해 이들을
libs.versions.toml에 정의하고libs.xxx참조 방식으로 통일하는 것을 권장합니다.- implementation("androidx.datastore:datastore-preferences:1.1.1") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1") + implementation(libs.androidx.datastore.preferences) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.coroutines.android)(libs.versions.toml에 해당 항목이 이미 정의되어 있다면 바로 적용 가능)
Also applies to: 98-98, 101-108
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
app/build.gradle.kts(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: ci-build
🔇 Additional comments (3)
app/build.gradle.kts (3)
11-11: Google Services 플러그인 추가 적절FCM 통합 구현에 필요한 Google Services 플러그인 추가로 Firebase 설정이 정상적으로 적용됩니다.
61-61: buildConfig 활성화 적절BASE_URL을 buildConfigField로 주입하기 위해 필요한 설정으로, 정상적으로 추가되었습니다.
115-118: Firebase 의존성 추가 적절Firebase BOM과 Cloud Messaging 라이브러리가 정상적으로 추가되어 FCM 통합 구현을 지원합니다.
|
LGTM |
…oid into feat/fcm-alarm-#110
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
app/src/main/java/com/konkuk/medicarecall/data/repository/MemberRegisterRepository.kt (1)
8-14: Trailing comma를 추가해주세요.ktlint가 닫는 괄호 앞 trailing comma 누락을 감지했습니다.
다음 diff를 적용해서 수정하세요:
suspend fun registerMember( token: String, name: String, birthDate: String, gender: GenderType, - fcmToken: String + fcmToken: String, ): Result<MemberTokenResponseDto>app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt (1)
28-34: 생성자 파라미터에 trailing comma 하나만 더 넣으면 ktlint 해결됩니다
LoginViewModel생성자에fcmRepository추가한 건 좋습니다. 다만 멀티라인 파라미터 리스트라서 Line 33의 마지막 파라미터 뒤에 콤마가 없어서TrailingCommaOnDeclarationSite에러가 나고 있어요.class LoginViewModel @Inject constructor( private val verificationRepository: VerificationRepository, private val memberRegisterRepository: MemberRegisterRepository, private val dataStoreRepository: DataStoreRepository, private val checkLoginStatusUseCase: CheckLoginStatusUseCase, - private val fcmRepository: FcmRepository + private val fcmRepository: FcmRepository, ) : ViewModel() {app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/MemberRegisterRepositoryImpl.kt (1)
16-31: 회원가입에 fcmToken 전달은 잘 붙였고, trailing comma만 추가해 주시면 됩니다
registerMember시그니처와MemberRegisterRequestDto생성자 모두 멀티라인이라, 마지막 인자 뒤에 콤마가 없어서TrailingComma관련 에러가 발생하고 있어요.override suspend fun registerMember( token: String, name: String, birthDate: String, gender: GenderType, - fcmToken: String + fcmToken: String, ): Result<MemberTokenResponseDto> = ... MemberRegisterRequestDto( name, birthDate, gender, - fcmToken + fcmToken, ),이렇게만 정리하면 이 파일의 ktlint 에러는 모두 사라질 것 같습니다.
♻️ Duplicate comments (2)
app/build.gradle.kts (1)
122-122:⚠️ 중요: 이전 리뷰에서 지적된 KSP 호환성 문제 여전히 미해결
ksp(libs.hilt.manager)는 빌드를 실패시킬 가능성이 높습니다.androidx.hilt:hilt-compiler는 KAPT만 지원하며 KSP는 미지원합니다.두 가지 해결책:
옵션 1 (불필요 시, 권장): 제거
- ksp(libs.hilt.manager)옵션 2 (필요 시): KAPT로 전환
plugins블록에 추가:id("org.jetbrains.kotlin.kapt")그리고 라인 122를 수정:
- ksp(libs.hilt.manager) + kapt(libs.hilt.manager)WorkManager나 AndroidX Hilt 확장을 사용 중이라면 옵션 2를, 그렇지 않으면
libs.versions.toml에서hilt-manager항목을 삭제하는 것을 권장합니다.app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (1)
41-67:serviceScope를 onDestroy에서 cancel하지 않으면 서비스 종료 후 코루틴이 남을 수 있어요
SupervisorJob을 사용한 IO 스코프는 좋은데, 현재는 서비스 생명주기와 연결되지 않아서 서비스가 종료되어도 작업이 계속 돌 수 있습니다. 기존 리뷰에서도 언급된 내용이라, 아래처럼onDestroy에서 정리해 두는 게 안전합니다.override fun onDestroy() { serviceScope.cancel() super.onDestroy() }이 메서드를
FcmService안에 추가해 주세요.
🧹 Nitpick comments (3)
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmUpdateService.kt (1)
12-12: 요청 body에 Map 대신 DTO 사용을 고려해보세요.현재
Map<String, String>를 사용하고 있는데, 간단한 payload지만FcmTokenUpdateRequestDto같은 전용 DTO를 만들면 타입 안전성이 높아지고 API 계약이 명확해집니다. 다른 DTO들과의 일관성도 좋아지구요.DTO를 추가하려면:
// FcmTokenUpdateRequestDto.kt data class FcmTokenUpdateRequestDto( val fcmToken: String, )그리고 서비스 인터페이스 수정:
- @Body body: Map<String, String>, // {"fcmToken": "string"} + @Body body: FcmTokenUpdateRequestDto,app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt (2)
52-66:FcmRepositoryImpl로 캐스팅하지 않고도 인터페이스로 바로 호출할 수 있어요
FcmRepository인터페이스에 이미validateAndRefreshTokenIfNeeded가 정의되어 있어서, 구현체로 캐스팅할 필요가 없고, 구현 교체에도 조금 더 유연해집니다.- // 서버 갱신 (jwtToken은 DataStore에서 가져오거나, 로그인 시 저장된 값을 사용) - val jwtToken = dataStoreRepository.getAccessToken() ?: return@launch - (fcmRepository as? FcmRepositoryImpl)?.validateAndRefreshTokenIfNeeded(jwtToken) + // 서버 갱신 (jwtToken은 DataStore에서 가져오거나, 로그인 시 저장된 값을 사용) + val jwtToken = dataStoreRepository.getAccessToken() ?: return@launch + fcmRepository.validateAndRefreshTokenIfNeeded(jwtToken)이렇게 하면
import com.konkuk.medicarecall.data.repositoryimpl.FcmRepositoryImpl도 정리할 수 있습니다.
128-138: POST_NOTIFICATIONS 권한 체크는 Android 13+에서만 필요하니, SDK 버전 조건을 같이 쓰는 걸 추천해요현재는 모든 버전에서
Manifest.permission.POST_NOTIFICATIONS를 체크하고 있는데, 일반적으로는 Android 13(API 33) 이상에서만 런타임 권한을 요구합니다. 보통은 아래처럼 버전 조건을 같이 두는 패턴을 많이 씁니다.- if (ActivityCompat.checkSelfPermission( - this, - Manifest.permission.POST_NOTIFICATIONS, - ) == PackageManager.PERMISSION_GRANTED - ) { + if ( + Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU || + ActivityCompat.checkSelfPermission( + this, + Manifest.permission.POST_NOTIFICATIONS, + ) == PackageManager.PERMISSION_GRANTED + ) { val notificationId = (0..Int.MAX_VALUE).random() NotificationManagerCompat.from(this).notify(notificationId, builder.build()) } else { Log.w(TAG, "POST_NOTIFICATIONS 권한이 없어 알림을 표시하지 못했습니다.") }이렇게 하면 13 미만에서는 권한 체크 없이 바로 알림을 보내고, 13 이상에서만 실제 권한 상태를 보고 분기하게 됩니다. 실제 동작은 Android 버전별 문서를 한 번 더 확인해 주시면 좋겠습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
app/build.gradle.kts(3 hunks)app/src/main/AndroidManifest.xml(3 hunks)app/src/main/java/com/konkuk/medicarecall/MainActivity.kt(4 hunks)app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmUpdateService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmValidationService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/api/fcm/NotificationService.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/di/ApiModule.kt(2 hunks)app/src/main/java/com/konkuk/medicarecall/data/dto/request/MemberRegisterRequestDto.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/dto/request/NotificationStatusRequestDto.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/model/FcmToken.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repository/FcmRepository.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repository/MemberRegisterRepository.kt(2 hunks)app/src/main/java/com/konkuk/medicarecall/data/repository/NotificationRepository.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt(1 hunks)app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/MemberRegisterRepositoryImpl.kt(3 hunks)app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt(5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- app/src/main/java/com/konkuk/medicarecall/data/model/FcmToken.kt
- app/src/main/java/com/konkuk/medicarecall/MainActivity.kt
- app/src/main/java/com/konkuk/medicarecall/data/repository/FcmRepository.kt
🧰 Additional context used
🧬 Code graph analysis (1)
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt (1)
app/src/main/java/com/konkuk/medicarecall/data/repository/FcmRepository.kt (3)
getFcmToken(5-5)saveFcmToken(3-9)saveFcmToken(4-4)
🪛 GitHub Actions: Android CI
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmUpdateService.kt
[error] 12-12: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
app/src/main/java/com/konkuk/medicarecall/data/repository/NotificationRepository.kt
[error] 4-4: SpacingAroundColon. Unexpected spacing before ':'
app/src/main/java/com/konkuk/medicarecall/data/api/fcm/NotificationService.kt
[error] 13-13: SpacingAroundColon. Unexpected spacing before ':'
[error] 14-14: FunctionReturnTypeSpacing. Unexpected spacing after function return type
[error] 14-14: SpacingAroundColon. Unexpected spacing before ':'
[error] 14-14: Indentation. Unexpected indentation (8) (should be 4)
[error] 13-13: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
app/src/main/java/com/konkuk/medicarecall/data/dto/request/NotificationStatusRequestDto.kt
[error] 4-4: SpacingAroundColon. Unexpected spacing before ':'
[error] 4-4: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
app/src/main/java/com/konkuk/medicarecall/data/repository/MemberRegisterRepository.kt
[error] 4-4: NoUnusedImports. Unused import.
[error] 13-13: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
[error] 28-28: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
[error] 78-78: NoBlankLineBeforeRbrace. Unexpected blank line(s) before closing brace.
app/src/main/java/com/konkuk/medicarecall/data/di/ApiModule.kt
[error] 152-152: NoBlankLineBeforeRbrace. Unexpected blank line(s) before closing brace.
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt
[error] 33-33: Trailing comma missing before ')'. (TrailingCommaOnDeclarationSite)
[error] 142-142: NoBlankLineBeforeRbrace. Unexpected blank line(s) before closing brace.
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/MemberRegisterRepositoryImpl.kt
[error] 6-6: NoUnusedImports. Unused import.
[error] 30-30: TrailingCommaOnCallSite. Missing trailing comma before ')'
[error] 21-21: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
🔇 Additional comments (11)
app/build.gradle.kts (6)
11-11: Google Services 플러그인 추가 — FCM 통합 준비 완료! 👍Google Services 플러그인이 적절히 추가되었으니, Firebase BOM과 메시징 의존성을 활용할 수 있습니다. 좋은 구성입니다.
37-37: BASE_URL 설정 간소화 — 명확하고 깔끔해졌네요 ✨Kotlin의 문자열 보간으로 한 줄로 통합했습니다. Elvis 연산자로 안전한 기본값도 처리되어 있습니다.
61-61: buildConfig 활성화 — 필수 설정입니다 ✓BuildConfig 필드를 사용하므로 활성화가 필요했습니다. 올바르게 추가되었습니다.
83-86: DataStore & Coroutines 명시적 추가 — 토큰 저장 구조 완성 👍SharedPreferences에서 DataStore로의 전환 목표와 정확히 맞춰졌습니다. FCM 토큰 암호화 저장을 위해 필요한 코루틴 버전도 일관되게 설정되어 있습니다.
115-118: Firebase 의존성 적절히 추가됨 🔥BOM을 통한 버전 관리와 FCM 메시징 의존성이 올바르게 설정되었습니다. Analytics도 함께 추가되어 푸시 알림 추적에 유용할 것 같습니다.
108-108: 유의: Accompanist WebView가 RC(릴리스 후보) 버전입니다⚠️
accompanist-webview:0.24.13-rc는 아직 정식 버전이 아닙니다. 가능하면 안정화된 버전을 사용하는 걸 권장합니다. 필요한 특정 기능이 있다면 괜찮지만, 없다면 안정화 버전으로 업그레이드하세요.app/src/main/AndroidManifest.xml (3)
9-9: 알림 권한 추가 LGTM!Android 13(API 33) 이상에서 필수인 POST_NOTIFICATIONS 권한이 올바르게 추가되었습니다.
41-47: FCM 서비스 등록 LGTM!FirebaseMessagingService가 올바르게 등록되었고, exported=false로 보안도 적절히 설정되었습니다.
27-29: FCM 채널 메타데이터 확인 완료!AndroidManifest.xml의
fcm_alert값이 App.kt의FCM_CHANNEL_ID상수와 정확히 일치합니다. 채널 ID가 일관되게 설정되어 있으므로 문제없습니다.app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmValidationService.kt (1)
6-9: FCM 토큰 검증 서비스 LGTM!간결하고 명확한 Retrofit 인터페이스입니다. 토큰 검증 엔드포인트가 잘 정의되어 있습니다.
app/src/main/java/com/konkuk/medicarecall/data/dto/request/MemberRegisterRequestDto.kt (1)
11-11: fcmToken 필드 적절히 전달 완료검증 결과, fcmToken이 전체 레이어에서 올바르게 전달되고 있습니다:
- Repository 인터페이스 및 구현에 fcmToken 파라미터 포함 ✓
- ViewModel에서 Firebase로부터 토큰을 조회하여 registerMember() 호출 시 전달 ✓
- DTO에 정의된 fcmToken 필드로 최종 전달 ✓
회원 가입 플로우에서 FCM 토큰이 모든 계층을 통해 일관되게 전달되고 있습니다.
| suspend fun updateFcmToken( | ||
| @Header("Authorization") header: String? = null, // Optional header | ||
| @Body body: Map<String, String> // {"fcmToken": "string"} | ||
| ): Response<Unit> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Trailing comma를 추가해주세요.
ktlint가 닫는 괄호 앞 trailing comma 누락을 감지했습니다.
다음 diff를 적용해서 수정하세요:
suspend fun updateFcmToken(
@Header("Authorization") header: String? = null, // Optional header
- @Body body: Map<String, String> // {"fcmToken": "string"}
+ @Body body: Map<String, String>, // {"fcmToken": "string"}
): Response<Unit>🧰 Tools
🪛 GitHub Actions: Android CI
[error] 12-12: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/api/fcm/FcmUpdateService.kt
around lines 10 to 13, ktlint flagged a missing trailing comma before the
closing parenthesis of the updateFcmToken function parameter list; add a
trailing comma after the last parameter (the @Body body: Map<String, String>
entry) so the parameter list ends with a comma before the closing parenthesis to
satisfy ktlint formatting rules.
| suspend fun changeStatus( | ||
| @Path("notificationId") notificationId: String, | ||
| @Body status : NotificationStatusRequestDto | ||
| ) : Response<Unit> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
여러 코드 스타일 이슈를 수정해주세요.
ktlint가 여러 스타일 위반을 감지했습니다:
- 콜론 앞 불필요한 공백
- 리턴 타입 앞 불필요한 공백
- 닫는 괄호 들여쓰기 오류
- trailing comma 누락
다음 diff를 적용해서 수정하세요:
@POST("notifications/{notificationId}")
suspend fun changeStatus(
@Path("notificationId") notificationId: String,
- @Body status : NotificationStatusRequestDto
- ) : Response<Unit>
+ @Body status: NotificationStatusRequestDto,
+ ): Response<Unit>
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| suspend fun changeStatus( | |
| @Path("notificationId") notificationId: String, | |
| @Body status : NotificationStatusRequestDto | |
| ) : Response<Unit> | |
| suspend fun changeStatus( | |
| @Path("notificationId") notificationId: String, | |
| @Body status: NotificationStatusRequestDto, | |
| ): Response<Unit> |
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 13-13: SpacingAroundColon. Unexpected spacing before ':'
[error] 14-14: FunctionReturnTypeSpacing. Unexpected spacing after function return type
[error] 14-14: SpacingAroundColon. Unexpected spacing before ':'
[error] 14-14: Indentation. Unexpected indentation (8) (should be 4)
[error] 13-13: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/api/fcm/NotificationService.kt
around lines 11 to 14, fix ktlint style issues in the suspend fun signature:
remove the unnecessary space before colons so parameters read "status:
NotificationStatusRequestDto" and the return type reads ": Response<Unit>",
ensure the closing parenthesis is indented/aligned with the function start
(place it on its own line if multiline), and add a trailing comma after the last
parameter in the multiline parameter list so the signature conforms to ktlint
rules.
| @Provides | ||
| @Singleton | ||
| fun provideFcmValidationService(retrofit: Retrofit): FcmValidationService { | ||
| return retrofit.create(FcmValidationService::class.java) | ||
| } | ||
|
|
||
| @Provides | ||
| @Singleton | ||
| fun provideFcmUpdateService(retrofit: Retrofit): FcmUpdateService { | ||
| return retrofit.create(FcmUpdateService::class.java) | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FCM Retrofit 서비스 DI 추가는 OK인데, 마지막 공백 줄 때문에 ktlint가 깨지고 있어요
provideFcmValidationService, provideFcmUpdateService 구현은 기존 패턴이랑 잘 맞습니다. 다만 Line 152의 빈 줄 때문에 NoBlankLineBeforeRbrace 에러가 나고 있어서, 닫는 중괄호 바로 앞 공백만 지워주면 CI 통과할 것 같아요.
fun provideFcmUpdateService(retrofit: Retrofit): FcmUpdateService {
return retrofit.create(FcmUpdateService::class.java)
}
-
-}
+}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Provides | |
| @Singleton | |
| fun provideFcmValidationService(retrofit: Retrofit): FcmValidationService { | |
| return retrofit.create(FcmValidationService::class.java) | |
| } | |
| @Provides | |
| @Singleton | |
| fun provideFcmUpdateService(retrofit: Retrofit): FcmUpdateService { | |
| return retrofit.create(FcmUpdateService::class.java) | |
| } | |
| } | |
| @Provides | |
| @Singleton | |
| fun provideFcmValidationService(retrofit: Retrofit): FcmValidationService { | |
| return retrofit.create(FcmValidationService::class.java) | |
| } | |
| @Provides | |
| @Singleton | |
| fun provideFcmUpdateService(retrofit: Retrofit): FcmUpdateService { | |
| return retrofit.create(FcmUpdateService::class.java) | |
| } | |
| } |
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 152-152: NoBlankLineBeforeRbrace. Unexpected blank line(s) before closing brace.
🤖 Prompt for AI Agents
In app/src/main/java/com/konkuk/medicarecall/data/di/ApiModule.kt around lines
141 to 153, there's an extra blank line immediately before the closing brace
causing a ktlint NoBlankLineBeforeRbrace error; remove the empty line so the
closing '}' directly follows the previous method block without any intervening
blank line to satisfy the lint rule.
| data class NotificationStatusRequestDto( | ||
| val read : Boolean | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드 스타일 이슈를 수정해주세요.
ktlint가 두 가지 스타일 이슈를 발견했습니다:
- 콜론 앞 불필요한 공백
- 닫는 괄호 앞 trailing comma 누락
다음 diff를 적용해서 수정하세요:
data class NotificationStatusRequestDto(
- val read : Boolean
+ val read: Boolean,
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| data class NotificationStatusRequestDto( | |
| val read : Boolean | |
| ) | |
| data class NotificationStatusRequestDto( | |
| val read: Boolean, | |
| ) |
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 4-4: SpacingAroundColon. Unexpected spacing before ':'
[error] 4-4: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/data/dto/request/NotificationStatusRequestDto.kt
around lines 3 to 5, ktlint reports two style issues: remove the unnecessary
space before the colon in the property declaration (change "val read : Boolean"
to "val read: Boolean") and add a trailing comma before the closing parenthesis
of the data class parameter list; apply those two changes and save the file.
| package com.konkuk.medicarecall.data.repository | ||
|
|
||
| import com.konkuk.medicarecall.data.dto.response.MemberTokenResponseDto | ||
| import com.konkuk.medicarecall.data.model.FcmToken |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용하지 않는 import를 제거해주세요.
FcmToken import가 사용되지 않습니다. ktlint가 이를 감지했습니다.
다음 diff를 적용해서 수정하세요:
package com.konkuk.medicarecall.data.repository
import com.konkuk.medicarecall.data.dto.response.MemberTokenResponseDto
-import com.konkuk.medicarecall.data.model.FcmToken
import com.konkuk.medicarecall.ui.type.GenderType🧰 Tools
🪛 GitHub Actions: Android CI
[error] 4-4: NoUnusedImports. Unused import.
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/data/repository/MemberRegisterRepository.kt
around line 4, the import "com.konkuk.medicarecall.data.model.FcmToken" is
unused; remove that import line from the file (and ensure there are no remaining
references to FcmToken in this file), then re-run ktlint/gradle checks to
confirm the warning is resolved.
| @Singleton | ||
| class FcmRepositoryImpl @Inject constructor( | ||
| @ApplicationContext private val context: Context, | ||
| private val fcmValidationService: FcmValidationService, | ||
| private val fcmUpdateService: FcmUpdateService | ||
| ) : FcmRepository { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
생성자 마지막 파라미터 뒤에 콤마 하나만 추가하면 ktlint 에러가 사라집니다
FcmRepositoryImpl 생성자 파라미터가 멀티라인이라 fcmUpdateService 뒤에 trailing comma가 필요합니다 (TrailingCommaOnDeclarationSite).
@Singleton
class FcmRepositoryImpl @Inject constructor(
@ApplicationContext private val context: Context,
private val fcmValidationService: FcmValidationService,
- private val fcmUpdateService: FcmUpdateService
+ private val fcmUpdateService: FcmUpdateService,
) : FcmRepository {🧰 Tools
🪛 GitHub Actions: Android CI
[error] 28-28: TrailingCommaOnDeclarationSite. Missing trailing comma before ')'
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
around lines 24 to 29, the multiline constructor parameter list is missing a
trailing comma after the last parameter (`fcmUpdateService`), which triggers
ktlint's TrailingCommaOnDeclarationSite rule; add a comma after
`fcmUpdateService` (i.e., make the final constructor parameter line end with a
comma) so the declaration conforms to ktlint and formats correctly.
| override suspend fun clearToken() { | ||
| context.fcmDataStore.updateData { | ||
| FcmToken(null) | ||
| } | ||
| } | ||
|
|
||
| override suspend fun validateAndRefreshTokenIfNeeded(jwtToken: String) { | ||
| try { | ||
| val currentToken = getFcmToken() | ||
| Log.d("FcmRepositoryImpl", "현재 저장된 FCM 토큰: $currentToken") | ||
|
|
||
| // 서버에 토큰 유효성 검사 요청 | ||
| val isValid = runCatching { | ||
| fcmValidationService.validateToken().isSuccessful | ||
| }.getOrDefault(false) | ||
|
|
||
| if (isValid) { | ||
| Log.d("FcmRepositoryImpl", "FCM 토큰 유효함, 갱신 불필요") | ||
| return | ||
| } | ||
|
|
||
| // 유효하지 않으면 Firebase로 새 토큰 발급 | ||
| val newToken = FirebaseMessaging.getInstance().token.await() | ||
| saveFcmToken(newToken) | ||
| Log.d("FcmRepositoryImpl", "새 FCM 토큰 발급 및 저장 완료: $newToken") | ||
|
|
||
| // 서버에 갱신 요청 | ||
| val response = runCatching { | ||
| fcmUpdateService.updateFcmToken( | ||
| header = "Bearer $jwtToken", | ||
| body = mapOf("fcmToken" to newToken), | ||
| ) | ||
| } | ||
|
|
||
| response.onSuccess { | ||
| Log.d("FcmRepositoryImpl", "서버에 FCM 토큰 갱신 성공") | ||
| }.onFailure { | ||
| Log.e("FcmRepositoryImpl", "서버에 토큰 갱신 실패: ${it.message}") | ||
| } | ||
|
|
||
| } catch (e: Exception) { | ||
| Log.e("FcmRepositoryImpl", "validateAndRefreshTokenIfNeeded 실패: ${e.message}") | ||
| } | ||
| } | ||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
토큰 검증/갱신 로직은 좋고, 마지막 공백 줄만 정리하면 될 것 같아요
validateAndRefreshTokenIfNeeded의 흐름(검증 → 필요 시 Firebase에서 새 토큰 → DataStore 저장 → 서버 갱신)은 괜찮습니다.- Line 83의 빈 줄 때문에
NoBlankLineBeforeRbrace에러가 나고 있으니, 닫는 중괄호 바로 앞 공백만 제거해 주세요.
} catch (e: Exception) {
Log.e("FcmRepositoryImpl", "validateAndRefreshTokenIfNeeded 실패: ${e.message}")
}
- }
-
-}
+ }
+}참고로 currentToken은 지금 로그에만 쓰이고 있으니, 나중에 필요 없으면 변수 자체를 없애도 됩니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| override suspend fun clearToken() { | |
| context.fcmDataStore.updateData { | |
| FcmToken(null) | |
| } | |
| } | |
| override suspend fun validateAndRefreshTokenIfNeeded(jwtToken: String) { | |
| try { | |
| val currentToken = getFcmToken() | |
| Log.d("FcmRepositoryImpl", "현재 저장된 FCM 토큰: $currentToken") | |
| // 서버에 토큰 유효성 검사 요청 | |
| val isValid = runCatching { | |
| fcmValidationService.validateToken().isSuccessful | |
| }.getOrDefault(false) | |
| if (isValid) { | |
| Log.d("FcmRepositoryImpl", "FCM 토큰 유효함, 갱신 불필요") | |
| return | |
| } | |
| // 유효하지 않으면 Firebase로 새 토큰 발급 | |
| val newToken = FirebaseMessaging.getInstance().token.await() | |
| saveFcmToken(newToken) | |
| Log.d("FcmRepositoryImpl", "새 FCM 토큰 발급 및 저장 완료: $newToken") | |
| // 서버에 갱신 요청 | |
| val response = runCatching { | |
| fcmUpdateService.updateFcmToken( | |
| header = "Bearer $jwtToken", | |
| body = mapOf("fcmToken" to newToken), | |
| ) | |
| } | |
| response.onSuccess { | |
| Log.d("FcmRepositoryImpl", "서버에 FCM 토큰 갱신 성공") | |
| }.onFailure { | |
| Log.e("FcmRepositoryImpl", "서버에 토큰 갱신 실패: ${it.message}") | |
| } | |
| } catch (e: Exception) { | |
| Log.e("FcmRepositoryImpl", "validateAndRefreshTokenIfNeeded 실패: ${e.message}") | |
| } | |
| } | |
| } | |
| override suspend fun clearToken() { | |
| context.fcmDataStore.updateData { | |
| FcmToken(null) | |
| } | |
| } | |
| override suspend fun validateAndRefreshTokenIfNeeded(jwtToken: String) { | |
| try { | |
| val currentToken = getFcmToken() | |
| Log.d("FcmRepositoryImpl", "현재 저장된 FCM 토큰: $currentToken") | |
| // 서버에 토큰 유효성 검사 요청 | |
| val isValid = runCatching { | |
| fcmValidationService.validateToken().isSuccessful | |
| }.getOrDefault(false) | |
| if (isValid) { | |
| Log.d("FcmRepositoryImpl", "FCM 토큰 유효함, 갱신 불필요") | |
| return | |
| } | |
| // 유효하지 않으면 Firebase로 새 토큰 발급 | |
| val newToken = FirebaseMessaging.getInstance().token.await() | |
| saveFcmToken(newToken) | |
| Log.d("FcmRepositoryImpl", "새 FCM 토큰 발급 및 저장 완료: $newToken") | |
| // 서버에 갱신 요청 | |
| val response = runCatching { | |
| fcmUpdateService.updateFcmToken( | |
| header = "Bearer $jwtToken", | |
| body = mapOf("fcmToken" to newToken), | |
| ) | |
| } | |
| response.onSuccess { | |
| Log.d("FcmRepositoryImpl", "서버에 FCM 토큰 갱신 성공") | |
| }.onFailure { | |
| Log.e("FcmRepositoryImpl", "서버에 토큰 갱신 실패: ${it.message}") | |
| } | |
| } catch (e: Exception) { | |
| Log.e("FcmRepositoryImpl", "validateAndRefreshTokenIfNeeded 실패: ${e.message}") | |
| } | |
| } | |
| } |
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 78-78: NoBlankLineBeforeRbrace. Unexpected blank line(s) before closing brace.
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/FcmRepositoryImpl.kt
around lines 39 to 84, remove the extra blank line immediately before the
closing brace at line 83 to fix the NoBlankLineBeforeRbrace error; ensure the
file ends with the closing brace directly after the previous line (no blank
line), and optionally remove the now-unused currentToken variable and its Log
statement if you decide it’s unnecessary.
| import com.konkuk.medicarecall.data.api.member.MemberRegisterService | ||
| import com.konkuk.medicarecall.data.dto.request.MemberRegisterRequestDto | ||
| import com.konkuk.medicarecall.data.dto.response.MemberTokenResponseDto | ||
| import com.konkuk.medicarecall.data.repository.FcmRepository | ||
| import com.konkuk.medicarecall.data.repository.MemberRegisterRepository | ||
| import com.konkuk.medicarecall.ui.type.GenderType |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용되지 않는 FcmRepository import 때문에 lint가 실패하고 있어요
Line 6의 import com.konkuk.medicarecall.data.repository.FcmRepository는 이 파일에서 실제로 사용되지 않아서 NoUnusedImports 에러가 나는 상태입니다. 안전하게 삭제해도 될 것 같아요.
-import com.konkuk.medicarecall.data.repository.FcmRepository📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import com.konkuk.medicarecall.data.api.member.MemberRegisterService | |
| import com.konkuk.medicarecall.data.dto.request.MemberRegisterRequestDto | |
| import com.konkuk.medicarecall.data.dto.response.MemberTokenResponseDto | |
| import com.konkuk.medicarecall.data.repository.FcmRepository | |
| import com.konkuk.medicarecall.data.repository.MemberRegisterRepository | |
| import com.konkuk.medicarecall.ui.type.GenderType | |
| import com.konkuk.medicarecall.data.api.member.MemberRegisterService | |
| import com.konkuk.medicarecall.data.dto.request.MemberRegisterRequestDto | |
| import com.konkuk.medicarecall.data.dto.response.MemberTokenResponseDto | |
| import com.konkuk.medicarecall.data.repository.MemberRegisterRepository | |
| import com.konkuk.medicarecall.ui.type.GenderType |
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 6-6: NoUnusedImports. Unused import.
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/data/repositoryimpl/MemberRegisterRepositoryImpl.kt
around lines 3 to 8, the import for
com.konkuk.medicarecall.data.repository.FcmRepository is unused and causing a
NoUnusedImports lint error; remove that import line and save the file (then run
the linter or Gradle build to confirm the error is resolved).
| /** 인증번호 확인 및 로그인 처리 */ | ||
| fun confirmPhoneNumber(phone: String, code: String) { | ||
| viewModelScope.launch { | ||
| if (!debug) { | ||
| verificationRepository.confirmPhoneNumber(phone, code) | ||
| .onSuccess { | ||
| Log.d( | ||
| "httplog", | ||
| "${it.message} ${it.memberStatus} 액세스토큰: ${it.accessToken} 리프레시 토큰: ${it.refreshToken} ${it.verified} ${it.token} ", | ||
| ) | ||
| Log.d("httplog", "로그인 성공, 액세스토큰: ${it.accessToken}") | ||
|
|
||
| isVerified = it.verified | ||
| if (isVerified) { | ||
| token = it.token ?: "" | ||
| dataStoreRepository.saveAccessToken(it.accessToken ?: "") | ||
| dataStoreRepository.saveRefreshToken(it.refreshToken ?: "") | ||
|
|
||
| // 로그인 후 FCM 토큰 유효성 검사 | ||
| it.accessToken?.let { jwt -> | ||
| fcmRepository.validateAndRefreshTokenIfNeeded(jwt) | ||
| Log.d("httplog", "FCM 토큰 유효성 검사 및 갱신 완료") | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
validateAndRefreshTokenIfNeeded는 suspend라, 현재 위치에서 바로 호출하면 컴파일 이슈가 날 수 있어요
FcmRepository 인터페이스에서 validateAndRefreshTokenIfNeeded가 suspend로 정의되어 있는데, 지금은 Result.onSuccess { ... } 블록 안에서 그냥 호출하고 있어서 컴파일 에러가 발생할 가능성이 큽니다.
viewModelScope.launch 안이긴 하지만, onSuccess의 람다는 일반 함수 타입이라서 suspend 호출을 직접 넣을 수 없어요. 아래처럼 내부에서 한 번 더 코루틴을 띄우는 식으로 옮기는 게 안전합니다.
- // 로그인 후 FCM 토큰 유효성 검사
- it.accessToken?.let { jwt ->
- fcmRepository.validateAndRefreshTokenIfNeeded(jwt)
- Log.d("httplog", "FCM 토큰 유효성 검사 및 갱신 완료")
- }
+ // 로그인 후 FCM 토큰 유효성 검사
+ it.accessToken?.let { jwt ->
+ viewModelScope.launch {
+ fcmRepository.validateAndRefreshTokenIfNeeded(jwt)
+ Log.d("httplog", "FCM 토큰 유효성 검사 및 갱신 완료")
+ }
+ }이렇게 하면 suspend 호출이 코루틴 블록 안으로 들어가서 컴파일도 되고, 구조도 명확해집니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** 인증번호 확인 및 로그인 처리 */ | |
| fun confirmPhoneNumber(phone: String, code: String) { | |
| viewModelScope.launch { | |
| if (!debug) { | |
| verificationRepository.confirmPhoneNumber(phone, code) | |
| .onSuccess { | |
| Log.d( | |
| "httplog", | |
| "${it.message} ${it.memberStatus} 액세스토큰: ${it.accessToken} 리프레시 토큰: ${it.refreshToken} ${it.verified} ${it.token} ", | |
| ) | |
| Log.d("httplog", "로그인 성공, 액세스토큰: ${it.accessToken}") | |
| isVerified = it.verified | |
| if (isVerified) { | |
| token = it.token ?: "" | |
| dataStoreRepository.saveAccessToken(it.accessToken ?: "") | |
| dataStoreRepository.saveRefreshToken(it.refreshToken ?: "") | |
| // 로그인 후 FCM 토큰 유효성 검사 | |
| it.accessToken?.let { jwt -> | |
| fcmRepository.validateAndRefreshTokenIfNeeded(jwt) | |
| Log.d("httplog", "FCM 토큰 유효성 검사 및 갱신 완료") | |
| } | |
| } | |
| /** 인증번호 확인 및 로그인 처리 */ | |
| fun confirmPhoneNumber(phone: String, code: String) { | |
| viewModelScope.launch { | |
| if (!debug) { | |
| verificationRepository.confirmPhoneNumber(phone, code) | |
| .onSuccess { | |
| Log.d("httplog", "로그인 성공, 액세스토큰: ${it.accessToken}") | |
| isVerified = it.verified | |
| if (isVerified) { | |
| token = it.token ?: "" | |
| dataStoreRepository.saveAccessToken(it.accessToken ?: "") | |
| dataStoreRepository.saveRefreshToken(it.refreshToken ?: "") | |
| // 로그인 후 FCM 토큰 유효성 검사 | |
| it.accessToken?.let { jwt -> | |
| viewModelScope.launch { | |
| fcmRepository.validateAndRefreshTokenIfNeeded(jwt) | |
| Log.d("httplog", "FCM 토큰 유효성 검사 및 갱신 완료") | |
| } | |
| } |
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt
around lines 78 to 97, the call to
fcmRepository.validateAndRefreshTokenIfNeeded(jwt) is a suspend function but is
being invoked directly inside Result.onSuccess { ... } (a non-suspend lambda),
which will cause a compile error; fix it by moving the suspend call into a
coroutine scope (e.g., call viewModelScope.launch {
fcmRepository.validateAndRefreshTokenIfNeeded(jwt) } or use
lifecycleScope/withContext as appropriate), ensuring you pass the jwt captured
from it.accessToken and keep the surrounding logic (logging) after the coroutine
completes or move the log inside the launched coroutine.
| /** 회원가입 */ | ||
| fun memberRegister(name: String, birthDate: String, gender: GenderType) { | ||
| viewModelScope.launch { | ||
| if (!debug) { | ||
| try { | ||
| if (debug) { | ||
| _events.emit(LoginEvent.MemberRegisterSuccess) | ||
| return@launch | ||
| } | ||
|
|
||
| // 🔹 이미 로그인 단계에서 FCM 토큰은 유효성 검증 완료됨 | ||
| val fcmToken = FirebaseMessaging.getInstance().token.await() | ||
| Log.d("httplog", "회원가입 시 FCM 토큰 사용: $fcmToken") | ||
|
|
||
| memberRegisterRepository.registerMember( | ||
| token, | ||
| name, | ||
| birthDate.formatAsDate(), | ||
| gender, | ||
| ) | ||
| .onSuccess { | ||
| // 성공 로직 | ||
| // responseDto에는 MemberRegisterResponseDto 객체가 들어있음 | ||
| // 예: 성공 메시지 표시, 다음 화면으로 이동 | ||
| Log.d("httplog", "성공, ${it.accessToken} ${it.refreshToken}") | ||
| dataStoreRepository.saveAccessToken(it.accessToken) | ||
| dataStoreRepository.saveRefreshToken(it.refreshToken) | ||
| _events.emit(LoginEvent.MemberRegisterSuccess) | ||
| } | ||
| .onFailure { exception -> | ||
| // 실패 로직 | ||
| // exception에는 API 호출 중 발생한 예외가 들어있음 | ||
| // 예: 에러 메시지 표시 | ||
| Log.e("httplog", "회원가입 실패: ${exception.message}") | ||
| _events.emit(LoginEvent.MemberRegisterFailure) | ||
| } | ||
| } else { | ||
| _events.emit(LoginEvent.MemberRegisterSuccess) | ||
| token = token, | ||
| name = name, | ||
| birthDate = birthDate.formatAsDate(), | ||
| gender = gender, | ||
| fcmToken = fcmToken, | ||
| ).onSuccess { | ||
| Log.d("httplog", "회원가입 성공: ${it.accessToken} ${it.refreshToken}") | ||
| dataStoreRepository.saveAccessToken(it.accessToken) | ||
| dataStoreRepository.saveRefreshToken(it.refreshToken) | ||
| _events.emit(LoginEvent.MemberRegisterSuccess) | ||
| }.onFailure { e -> | ||
| Log.e("httplog", "회원가입 실패: ${e.message}") | ||
| _events.emit(LoginEvent.MemberRegisterFailure) | ||
| } | ||
|
|
||
| } catch (e: Exception) { | ||
| Log.e("httplog", "회원가입 중 예외 발생: ${e.message}") | ||
| _events.emit(LoginEvent.MemberRegisterFailure) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
회원가입 블록은 전체적으로 괜찮지만, 빈 줄 하나와 FCM 토큰 취득 위치를 살짝 정리하면 좋아 보여요
- Line 142의 빈 줄 때문에
NoBlankLineBeforeRbrace에러가 나고 있습니다.} catch사이의 공백만 정리해 주세요.
}.onFailure { e ->
Log.e("httplog", "회원가입 실패: ${e.message}")
_events.emit(LoginEvent.MemberRegisterFailure)
}
-
- } catch (e: Exception) {
+ } catch (e: Exception) {FirebaseMessaging.getInstance().token.await()를 ViewModel에서 직접 호출하는 대신, 나중에는FcmRepository를 통해 토큰을 얻는 방향으로 정리하면 FCM 관련 처리가 한 레이어에 모여서 유지보수가 좀 더 편해질 것 같습니다. (지금 구현도 동작상 문제는 없어 보이니, 추후 리팩터 용도로만 고려하셔도 됩니다.)
🧰 Tools
🪛 GitHub Actions: Android CI
[error] 142-142: NoBlankLineBeforeRbrace. Unexpected blank line(s) before closing brace.
🤖 Prompt for AI Agents
In
app/src/main/java/com/konkuk/medicarecall/ui/feature/login/info/viewmodel/LoginViewModel.kt
around lines 114–148, remove the extra blank line before the catch block to
satisfy NoBlankLineBeforeRbrace (i.e., eliminate the empty line between the
closing brace of the try block and the catch), and instead of calling
FirebaseMessaging.getInstance().token.await() directly in the ViewModel, inject
or obtain an FcmRepository and call a suspending method like
fcmRepository.getToken() so FCM token retrieval is centralized in the repository
layer (update constructor/DI to provide FcmRepository, replace direct call with
repository call and preserve logging and error handling).
🔗 관련 이슈
📙 작업 설명
케어콜과 관련해서 FCM을 통한 알림 구현 코드를 추가했습니다.
앱 내에서 알림 아이콘은 단색으로만 구현해야해서 일단 메디의 몸통으로 뜨도록 하고, 앱 밖에선 앱의 아이콘으로 상단 표시줄에 뜨게 됩니다.
다음 문서들을 참고해 코드 작성했습니다
https://firebase.google.com/docs/cloud-messaging/get-started?platform=android&hl=ko
https://like-while.tistory.com/65
추가적으로 SharedPerferences를 이용하는 방식에서 DataStore에 저장하는 방식으로 수정했습니다
암호화 도구 →
FcmTokenEncryptorDataStore에 저장할 데이터 모델 →
data class FcmToken(...)DataStore Serializer (읽을 때 복호화 / 쓸 때 암호화) →
FcmTokenSerializer레포지토리 인터페이스 →
FcmRepository레포지토리 구현체 →
FcmRepositoryImpl📸 스크린샷 또는 시연 영상 (선택)
| 기능 설명 |
| 기능 설명 |
|
💬 추가 설명 or 리뷰 포인트 (선택)
Summary by CodeRabbit
새 기능
변경 사항