Skip to content

Feat/notification#237

Open
thdudgus wants to merge 5 commits intomainfrom
feat/notification
Open

Feat/notification#237
thdudgus wants to merge 5 commits intomainfrom
feat/notification

Conversation

@thdudgus
Copy link
Copy Markdown
Contributor

@thdudgus thdudgus commented Mar 19, 2026

Fixes #227


변경사항

  • pubspec.yaml에 firebase_messaging 및 flutter_local_notifications 의존성을 추가

  • 안드로이드(AndroidManifest.xml, colors.xml)와 iOS(Info.plist, Runner.entitlements)에서 푸시 알림 지원을 위한 권한, 메타데이터, 백그라운드 모드를 포함한 설정

  • 알림 서비스 (NotificationService.dart) 구현

  • 백엔드와의 FCM 토큰 등록 연동

  • 애플리케이션 구조 및 라우터 연동 (후에 딥링크 라우터 위함.)

  • 알림 기능 개선 (NotificationService)

    • 안드로이드 전용 알림 채널 (high_importance_channel) 생성 로직 추가
    • 포그라운드 상태에서 알림 메시지뿐만 아니라 데이터 메시지(Data Payload)도 처리할 수 있도록 개선
    • 앱 시작 시 로그인 상태인 경우 FCM 토큰을 자동으로 등록하는 로직 추가
    • 알림 수신 및 처리 과정에 상세한 개발자 로그 추가
  • 네트워크 로깅 추가 (DioClient)

    • CustomInterceptor를 통해 HTTP 요청, 응답, 에러 발생 시 상세 로그를 출력하도록 개선
  • 안드로이드 설정 수정

    • ANR(응답 없음) 문제를 일으킬 수 있는 Ably 관련 FirebaseMessagingReceiver 제거

TODO

  • notification 딥링크

refactor: 푸시 알림 환경 동적 설정 및 FCM 토큰 등록 로직 개선

  • ios/Runner/Runner.entitlements의 aps-environment 값을 $(APS_ENVIRONMENT) 변수로 변경하여 빌드 환경에 따라 자동 전환되도록 수정
  • NotificationService._registerToken에 지수 백오프(Exponential Backoff) 기반 재시도 로직을 추가하여 토큰 등록의 견고함을 강화
  • 로그인 성공 시점 및 앱 초기 구동 시 로그인 상태를 감지하여 토큰 등록을 시도하는 리스너 로직을 고도화
  • 네트워크 불안정 등으로 인한 토큰 등록 실패 케이스에 대응하고 로깅(Logging) 시스템을 강화

- pubspec.yaml에 firebase_messaging 및 flutter_local_notifications 의존성을 추가
- 안드로이드(AndroidManifest.xml, colors.xml)와 iOS(Info.plist, Runner.entitlements)에서 푸시 알림 지원을 위한 권한, 메타데이터, 백그라운드 모드를 포함한 설정
- 알림 서비스 (NotificationService.dart) 구현
- 백엔드와의 FCM 토큰 등록 연동
- 애플리케이션 구조 및 라우터 연동 (후에 딥링크 라우터 위함.)
- Ably 리시버 제거: FCM 메시지를 가로채어 CPU 점유율을 94%까지 높이던 Ably의 FirebaseMessagingReceiver를 AndroidManifest.xml에서 명시적으로 제외함.
- 핸들러 등록 시점 조정: 시스템이 즉각 반응할 수 있도록 백그라운드 핸들러 등록 로직을 main() 함수의 최상단으로 이동.
- 비차단 초기화: 권한 요청, 폰트 로딩 등 시간이 걸리는 로직에서 await를 제거하여 메인 스레드가 멈추는 현상 방지.
- 고성능 로깅: 동기식 print를 비동기식 dart:developer.log로 교체하고, kDebugMode 가드와 문자열 길이 제한을 적용하여 디버깅 효율과 성능을 향상시킴.
- 중복 리스너 최적화: AuthViewModel의 상태 변화에 따른 토큰 등록 리스너가 중복 생성되지 않도록 로직 정밀화.
@thdudgus thdudgus self-assigned this Mar 19, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 앱에 푸시 알림 기능을 전면적으로 도입하고 관련 인프라를 구축하는 것을 목표로 합니다. Firebase Cloud Messaging과 Flutter Local Notifications을 활용하여 사용자에게 알림을 보낼 수 있는 기반을 마련했으며, 안드로이드 및 iOS 플랫폼에 필요한 모든 설정을 완료했습니다. 또한, 알림 서비스의 안정성과 디버깅 편의성을 높이기 위한 여러 개선 사항과 네트워크 요청 로깅 기능도 포함되어 있습니다. 이를 통해 사용자 참여를 유도하고 중요한 정보를 효과적으로 전달할 수 있는 새로운 채널이 확보되었습니다.

