/** * 앱 공통 레이아웃 — TopAppBar + 상단 광고 배너 + 콘텐츠 + 하단 메뉴 (PRD §3.6.2, Phase B) * 설문/인지검사 동작 페이지는 광고 제외 (PRD §3.10.1) * TopAppBar는 고정 상단 바, 콘텐츠 영역은 상단 56px 여백 필요 */ import { Outlet, useLocation } from 'react-router-dom'; import styled from 'styled-components'; import TopAppBar from './TopAppBar'; import AdBanner from './AdBanner'; import BottomNav from './BottomNav'; import AppMenu from './AppMenu'; import { useState } from 'react'; const Shell = styled.div` display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; padding-bottom: 64px; // 하단 메뉴 높이 `; const TopSpacer = styled.div` height: 56px; flex-shrink: 0; `; const Content = styled.main` flex: 1; padding: 16px; `; export default function Layout() { const location = useLocation(); const [menuOpen, setMenuOpen] = useState(false); // 설문/인지검사 동작 페이지에는 광고 미노출 (PRD §3.10.1) const hideAd = location.pathname.startsWith('/survey') || location.pathname.startsWith('/test'); // TopAppBar: 인지검사 실제 테스트 페이지(/test/mc|ep|cst|nb|mr)에서만 숨김 // TestHome(/test), TestStart(/test/start)는 표시 const isTestActionPage = /^\/test\/(mc|ep|cst|nb|mr)($|\/)/.test(location.pathname); const showTopAppBar = !isTestActionPage; return ( {showTopAppBar && ( setMenuOpen((v) => !v)} /> )} {showTopAppBar && } {!hideAd && } {menuOpen && setMenuOpen(false)} />} ); }