nexo/agent-task/m-flutter-web-notification/02+01_web_plugin_baseline/PLAN-local-G06.md
toki caafd4f320 feat(messaging): Web 알림 API 기준을 추가한다
Flutter Web foreground 알림을 준비하기 위해 초기화 옵션과 token prefix 경계를 public API로 고정한다.

포트/환경 기준 문서와 로드맵 정리, agent-task 리뷰 산출물도 함께 반영한다.
2026-06-07 10:59:12 +09:00

12 KiB

Plan - WEBPLUGIN

이 파일을 읽는 구현 에이전트에게

이 task는 agent-task/m-flutter-web-notification/01_api_options/complete.log가 생긴 뒤 구현한다. CODE_REVIEW-local-G06.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채워야 완료다. 사용자에게 직접 질문하지 말고, 차단이 있으면 review stub의 사용자 리뷰 요청에 근거를 남긴다.

배경

Flutter Web plugin 등록과 browser Notification interop는 public initialize/options boundary가 먼저 있어야 한다. 이 plan은 첫 Epic의 plugin-webinterop를 묶어 처리하되, foreground permission/display public API는 다음 Epic으로 남긴다.

사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 사용자 리뷰 요청 섹션에 기록한다. 직접 사용자 프롬프트는 금지되며, code-review가 요청 정당성을 검증하고 필요할 때만 USER_REVIEW.md를 작성한다.

Roadmap Targets

  • Milestone: agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification.md
  • Task ids:
    • plugin-web: nexo_messaging에 Flutter Web plugin 등록과 web 전용 구현 파일을 추가한다.
    • interop: browser Notification API 연동은 package:web 기반으로 작성하고, Android/iOS/macOS import와 분리한다.
  • Completion mode: check-on-pass

분석 결과

읽은 파일

  • agent-roadmap/current.md
  • agent-roadmap/phase/messaging-runtime/milestones/flutter-web-notification.md
  • agent-test/local/rules.md
  • agent-test/local/messaging-flutter-smoke.md
  • agent-test/local/client-app-smoke.md
  • packages/messaging_flutter/pubspec.yaml
  • packages/messaging_flutter/lib/nexo_messaging.dart
  • packages/messaging_flutter/lib/src/nexo_messaging_plugin.dart
  • packages/messaging_flutter/lib/src/notification_opened_event.dart
  • packages/messaging_flutter/lib/src/push_notification_type.dart
  • packages/messaging_flutter/test/nexo_messaging_test.dart
  • packages/messaging_flutter/README.md
  • packages/messaging_flutter/analysis_options.yaml
  • apps/flutter-test/lib/main.dart
  • apps/flutter-test/test/widget_test.dart
  • apps/flutter-test/pubspec.yaml
  • apps/flutter-test/analysis_options.yaml
  • Official Flutter docs: https://docs.flutter.dev/packages-and-plugins/developing-packages
  • Official package:web page/API: https://pub.dev/packages/web, https://pub.dev/documentation/web/latest/web/Notification-extension-type.html

테스트 환경 규칙

  • test_env=local.
  • agent-test/local/rules.md, messaging-flutter-smoke.md, client-app-smoke.md를 읽었다.
  • 적용 명령: cd packages/messaging_flutter && flutter pub get, cd packages/messaging_flutter && flutter test, cd packages/messaging_flutter && flutter analyze --no-fatal-infos.
  • Web plugin registration이 consuming app dependency graph에 영향을 주므로 필요 시 cd apps/flutter-test && flutter pub get도 실행한다.
  • Flutter SDK와 Dart SDK는 /sdk/flutter/bin/에서 확인했다.

테스트 커버리지 공백

  • 기존 tests는 web plugin registration과 browser interop를 검증하지 않는다.
  • 이 plan은 web implementation file의 registerWith와 internal interop mapper를 Dart unit/analyze 대상으로 만든다.
  • 실제 Chrome permission prompt/display/click smoke는 foregroundsmoke Epic에서 별도 처리한다.