Highlights

  • 푸시 알림 기능 추가: Firebase Messaging 및 Flutter Local Notifications 의존성을 추가하고, 안드로이드 및 iOS 플랫폼별 푸시 알림 설정을 완료하여 앱에 푸시 알림 기능을 도입했습니다.
  • 알림 서비스 개선: 새로운 NotificationService를 구현하여 안드로이드 전용 알림 채널 생성, 포그라운드 데이터 메시지 처리, 로그인 시 FCM 토큰 자동 등록, 상세 개발자 로그 추가 등 알림 처리 로직을 개선했습니다.
  • FCM 토큰 등록 연동: 백엔드와 FCM 토큰 등록을 연동하여 사용자의 알림 수신을 위한 장치 토큰을 관리할 수 있도록 했습니다. 로그인 상태에 따라 토큰 등록을 시도합니다.
  • 네트워크 로깅 강화: DioClientCustomInterceptor를 추가하여 HTTP 요청, 응답, 에러 발생 시 상세 로그를 출력하도록 개선하여 디버깅 편의성을 높였습니다.
  • 안드로이드 설정 최적화: ANR(응답 없음) 문제를 유발할 수 있는 Ably 관련 FirebaseMessagingReceiver를 제거하여 안드로이드 앱의 안정성을 향상시켰습니다.
  • 라우터 연동 준비: 알림 클릭 시 딥링크 라우팅을 위해 rootNavigatorKey를 전역으로 이동하고, 알림 데이터 페이로드를 기반으로 화면 이동을 처리할 수 있는 로직의 기반을 마련했습니다.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

푸시 알림 기능이 성공적으로 통합되었으며, Firebase Messaging과 Flutter Local Notifications을 활용하여 백그라운드 및 포그라운드 알림 처리가 잘 구현되었습니다. 네트워크 로깅 개선과 인증 관련 로그 메시지 변경도 코드 품질 향상에 기여했습니다. 전반적으로 변경 사항은 잘 구성되어 있으며, 알림 기능의 핵심적인 요구 사항을 충족합니다. 몇 가지 개선 사항을 제안합니다.

Comment on lines +286 to +317
/*
switch (notificationType) {
case 'LIKE':
case 'COMMENT':
case 'REPLY':
final diaryId = metadata['diaryId'];
if (diaryId != null) {
// 현재 일기 상세 라우트가 없으므로, 필요 시 GoRouter에 추가해야 함
// GoRouter.of(context).push('/book-log/detail/$diaryId');
if (kDebugMode) dev.log("Navigating to Diary Detail for diaryId: $diaryId (Currently commented out)", name: 'NOTIFICATION');
}
break;
case 'FOLLOW':
final memberId = metadata['memberId'];
if (memberId != null) {
GoRouter.of(context).push('/profile/$memberId'); // 예시 라우트
if (kDebugMode) dev.log("Navigating to Member Profile for memberId: $memberId (Currently commented out)", name: 'NOTIFICATION');
}
break;
case 'QUIZ_COMPLETED':
final bookId = metadata['bookId'];
final challengeId = metadata['challengeId'];
if (bookId != null && challengeId != null) {
GoRouter.of(context).push('/reading-challenge/detail/$bookId?challengeId=$challengeId');
if (kDebugMode) dev.log("Navigating to Reading Challenge Detail for bookId: $bookId, challengeId: $challengeId (Currently commented out)", name: 'NOTIFICATION');
}
break;
default:
if (kDebugMode) dev.log("Unknown notification type or no navigation defined for: $notificationType", name: 'NOTIFICATION');
break;
}
*/
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

_handleMessageData 내의 라우팅 로직이 현재 TODO로 주석 처리되어 있습니다. 알림 기능의 핵심적인 사용자 경험을 제공하기 위해 이 부분의 구현이 시급합니다. notificationTypemetadata를 기반으로 적절한 화면으로 이동하는 로직을 완성해야 합니다. 주석 처리된 예시 코드를 참고하여 구현을 진행하면 좋을 것 같습니다.

