Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/webview/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import { ActivityIndicator, TouchableOpacity } from "react-native";
import { SafeAreaView } from "react-native-safe-area-context";
import { WebView } from "react-native-webview";
import styled from "styled-components/native";

Check warning on line 17 in app/webview/[slug].tsx

View workflow job for this annotation

GitHub Actions / android-check

Using exported name 'styled' as identifier for default import

const webviewUrl =
process.env.EXPO_PUBLIC_WEBVIEW_URL || "https://develop.moadong.com";
Expand Down Expand Up @@ -143,6 +143,7 @@
};

const { handleMessage } = useWebViewMessageHandler({
host: 'webview_slug',
onNavigateBack: handleBack,
onNavigateWebview: handleNavigateWebview,
});
Expand Down
7 changes: 6 additions & 1 deletion constants/eventname.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ export const PAGE_VIEW_EVENT = {
MAIN_PAGE: 'MainPage',
SUBSCRIBE_PAGE: 'SubscribePage',
MORE_PAGE: 'MorePage',
}
}

// 앱 자체 진단용 — 사용자 행동이 아니라 웹↔앱 브리지 상태를 관측한다.
export const DIAGNOSTIC_EVENT = {
BRIDGE_UNKNOWN_MESSAGE: 'Bridge UnknownMessage',
}
7 changes: 6 additions & 1 deletion hooks/use-webview-message-handler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { WebViewMessage, WebViewMessageEvent, WebViewMessageTypes } from '@/types/webview-message.types';
import { reportUnknownBridgeMessage } from '@/utils/webview';
import { useCallback } from 'react';
import { Linking } from 'react-native';

interface UseWebViewMessageHandlerOptions {
/** 처리되지 않은 메시지를 어느 화면이 받았는지 구분하기 위한 이름 */
host: string;
// 뒤로가기 요청 시 호출
onNavigateBack?: () => void;
// 웹뷰 내 화면 이동 요청 시 호출
Expand All @@ -21,6 +24,7 @@ interface UseWebViewMessageHandlerOptions {

// WebView 메시지를 처리하는 Hook
export const useWebViewMessageHandler = ({
host,
onNavigateBack,
onNavigateWebview,
onSubscribe,
Expand Down Expand Up @@ -83,12 +87,13 @@ export const useWebViewMessageHandler = ({
);
break;
default:
console.warn('[WebViewHandler] 알 수 없는 메시지 타입:', message);
reportUnknownBridgeMessage((message as { type?: unknown }).type, host);
}
} catch (error) {
console.error('[WebViewHandler] 메시지 파싱 오류:', error);
}
}, [
host,
onNavigateBack,
onNavigateWebview,
onSubscribe,
Expand Down
1 change: 1 addition & 0 deletions ui/club-detail/club-detail-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export default function ClubWebViewScreen() {

// WebView 메시지 핸들러
const { handleMessage } = useWebViewMessageHandler({
host: 'club_detail',
onNavigateBack: handleBack,
onSubscribe: async (targetId: string, clubName?: string) => {
trackEvent(USER_EVENT.SUBSCRIBE_BUTTON_CLICKED, {
Expand Down
5 changes: 4 additions & 1 deletion ui/home/home-webview-screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useHomeWebViewPreloadContext } from '@/contexts/home-webview-preload-co
import { useMixpanelContext } from '@/contexts/mixpanel-context';
import { useSubscribedClubsContext } from '@/contexts/subscribed-clubs-context';
import { ensureAccessToken } from '@/services/auth-token.service';
import { appendSessionId, buildStudentTokenInjection, getWebViewUserAgent, isWebViewOrigin } from '@/utils/webview';
import { appendSessionId, buildStudentTokenInjection, getWebViewUserAgent, isWebViewOrigin, reportUnknownBridgeMessage } from '@/utils/webview';
import Constants from 'expo-constants';
import { useRouter } from 'expo-router';
import * as WebBrowser from 'expo-web-browser';
Expand Down Expand Up @@ -145,6 +145,9 @@ export function HomeWebViewScreen({ onError }: HomeWebViewScreenProps) {
payload: { version: Constants.expoConfig?.version ?? 'unknown' },
});
break;

default:
reportUnknownBridgeMessage(type, 'home');
}
} catch {
// 파싱 실패 무시
Expand Down
23 changes: 23 additions & 0 deletions utils/webview.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DIAGNOSTIC_EVENT } from '@/constants/eventname';
import { getMixpanel } from '@/utils/mixpanel';
import Constants from 'expo-constants';
import { Platform } from 'react-native';

Expand Down Expand Up @@ -53,3 +55,24 @@ export const buildStudentTokenInjection = (
window.__MOADONG_STUDENT_TOKEN__ = ${JSON.stringify(token)};
})(); true;`;
};

// 웹이 앱보다 앞서 나가 새 메시지를 추가했는데 호스트가 그걸 모르는 상황을 관측한다.
// 각 호스트 switch의 default에서만 부르므로, 그 호스트가 실제로 처리하지 못한 것만 잡힌다.
// 리포팅 실패가 메시지 처리를 막아선 안 되므로 예외를 밖으로 내보내지 않는다.
export const reportUnknownBridgeMessage = (type: unknown, host: string): void => {
// 서드파티가 쏜 비정형 메시지를 걸러내기 위해 문자열 type만 취급한다.
if (typeof type !== 'string') return;

console.warn(`[WebViewBridge] 처리되지 않은 메시지: ${type} (host: ${host})`);

getMixpanel()
.then(mixpanel =>
mixpanel?.track(DIAGNOSTIC_EVENT.BRIDGE_UNKNOWN_MESSAGE, {
message_type: type,
host,
app_version: Constants.expoConfig?.version || '1.0.0',
platform: Platform.OS,
}),
)
.catch(err => console.error('[WebViewBridge] 리포팅 실패:', err));
};
Loading