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
23 changes: 18 additions & 5 deletions apps/native/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export default function Home() {
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
if (canGoBack && webViewRef.current) {
// WebView에서 뒤로 갈 수 있으면 뒤로 가기
webViewRef.current.goBack();
// WebView에 뒤로가기 메시지 전송 (SPA 라우팅 처리)
webViewRef.current.postMessage(JSON.stringify({ type: 'GO_BACK' }));
return true;
} else {
// 뒤로 갈 수 없으면 앱 종료 로직
Expand Down Expand Up @@ -80,6 +80,21 @@ export default function Home() {
};
}, [canGoBack, exitApp]);

// 웹에서 보내는 메시지 처리
const handleMessage = (event: any) => {
try {
const data = JSON.parse(event.nativeEvent.data);
if (data.type === 'NAVIGATION_STATE') {
// 웹 앱의 라우팅 히스토리 상태 업데이트
setCanGoBack(data.canGoBack);
// exitApp 상태 리셋 (새로운 네비게이션 발생 시)
setExitApp(false);
}
} catch (error) {
console.log('Message parsing error:', error);
}
};

return (
<SafeAreaView style={styles.container} edges={['top', 'bottom']}>
<WebView
Expand All @@ -96,9 +111,7 @@ export default function Home() {
showsVerticalScrollIndicator={false}
allowsInlineMediaPlayback={true}
mediaPlaybackRequiresUserAction={false}
onNavigationStateChange={(navState) => {
setCanGoBack(navState.canGoBack);
}}
onMessage={handleMessage}
/>
</SafeAreaView>
);
Expand Down
4 changes: 4 additions & 0 deletions apps/web/app/(view)/(main)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
'use client';

import { BottomNavBar } from '@/components/BottomNavBar';
import { WebViewBridge } from '@/components/WebViewBridge';

export default function MainLayout({ children }: { children: React.ReactNode }) {
return (
<div className="relative min-h-screen">
{/* WebView와 네이티브 앱 간 통신 */}
<WebViewBridge />

{/* 메인 컨텐츠 - 하단바 높이만큼 여백 */}
<main
className="pb-[70px] overflow-y-auto"
Expand Down
1 change: 1 addition & 0 deletions apps/web/app/(view)/(main)/myPage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ function MyPageContent() {
alignItems: 'center',
justifyContent: 'center',
padding: '16px 20px',
paddingTop: 'calc(16px + env(safe-area-inset-top))',
borderBottom: '1px solid #f0f0f0',
}}>
<h1 style={{
Expand Down
55 changes: 55 additions & 0 deletions apps/web/components/WebViewBridge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use client';

import { useEffect } from 'react';
import { useRouter, usePathname } from 'next/navigation';

/**
* WebView와 네이티브 앱 간 통신을 처리하는 컴포넌트
* - 라우터 변경 시 히스토리 상태를 네이티브 앱에 전송
* - 네이티브 앱의 뒤로가기 요청을 받아 라우터 처리
*/
export function WebViewBridge() {
const router = useRouter();
const pathname = usePathname();

useEffect(() => {
// window.history.length를 사용하여 뒤로 갈 수 있는지 확인
const canGoBack = window.history.length > 1;

// 네이티브 앱에 히스토리 상태 전송
if (typeof window !== 'undefined' && (window as any).ReactNativeWebView) {
(window as any).ReactNativeWebView.postMessage(
JSON.stringify({
type: 'NAVIGATION_STATE',
canGoBack,
})
);
}
}, [pathname]); // pathname 변경 시마다 실행

useEffect(() => {
// 네이티브 앱에서 보내는 메시지 수신
const handleMessage = (event: MessageEvent) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'GO_BACK') {
// 네이티브 앱의 뒤로가기 요청 처리
router.back();
}
} catch (error) {
// 메시지 파싱 실패 시 무시
}
};

window.addEventListener('message', handleMessage);
// React Native WebView는 'message' 대신 document에서 이벤트를 발생시킬 수 있음
document.addEventListener('message', handleMessage as any);

return () => {
window.removeEventListener('message', handleMessage);
document.removeEventListener('message', handleMessage as any);
};
}, [router]);

return null; // UI를 렌더링하지 않음
}
1 change: 1 addition & 0 deletions packages/main-feature/src/components/web/RecordPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export function RecordPage() {
alignItems: 'center',
justifyContent: 'space-between',
padding: '12px 16px',
paddingTop: 'calc(12px + env(safe-area-inset-top))',
}}
>
<div style={{ fontSize: '14px', fontWeight: '600', color: '#333' }}>GutHub | 하단남의 하루</div>
Expand Down
1 change: 1 addition & 0 deletions packages/main-feature/src/components/web/ShoppingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export function ShoppingPage() {
alignItems: 'center',
justifyContent: 'space-between',
padding: '12px 16px',
paddingTop: 'calc(12px + env(safe-area-inset-top))',
borderBottom: '1px solid #f0f0f0',
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AppBar, Layout as SharedLayout } from "@repo/shared";

const Layout = ({ children }: { children: React.ReactNode }) => {
return (
<div className="min-h-screen w-full relative overflow-hidden bg-[#FFF5F5]">
<div className="min-h-screen w-full relative bg-[#FFF5F5]">
<AppBar leftContent={<AppBar.MainLogo nickName="허브" />} rightContent={<AppBar.Date date="4일" />} bgColor="bg-[#FFF5F5]" />
<SharedLayout>
{/* 하단 네비게이션 바는 (main) 레이아웃에서 처리 */}
Expand Down
7 changes: 6 additions & 1 deletion packages/shared/src/components/AppBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,12 @@ interface AppBarProps {

const AppBar = ({ leftContent, text, rightContent, bgColor = 'bg-background' }: AppBarProps) => {
return (
<div className={`fixed top-0 left-auto right-auto z-100 flex justify-between items-center border-none pt-4 px-4 pb-2 w-full h-[32px] ${bgColor}`}>
<div
className={`fixed top-0 left-auto right-auto z-100 flex justify-between items-center border-none px-4 pb-2 w-full h-[32px] ${bgColor}`}
style={{
paddingTop: 'calc(1rem + env(safe-area-inset-top))'
}}
>
<div className="flex items-start">
{leftContent && <div className="mr-6 flex items-center">{leftContent}</div>}
<span className="font-semibold text-[20px] whitespace-nowrap max-w-full">
Expand Down
7 changes: 6 additions & 1 deletion packages/shared/src/components/Layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@ interface LayoutProps {

const Layout = ({ children }: LayoutProps) => {
return (
<div className="pt-[32px]">
<div
className="pt-[32px]"
style={{
paddingTop: 'calc(32px + 1rem + env(safe-area-inset-top))'
}}
>
{children}
</div>
)
Expand Down