Comment thread ios/Runner/Runner.entitlements Outdated
Comment on lines +11 to +12
<key>aps-environment</key>
<string>development</string>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

iOS 푸시 알림 환경 설정(aps-environment)이 development로 직접 설정되어 있습니다. 프로덕션 빌드 시에는 이 값을 production으로 변경해야 합니다. Xcode 빌드 설정에서 CODE_SIGN_ENTITLEMENTS를 사용하여 빌드 구성에 따라 자동으로 development 또는 production으로 전환되도록 관리하는 것이 좋습니다. 이렇게 하면 수동 변경으로 인한 실수를 방지할 수 있습니다.

	<key>aps-environment</key>
	<string>$(APS_ENVIRONMENT)</string>

Comment thread lib/core/notification_service.dart Outdated
Comment on lines +157 to +162
// 로그인 상태가 아니거나 로딩 중이면 토큰 등록을 보류하거나 스킵
// (나중에 로그인 성공 시 다시 호출하도록 로직 보완 필요할 수 있음)
if (authState.value is! AuthSuccess) {
if (kDebugMode) dev.log("User not logged in. Skipping FCM token registration.", name: 'NOTIFICATION');
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

_registerToken 함수에서 authState.value is! AuthSuccess 조건으로 토큰 등록을 건너뛰는 로직이 있습니다. 이는 로그인되지 않은 상태에서 불필요한 요청을 방지하지만, 로그인 성공 직후 토큰 등록이 실패했을 때 재시도 메커니즘이 명확하지 않습니다. 토큰 등록이 중요한 기능이라면, 실패 시 일정 시간 후 재시도하거나, 사용자 로그인 상태가 변경될 때마다 토큰 등록을 다시 시도하는 등의 견고한 로직을 고려해볼 수 있습니다.

Comment on lines +191 to +192
String? title = message.notification?.title ?? message.data['title'] ?? message.data['messageTitle'];
String? body = message.notification?.body ?? message.data['body'] ?? message.data['messageBody'] ?? message.data['content'];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

_showNotification에서 알림 제목과 본문을 message.notification 또는 message.data에서 추출하는 로직이 유연하게 구현되어 있습니다. 하지만 message.data에서 title, messageTitle, body, messageBody, content와 같은 여러 키를 확인하는 방식은 백엔드에서 보내는 데이터 페이로드의 일관성이 중요합니다. 예상되는 데이터 키에 대한 명확한 문서화 또는 백엔드와의 통신 규약 확립이 유지보수성을 높이는 데 도움이 될 것입니다.

Comment on lines +261 to +269
// TODO: 여기서 데이터 파싱 및 라우팅 로직 구현
// routerProvider 접근을 위해 BuildContext가 필요하며,
// NotificationService가 직접 Riverpod Consumer가 아니므로
// rootNavigatorKey를 통해 context를 얻어 GoRouter를 사용해야 합니다.
final context = rootNavigatorKey.currentContext;
if (context == null) {
if (kDebugMode) dev.log("Router context not available for navigation.", name: 'NOTIFICATION');
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

_handleMessageData 함수 내에서 rootNavigatorKey.currentContext를 사용하여 라우터 컨텍스트를 가져오는 방식은 NotificationServiceBuildContext에 직접 의존하지 않도록 하는 좋은 방법입니다. 그러나 contextnull일 경우 dev.log만 하고 반환하는 것은 알림 클릭 시 내비게이션이 필요한 경우 사용자 경험에 영향을 줄 수 있습니다. contextnull이 아닌 경우에만 내비게이션 로직이 실행되도록 하는 것은 좋지만, null인 경우에 대한 사용자 피드백(예: 토스트 메시지)이나 추가적인 처리 방안을 고려해볼 수 있습니다.

thdudgus and others added 2 commits March 19, 2026 21:34
- ios/Runner/Runner.entitlements의 aps-environment 값을 $(APS_ENVIRONMENT) 변수로 변경하여 빌드 환경에 따라 자동 전환되도록 수정
- NotificationService._registerToken에 지수 백오프(Exponential Backoff) 기반 재시도 로직을 추가하여 토큰 등록의 견고함을 강화
- 로그인 성공 시점 및 앱 초기 구동 시 로그인 상태를 감지하여 토큰 등록을 시도하는 리스너 로직을 고도화
- 네트워크 불안정 등으로 인한 토큰 등록 실패 케이스에 대응하고 로깅(Logging) 시스템을 강화
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

notification 기능 개발

1 participant