심볼 참조

  • renamed/removed symbol: none.
  • 새 dependency/call site:
    • packages/messaging_flutter/pubspec.yamlweb: ^1.1.1 추가.
    • flutter.plugin.platforms.webpluginClassfileName 추가.
    • 새 web 전용 파일이 package:web를 import한다. Android/iOS/macOS 공용 파일은 package:web를 직접 import하지 않는다.

분할 판단

  • split decision policy를 plan 파일 선택 전에 평가했다.
  • shared task group: m-flutter-web-notification.
  • 이 subtask directory 02+01_web_plugin_baseline은 predecessor index 01에 의존한다.
  • predecessor status: agent-task/m-flutter-web-notification/01_api_options/complete.log는 아직 없다. 구현 시작 전 해당 active sibling 또는 archive candidate complete.log를 확인해야 한다.
  • 이 plan은 plugin-webinterop를 완료 대상으로 삼고, permission, display, event-map은 후속 Epic으로 남긴다.

범위 결정 근거

  • 포함: Flutter web platform registration, web implementation entry file, package:web dependency, web-only browser Notification interop boundary.
  • 제외: permission request public API, browser notification display/click routing behavior, service worker, VAPID, web_rn-v2 server registration, core/push-proxy platform changes, Flutter test web UI.
  • 제외 이유: 첫 Epic baseline은 platform support를 깨지 않게 올리는 기준선이고, user-visible foreground behavior는 다음 Epic에서 검증 단위가 다르다.

빌드 등급

  • local-G06: package manifest, generated plugin registration contract, web-only imports, and dependency resolution risk가 있지만 범위가 package 내부로 제한되고 deterministic Flutter commands로 검증 가능하다.

구현 체크리스트

  • 구현 시작 전 01_api_options predecessor complete.log가 active 또는 archive 경로에 있는지 확인한다.
  • packages/messaging_flutter/pubspec.yamlweb: ^1.1.1 dependency와 Flutter web platform entry를 추가한다.
  • NexoMessagingWeb.registerWith() web 전용 파일을 추가하고 공용 Dart 파일이 package:web를 직접 import하지 않게 유지한다.
  • browser Notification API 접근은 package:web 기반 web-only interop wrapper로 분리한다.
  • web baseline unit/analyze 검증을 추가 또는 보강한다.
  • cd packages/messaging_flutter && flutter pub get, cd packages/messaging_flutter && flutter test, cd packages/messaging_flutter && flutter analyze --no-fatal-infos를 실행한다.
  • CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

의존 관계 및 구현 순서

  1. agent-task/m-flutter-web-notification/01_api_options/complete.log 또는 archive의 matching 01_api_options/complete.log를 확인한다.
  2. predecessor가 없으면 구현하지 말고 review stub의 사용자 리뷰 요청이 아니라 계획 미충족으로 보고한다.
  3. predecessor가 있으면 이 plan의 구현을 진행한다.

[WEBPLUGIN-1] Flutter Web Plugin Registration

문제

packages/messaging_flutter/pubspec.yaml은 Android/iOS/macOS만 등록되어 있어 Flutter Web app이 plugin registrant를 생성할 수 없다. Flutter 공식 문서는 web plugin Dart implementation에 pluginClassfileName을 사용한다고 설명한다.

Before:

21  flutter:
22    plugin:
23      platforms:
24        android:
25          package: com.tokilabs.nexo.messaging
26          pluginClass: NexoMessagingPlugin
27        ios:
28          pluginClass: NexoMessagingPlugin
29        macos:
30          pluginClass: NexoMessagingPlugin

해결 방법

pubspec.yaml에 web dependency와 platform entry를 추가한다.

dependencies:
  flutter:
    sdk: flutter
  firebase_messaging: ^15.2.5
  web: ^1.1.1

flutter:
  plugin:
    platforms:
      web:
        pluginClass: NexoMessagingWeb
        fileName: src/nexo_messaging_web.dart

