Conversation
- FlutterMessageQueue 단일 채널에서 AppBridge.* 개별 채널 인터페이스로 마이그레이션 - window.AppBridge 네임스페이스로 채널 통합 (feature detection 지원) - callHandler 리턴값 활용한 에러 핸들링 (success/error 응답) - mixin 패턴으로 도메인별 핸들러 분리 (media, auth, share, device) - 구버전 웹 호환을 위한 FlutterMessageQueue 레거시 유지 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
WebView ↔ Flutter 브릿지를 단일 FlutterMessageQueue에서 window.AppBridge.* 개별 채널 구조로 리팩토링하고(레거시 호환 유지), 도메인별 mixin 핸들러로 책임을 분리한 변경입니다.
Changes:
- WebView 주입 스크립트를
BridgeSetupScript로 분리하고window.AppBridge네임스페이스 하위 채널들을 초기화 WebViewBridgeHandler를BridgeHandlerBase + (media/auth/share/device) mixin구조로 재편, 개별 JS handler 등록 추가- 네이티브 공유 기능을 위해
share_plus의존성 추가 및 Share 브릿지 핸들러 신규 도입
Reviewed changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| pubspec.yaml | share_plus 의존성 추가 |
| pubspec.lock | share_plus 및 전이 의존성 lock 반영, SDK 메타 갱신 |
| lib/web_view/web_view.dart | WebView 초기 UserScript를 BridgeSetupScript.script로 변경 |
| lib/bridge/web_view_bridge_handler.dart | 브릿지 핸들러를 mixin 기반으로 재구성하고 개별 채널 등록 로직 추가 |
| lib/bridge/bridge_setup_script.dart | window.AppBridge.* 채널 및 레거시 FlutterMessageQueue 초기화 스크립트 신규 |
| lib/bridge/handlers/bridge_handler_base.dart | 공통 의존성/권한 다이얼로그 유틸 베이스 신규 |
| lib/bridge/handlers/media_bridge_handler.dart | 앨범/카메라 이미지 처리 로직을 mixin으로 분리 |
| lib/bridge/handlers/auth_bridge_handler.dart | 카카오/애플 로그인 브릿지 로직을 mixin으로 분리 |
| lib/bridge/handlers/device_bridge_handler.dart | 디바이스 토큰/햅틱/환경전환 브릿지 로직을 mixin으로 분리 |
| lib/bridge/handlers/share_bridge_handler.dart | 네이티브 공유 시트 브릿지 로직 신규 |
| ios/Podfile.lock | iOS 플러그인(pods) lock 반영 (share_plus 등) |
| void triggerHaptic(List<dynamic> arguments) async { | ||
| if (arguments.isEmpty) { | ||
| logger.w('햅틱 피드백 타입이 전달되지 않았습니다.'); | ||
| return; | ||
| } |
There was a problem hiding this comment.
[P1] async 함수가 void를 반환하고 있습니다. 이 형태는 예외가 상위에서 잡히지 않아(unhandled) 런타임 크래시/로그 누락이 날 수 있으니 Future<void>로 바꾸고 호출부에서 await되도록 해주세요.
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: triggerHaptic의 리턴 타입을 void에서 Future<void>로 변경하여 예외가 호출부에 전파되도록 수정했습니다.
| } | ||
| } | ||
|
|
||
| void switchEnvironment(List<dynamic> arguments) async { |
There was a problem hiding this comment.
[P1] switchEnvironment도 void async라서 실패/예외가 호출자에 전파되지 않습니다. Future<void>로 변경하고 브릿지 채널 콜백에서 await해서 완료 시점/에러 처리를 일관되게 해주세요.
| void switchEnvironment(List<dynamic> arguments) async { | |
| Future<void> switchEnvironment(List<dynamic> arguments) async { |
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: switchEnvironment도 void에서 Future<void>로 변경했습니다.
| triggerHaptic(args); | ||
| }); | ||
|
|
||
| _registerChannel('AppBridge.env', (args) async { | ||
| switchEnvironment(args); |
There was a problem hiding this comment.
[P1] AppBridge.haptic/AppBridge.env에서 내부 async 작업을 await하지 않고 즉시 리턴하고 있습니다. 현재 triggerHaptic/switchEnvironment가 void async라 예외가 잡히지 않으니 Future<void>로 바꾸고 여기서 await 처리해주세요.
| triggerHaptic(args); | |
| }); | |
| _registerChannel('AppBridge.env', (args) async { | |
| switchEnvironment(args); | |
| await triggerHaptic(args); | |
| }); | |
| _registerChannel('AppBridge.env', (args) async { | |
| await switchEnvironment(args); |
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: AppBridge.haptic/AppBridge.env 채널에서 await triggerHaptic(args), await switchEnvironment(args)로 변경했습니다.
| try { | ||
| logger.d('Channel $name called with args: $args'); | ||
| final result = await handler(args); |
There was a problem hiding this comment.
[P2] 채널 호출 시 args를 그대로 로깅하고 있어 토큰/개인정보(공유 텍스트 등)가 디바이스 로그에 남을 수 있습니다. 릴리즈 빌드에서 비활성화하거나 민감값을 마스킹/요약 로깅으로 바꿔주세요.
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: 채널 로깅을 args count만 출력하도록 변경하여 민감값이 로그에 남지 않도록 했습니다.
| logger.e('공유 중 오류 발생: $e'); | ||
| await controller.evaluateJavascript( | ||
| source: "onShareError && onShareError('$e')", | ||
| ); |
There was a problem hiding this comment.
[P1] onShareError('$e')처럼 에러 문자열을 JS 문자열 리터럴에 직접 삽입하면 '/개행 포함 시 스크립트가 깨지거나 의도치 않은 코드가 실행될 수 있습니다. JS로 넘길 값은 jsonEncode로 이스케이프하거나, callHandler의 리턴값(Map)으로만 에러를 전달하도록 바꿔주세요.
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: 에러 문자열을 jsonEncode로 이스케이프하여 JS 문자열 리터럴 깨짐/인젝션을 방지했습니다.
| await controller.evaluateJavascript( | ||
| source: "onKakaoLoginSuccess('${kakaoLoginResult.accessToken}')", | ||
| ); | ||
| onHideLoading?.call(); | ||
| } catch (error) { | ||
| await controller.evaluateJavascript( | ||
| source: "onKakaoLoginError('$error')", | ||
| ); | ||
| onHideLoading?.call(); | ||
| } |
There was a problem hiding this comment.
[P1] onHideLoading이 evaluateJavascript 성공을 전제로 호출되고 있어, JS 실행이 실패하면 로딩이 영구히 남을 수 있습니다. try/finally로 onHideLoading?.call()을 보장하고, 에러 시 JS 호출도 실패할 수 있으니 그 부분도 별도 보호 로직이 필요합니다.
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: handleKakaoLogin/handleAppleLogin에서 onHideLoading을 finally 블록으로 이동하여 로딩 해제를 보장합니다. JS 에러 콜백도 별도 try-catch로 보호했습니다.
| final MethodChannel platform = | ||
| const MethodChannel('com.bottlenote.official.app/intents'); | ||
|
|
There was a problem hiding this comment.
[P2] platform MethodChannel 필드가 이 파일에서 사용되지 않습니다(검색 기준 참조 없음). 실제로 필요 없다면 제거해서 unused_field 경고를 없애고, 필요하다면 사용처를 추가/이동해 주세요.
| final MethodChannel platform = | |
| const MethodChannel('com.bottlenote.official.app/intents'); |
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: 미사용 platform MethodChannel 필드 및 관련 import 제거했습니다.
| @@ -118,385 +155,39 @@ class WebViewBridgeHandler { | |||
| break; | |||
There was a problem hiding this comment.
[P2] 레거시 FlutterMessageQueue 라우팅에서 controller.evaluateJavascript(...)/각 핸들러(Future 반환)를 await하지 않습니다. unawaited_futures 경고가 나고, 실패 시 에러가 유실될 수 있으니 callback을 async로 바꾸고 처리 메서드를 Future로 만들어 await해주세요.
There was a problem hiding this comment.
Resolved in 0bc0dba
변경 내용: _handleFlutterMessageQueue를 Future<void> async로 변경하고, 모든 핸들러 호출에 await를 추가했습니다.
- triggerHaptic/switchEnvironment: void async → Future<void> (#2784958686, #2784958709) - AppBridge.haptic/env 채널에서 await 추가 (#2784958735) - 채널 args 로깅에서 민감값 마스킹 (#2784958756) - share 에러 전달 시 jsonEncode 이스케이프 (#2784958775) - auth 핸들러 onHideLoading을 try/finally로 보장 (#2784958791) - 미사용 platform MethodChannel 제거 (#2784958807) - 레거시 _handleFlutterMessageQueue를 async/await로 변경 (#2784958825) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
변경 사항
window.AppBridge.*개별 채널로 마이그레이션window.AppBridge하위에 모든 채널 정리 (feature detection 지원)callHandler리턴값으로{success: true}/{error: '...'}응답 처리FlutterMessageQueue레거시 유지테스트
스크린샷 (UI 변경 시)
N/A (아키텍처 리팩토링)
관련 이슈