Skip to content

[refactor] WebView 브릿지 개별 채널 아키텍처#5

Merged
hyejj19 merged 2 commits intomainfrom
share-bridge-handler
Feb 9, 2026
Merged

[refactor] WebView 브릿지 개별 채널 아키텍처#5
hyejj19 merged 2 commits intomainfrom
share-bridge-handler

Conversation

@hyejj19
Copy link
Copy Markdown
Collaborator

@hyejj19 hyejj19 commented Feb 9, 2026

변경 사항

  • 개별 채널 인터페이스: FlutterMessageQueue 단일 채널에서 window.AppBridge.* 개별 채널로 마이그레이션
  • 네임스페이스 통합: window.AppBridge 하위에 모든 채널 정리 (feature detection 지원)
  • 에러 핸들링: callHandler 리턴값으로 {success: true} / {error: '...'} 응답 처리
  • mixin 기반 구조화: 도메인별 핸들러 분리 (media, auth, share, device)
  • 하위 호환성: 구버전 웹을 위한 FlutterMessageQueue 레거시 유지

테스트

  • 로컬 테스트 완료
  • iOS 빌드/실행 확인
  • Android 빌드/실행 확인

스크린샷 (UI 변경 시)

N/A (아키텍처 리팩토링)

관련 이슈

- 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>
Copilot AI review requested due to automatic review settings February 9, 2026 22:53
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

WebView ↔ Flutter 브릿지를 단일 FlutterMessageQueue에서 window.AppBridge.* 개별 채널 구조로 리팩토링하고(레거시 호환 유지), 도메인별 mixin 핸들러로 책임을 분리한 변경입니다.

Changes:

  • WebView 주입 스크립트를 BridgeSetupScript로 분리하고 window.AppBridge 네임스페이스 하위 채널들을 초기화
  • WebViewBridgeHandlerBridgeHandlerBase + (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 등)

Comment on lines +29 to +33
void triggerHaptic(List<dynamic> arguments) async {
if (arguments.isEmpty) {
logger.w('햅틱 피드백 타입이 전달되지 않았습니다.');
return;
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P1] async 함수가 void를 반환하고 있습니다. 이 형태는 예외가 상위에서 잡히지 않아(unhandled) 런타임 크래시/로그 누락이 날 수 있으니 Future<void>로 바꾸고 호출부에서 await되도록 해주세요.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: triggerHaptic의 리턴 타입을 void에서 Future<void>로 변경하여 예외가 호출부에 전파되도록 수정했습니다.

}
}

void switchEnvironment(List<dynamic> arguments) async {
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P1] switchEnvironmentvoid async라서 실패/예외가 호출자에 전파되지 않습니다. Future<void>로 변경하고 브릿지 채널 콜백에서 await해서 완료 시점/에러 처리를 일관되게 해주세요.

Suggested change
void switchEnvironment(List<dynamic> arguments) async {
Future<void> switchEnvironment(List<dynamic> arguments) async {

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: switchEnvironmentvoid에서 Future<void>로 변경했습니다.

Comment on lines +133 to +137
triggerHaptic(args);
});

_registerChannel('AppBridge.env', (args) async {
switchEnvironment(args);
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P1] AppBridge.haptic/AppBridge.env에서 내부 async 작업을 await하지 않고 즉시 리턴하고 있습니다. 현재 triggerHaptic/switchEnvironmentvoid async라 예외가 잡히지 않으니 Future<void>로 바꾸고 여기서 await 처리해주세요.

Suggested change
triggerHaptic(args);
});
_registerChannel('AppBridge.env', (args) async {
switchEnvironment(args);
await triggerHaptic(args);
});
_registerChannel('AppBridge.env', (args) async {
await switchEnvironment(args);

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: AppBridge.haptic/AppBridge.env 채널에서 await triggerHaptic(args), await switchEnvironment(args)로 변경했습니다.

Comment on lines +95 to +97
try {
logger.d('Channel $name called with args: $args');
final result = await handler(args);
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P2] 채널 호출 시 args를 그대로 로깅하고 있어 토큰/개인정보(공유 텍스트 등)가 디바이스 로그에 남을 수 있습니다. 릴리즈 빌드에서 비활성화하거나 민감값을 마스킹/요약 로깅으로 바꿔주세요.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: 채널 로깅을 args count만 출력하도록 변경하여 민감값이 로그에 남지 않도록 했습니다.

Comment on lines +54 to +57
logger.e('공유 중 오류 발생: $e');
await controller.evaluateJavascript(
source: "onShareError && onShareError('$e')",
);
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P1] onShareError('$e')처럼 에러 문자열을 JS 문자열 리터럴에 직접 삽입하면 '/개행 포함 시 스크립트가 깨지거나 의도치 않은 코드가 실행될 수 있습니다. JS로 넘길 값은 jsonEncode로 이스케이프하거나, callHandler의 리턴값(Map)으로만 에러를 전달하도록 바꿔주세요.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: 에러 문자열을 jsonEncode로 이스케이프하여 JS 문자열 리터럴 깨짐/인젝션을 방지했습니다.

Comment on lines +18 to +27
await controller.evaluateJavascript(
source: "onKakaoLoginSuccess('${kakaoLoginResult.accessToken}')",
);
onHideLoading?.call();
} catch (error) {
await controller.evaluateJavascript(
source: "onKakaoLoginError('$error')",
);
onHideLoading?.call();
}
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P1] onHideLoadingevaluateJavascript 성공을 전제로 호출되고 있어, JS 실행이 실패하면 로딩이 영구히 남을 수 있습니다. try/finallyonHideLoading?.call()을 보장하고, 에러 시 JS 호출도 실패할 수 있으니 그 부분도 별도 보호 로직이 필요합니다.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: handleKakaoLogin/handleAppleLogin에서 onHideLoadingfinally 블록으로 이동하여 로딩 해제를 보장합니다. JS 에러 콜백도 별도 try-catch로 보호했습니다.

Comment on lines 27 to 29
final MethodChannel platform =
const MethodChannel('com.bottlenote.official.app/intents');

Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P2] platform MethodChannel 필드가 이 파일에서 사용되지 않습니다(검색 기준 참조 없음). 실제로 필요 없다면 제거해서 unused_field 경고를 없애고, 필요하다면 사용처를 추가/이동해 주세요.

Suggested change
final MethodChannel platform =
const MethodChannel('com.bottlenote.official.app/intents');

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: 미사용 platform MethodChannel 필드 및 관련 import 제거했습니다.

Comment on lines 149 to 155
@@ -118,385 +155,39 @@ class WebViewBridgeHandler {
break;
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

[P2] 레거시 FlutterMessageQueue 라우팅에서 controller.evaluateJavascript(...)/각 핸들러(Future 반환)를 await하지 않습니다. unawaited_futures 경고가 나고, 실패 시 에러가 유실될 수 있으니 callbackasync로 바꾸고 처리 메서드를 Future로 만들어 await해주세요.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Resolved in 0bc0dba

변경 내용: _handleFlutterMessageQueueFuture<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>
@hyejj19 hyejj19 merged commit a41cb2d into main Feb 9, 2026
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.

2 participants