lib/src/nexo_messaging_web.dart를 추가한다.

import 'package:flutter_web_plugins/flutter_web_plugins.dart';

class NexoMessagingWeb {
  static void registerWith(Registrar registrar) {
    // Baseline registration; foreground behavior is wired in later tasks.
  }
}

수정 파일 및 체크리스트

  • packages/messaging_flutter/pubspec.yaml dependency/platform entry 추가
  • packages/messaging_flutter/lib/src/nexo_messaging_web.dart 추가
  • packages/messaging_flutter/pubspec.lock 갱신 여부 확인. package plugin lockfile은 이 repo에서 ignored이므로 source diff에 남기지 않는다.

테스트 작성

  • unit test가 web registrant generation 자체를 실행하지는 않는다.
  • flutter pub getflutter analyze --no-fatal-infos로 pubspec schema/import correctness를 검증한다.

중간 검증

cd packages/messaging_flutter && flutter pub get
cd packages/messaging_flutter && flutter analyze --no-fatal-infos

기대 결과: dependency resolution과 analyzer가 통과한다.

[WEBPLUGIN-2] Package Web Interop Boundary

문제

packages/messaging_flutter/lib/src/nexo_messaging_plugin.dart는 공용 Dart API에서 firebase_messaging를 직접 import하고 있고, browser Notification API 경계가 없다. package:web는 Dart web interop의 장기 해법이며, Notification.permissionNotification.requestPermission() API를 제공한다.

해결 방법

package:web import는 web-only implementation 아래로 제한한다. 예시 구조:

lib/src/web/browser_notification_interop.dart
lib/src/web/browser_notification_interop_web.dart

browser_notification_interop_web.dart만 아래 import를 가진다.

import 'dart:js_interop';
import 'package:web/web.dart' as web;

첫 Epic에서는 public permission/display API를 만들지 않는다. 대신 future foreground task가 사용할 private wrapper만 둔다.

class BrowserNotificationInterop {
  const BrowserNotificationInterop();

  String get permission => web.Notification.permission.toString();

  Future<String> requestPermission() async {
    final result = await web.Notification.requestPermission().toDart;
    return result.toDart;
  }
}

실제 display/click wiring은 permission, display, event-map Task에서 구현한다.

수정 파일 및 체크리스트

  • packages/messaging_flutter/lib/src/web/browser_notification_interop.dart 추가
  • packages/messaging_flutter/lib/src/web/browser_notification_interop_web.dart 추가
  • non-web common API가 package:web를 직접 import하지 않는지 확인
  • README Platform Status 또는 Current Limitations에 Web baseline registration 상태를 반영

테스트 작성

  • wrapper가 web-only인 경우 VM unit test에서 직접 import하지 않는다.
  • analyzer로 import boundary를 검증한다.
  • 가능하면 web implementation file의 non-browser pure mapper만 별도 testable helper로 분리한다. 실제 Notification prompt/display는 이 plan의 테스트 대상이 아니다.

중간 검증

cd packages/messaging_flutter && flutter analyze --no-fatal-infos

기대 결과: non-web analyzer에서도 package:web import가 공용 VM test path를 깨지 않는다.

수정 파일 요약

파일 항목
packages/messaging_flutter/pubspec.yaml WEBPLUGIN-1
packages/messaging_flutter/lib/src/nexo_messaging_web.dart WEBPLUGIN-1
packages/messaging_flutter/lib/src/web/browser_notification_interop.dart WEBPLUGIN-2
packages/messaging_flutter/lib/src/web/browser_notification_interop_web.dart WEBPLUGIN-2
packages/messaging_flutter/README.md WEBPLUGIN-2

최종 검증

cd packages/messaging_flutter && flutter pub get
cd packages/messaging_flutter && flutter test
cd packages/messaging_flutter && flutter analyze --no-fatal-infos

기대 결과: 세 명령 모두 exit code 0.

모든 코드 변경 완료 후 반드시 CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.