-
Notifications
You must be signed in to change notification settings - Fork 0
[feature] 상세 페이지 UI 웹 이관을 위한 WebView 통신 핸들러 구현 및 동적 Safety Area 적용 #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughReact Native WebView 메시지 파싱 훅 Changes
Sequence Diagram(s)sequenceDiagram
participant WebView as React Native WebView
participant Hook as useWebViewMessageHandler
participant Screen as ClubDetailScreen
participant Native as Native / App Logic
WebView->>Hook: onMessage(event)\nevent.nativeEvent.data (JSON string)
Hook->>Hook: JSON.parse -> WebViewMessage
alt NAVIGATE_BACK
Hook->>Screen: onNavigateBack()
Screen->>Native: navigateBack()
else NOTIFICATION_SUBSCRIBE
Hook->>Screen: onSubscribe(clubId, clubName?)
Screen->>Native: requestPermissionIfNeeded -> subscribe(clubId)
else NOTIFICATION_UNSUBSCRIBE
Hook->>Screen: onUnsubscribe(clubId)
Screen->>Native: unsubscribe(clubId)
else SHARE
Hook->>Screen: onShare({title, text, url})
Screen->>Native: openShareSheet(payload)
else Unknown / parse error
Hook->>Hook: console.warn / ignore
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@hooks/use-webview-message-handler.ts`:
- Around line 19-45: The handleMessage function must validate the parsed message
shape and payload types before acting: after JSON.parse in handleMessage, check
that message is an object and message.type is a string; for
WebViewMessageTypes.NOTIFICATION_SUBSCRIBE ensure message.payload exists and
typeof message.payload.clubId === 'string' and typeof message.payload.clubName
=== 'string' before calling onSubscribe, and for
WebViewMessageTypes.NOTIFICATION_UNSUBSCRIBE ensure typeof
message.payload.clubId === 'string' before calling onUnsubscribe; if validations
fail, log a warning (use console.warn) and skip calling
onSubscribe/onUnsubscribe.
In `@ui/club-detail/club-detail-screen.tsx`:
- Around line 134-154: When rendering the error header (when hasError is true)
ensure the top safe-area inset is applied so the Header doesn't overlap the
translucent StatusBar: modify the conditional branch that renders Header to use
a container that includes the top inset (either by changing Container edges to
include 'top' when hasError or wrapping Header in a SafeAreaView) so the top
padding is applied only for the error state; update the hasError branch (where
Container, StatusBar, Header, BackButton, HeaderTitle, SubscribeButton, and
MoaImage are used) to add that top-safe-area adjustment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@ui/club-detail/club-detail-screen.tsx`:
- Around line 108-136: Add a minimal validity guard for the incoming targetId in
the onSubscribe and onUnsubscribe handlers: check that targetId is a non-empty
string (e.g., typeof targetId === 'string' && targetId.trim().length > 0) and
return early (optionally log or track an error) if it fails; apply this guard
before calling isSubscribed(targetId), toggleSubscribe(targetId), or using
targetId elsewhere, ensuring you still call trackEvent where appropriate (e.g.,
keep the subscribe trackEvent but only proceed with
toggleSubscribe/setShowPermissionDialog when targetId is valid).
♻️ Duplicate comments (1)
ui/club-detail/club-detail-screen.tsx (1)
139-159: 에러 헤더에 상단 Safe Area 보정이 필요합니다.
StatusBar translucent+edges={['bottom']}조합에서hasError헤더가 상태바와 겹칠 수 있습니다.🛠️ 제안 수정안
-import { SafeAreaView } from 'react-native-safe-area-context'; +import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; ... const trackEvent = useMixpanelTrack(); + const insets = useSafeAreaInsets(); ... - {hasError && ( - <Header> + {hasError && ( + <Header style={{ paddingTop: insets.top + 12 }}>
웹뷰에서 전달되는 SHARE 메시지를 처리하기 위해 타입과 훅을 확장하고, ClubWebViewScreen에 네이티브 공유 로직을 연결했습니다. 기존 구조를 유지하며 기능을 확장했습니다.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@ui/club-detail/club-detail-screen.tsx`:
- Around line 137-142: The onShare handler currently passes url only in the url
field (iOS-only) and lacks error handling; update the onShare function to build
a single message that concatenates text and url (e.g., `${text}\n${url}`) so
Android receives the link, call Share.share with that combined message and
maintain title, and wrap the await Share.share call in a try/catch to
handle/rethrow or log errors as appropriate.
♻️ Duplicate comments (1)
ui/club-detail/club-detail-screen.tsx (1)
146-167: 에러 헤더의 Safe Area 보정 이슈는 기존 코멘트와 동일합니다.
이 구간은 상단 inset 미적용 가능성이 있어 이전 리뷰 의견을 참고해주세요.
🧹 Nitpick comments (1)
types/webview-message.types.ts (1)
2-16: 타입 리터럴을 상수 기반으로 묶어 드리프트를 줄여보세요.
현재 문자열 리터럴과 상수 객체가 분리되어 있어 불일치 위험이 있습니다. 상수 값을 참조하도록 타입을 연결하면 유지보수가 쉬워집니다.♻️ 제안 변경
export type WebViewMessage = - | { type: 'NAVIGATE_BACK' } - | { type: 'NOTIFICATION_SUBSCRIBE'; payload: { clubId: string; clubName?: string } } - | { type: 'NOTIFICATION_UNSUBSCRIBE'; payload: { clubId: string } } - | { type: 'SHARE'; payload: { title: string; text: string; url: string } }; + | { type: typeof WebViewMessageTypes.NAVIGATE_BACK } + | { type: typeof WebViewMessageTypes.NOTIFICATION_SUBSCRIBE; payload: { clubId: string; clubName?: string } } + | { type: typeof WebViewMessageTypes.NOTIFICATION_UNSUBSCRIBE; payload: { clubId: string } } + | { type: typeof WebViewMessageTypes.SHARE; payload: { title: string; text: string; url: string } };
| onShare: async ({ title, text, url }: { title: string; text: string; url: string }) => { | ||
| await Share.share({ | ||
| title, | ||
| message: text, | ||
| url, | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
React Native Share.share API Android url field behavior documentation
💡 Result:
In React Native’s built-in Share.share(content, options) API, the content.url field is documented as iOS-only. On Android, the supported content fields are message (text to share) and title (used as the chooser/title), so url is not part of the Android behavior and may be ignored by the native share sheet. To share a link on Android, put it in message (e.g., message: 'Check this out: https://…'). [1][2]
Docs: React Native Share API reference (shows url - a URL to share iOS, title - … Android). [1][2]
Sources
- React Native docs (v0.82) – Share API:
urlis iOS-only, Android usesmessage/title. [1] - React Native docs (“Next”) – same platform notes for
url/title. [2]
Android에서 공유 URL이 누락될 수 있습니다.
React Native Share.share에서 url 필드는 iOS 전용이며 Android에서는 무시됩니다. Android에서 링크 공유를 보장하려면 message에 URL을 포함해야 합니다. 또한 공유 실패 시 예외가 발생할 수 있으므로 최소한의 try/catch 처리도 권장됩니다.
🐛 제안 수정안
- onShare: async ({ title, text, url }: { title: string; text: string; url: string }) => {
- await Share.share({
- title,
- message: text,
- url,
- });
- },
+ onShare: async ({ title, text, url }: { title: string; text: string; url: string }) => {
+ try {
+ const message =
+ Platform.OS === 'android'
+ ? [text, url].filter(Boolean).join('\n')
+ : text;
+ await Share.share({
+ title,
+ message,
+ url,
+ });
+ } catch (e) {
+ console.warn('[ClubWebViewScreen] Share failed', e);
+ }
+ },🤖 Prompt for AI Agents
In `@ui/club-detail/club-detail-screen.tsx` around lines 137 - 142, The onShare
handler currently passes url only in the url field (iOS-only) and lacks error
handling; update the onShare function to build a single message that
concatenates text and url (e.g., `${text}\n${url}`) so Android receives the
link, call Share.share with that combined message and maintain title, and wrap
the await Share.share call in a try/catch to handle/rethrow or log errors as
appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@ui/club-detail/club-detail-screen.tsx`:
- Line 22: The error flag state (hasError via setHasError) is not reset on
subsequent loads, so add an onLoadStart handler that calls setHasError(false) to
clear the error when a new load begins; ensure this pairs with the existing
onError handler which sets setHasError(true), and attach the onLoadStart prop to
the same component responsible for loading (e.g., in ClubDetailScreen where
onError is used) so the header will hide after a successful retry.
♻️ Duplicate comments (3)
ui/club-detail/club-detail-screen.tsx (3)
108-136: (중복) targetId 유효성 가드 필요.
이전 리뷰에서 지적된 사항과 동일합니다.
137-142: (중복) Android 공유 시 URL 누락 가능성.
이전 리뷰와 동일하게, Android에서는url필드가 무시될 수 있어message에 포함 및 예외 처리 권장이 필요합니다.React Native Share.share url field Android message
147-167: (중복) 에러 헤더 상단 Safe Area 보정 필요.
이전 리뷰에서 제안된 상단 inset 적용 이슈가 여전히 존재합니다.react-native-safe-area-context SafeAreaView edges top translucent StatusBar
1. 배경 (Background)
기존에는 동아리 상세 페이지의 상단 탑바(뒤로가기, 타이틀)는 네이티브에서, 내용은 웹뷰에서 렌더링했습니다.
하지만 스크롤 시 웹뷰 내부 콘텐츠와 네이티브 탑바 간의 디자인 일관성을 맞추기 어려웠고, 더 몰입감 있는 UI를 제공하기 위해 상세 페이지의 탑바 영역까지 웹뷰에서 모두 처리하도록 변경했습니다.
이에 따라 기존 네이티브 헤더에 있던 '뒤로가기' 및 '알림 구독/취소' 기능을 웹뷰 내부 버튼으로 제어해야 하는 요구사항이 발생하여, WebView와 Native 간의 통신 구조를 구축했습니다.
2. 주요 변경 사항 (Changes)
2026-01-24.143236.mp4
A. WebView 메시지 통신 구조 (
useWebViewMessageHandler)웹에서 네이티브 기능을 호출할 수 있도록 단방향 메시지 통신을 구현했습니다.
통신 구조 (Architecture)
sequenceDiagram participant Web as WebView Content participant RN as React Native WebView participant Hook as useWebViewMessageHandler participant Native as Native Logic Note over Web, Native: 예: 알림 구독 요청 Web->>RN: window.ReactNativeWebView.postMessage(JSON) RN->>Hook: onMessage 이벤트 수신 Hook->>Hook: JSON 파싱 및 타입 확인 (WebViewMessage) rect rgb(200, 240, 255) Note right of Hook: Switch Case로 분기 처리 Hook->>Native: onSubscribe() 콜백 실행 end Native-->>Native: 구독 로직 실행 / 믹스패널 트래킹구현된 이벤트 (Events)
NAVIGATE_BACK: 앱 내비게이션 뒤로가기 실행NOTIFICATION_SUBSCRIBE: 알림 구독 요청 (권한 확인 및 로직 수행)NOTIFICATION_UNSUBSCRIBE: 알림 구독 취소새로운 이벤트 추가 방법 (How to Extend)
webview-message.types.ts의WebViewMessageUnion 타입에 새로운type과payload정의useWebViewMessageHandler.ts의handleMessage내부 switch 문에 해당 case 추가useWebViewMessageHandler호출처(Page)에서 핸들러 함수 연결B. 동적 Safety Area 적용 (Dynamic Safety Area)
웹뷰가 상단 영역(Status Bar)까지 확장되도록 네이티브 레이아웃을 변경했습니다.
SafeAreaView의 Top inset을 제거하고,StatusBar를 투명(translucent)하게 설정.padding-top: env(safe-area-inset-top)을 적용하여 콘텐츠가 가려지지 않게 처리.C. 초기 알림 상태 동기화 (Initial State Sync)
웹뷰 로드 시점의 알림 구독 상태를 전달하기 위해 URL Query Parameter를 활용했습니다.
is_subscribed=true: 이미 구독 중인 상태로 아이콘 렌더링 필요
3. 트러블슈팅 (Troubleshooting)
Q1. 상단 영역 콘텐츠 가림 문제 (Safety Area Overlap)
Q2. iOS 오버스크롤(Bounce)로 인한 UI 깨짐
Summary by CodeRabbit
새로운 기능
버그 수정
✏️ Tip: You can customize this high-level summary in your review settings.