import { AboutSection, AnswerBlock, BookingSection, EssentialInfoSection, ExhibitionSection, FaqSection, FestivalSection, GallerySection, HeroSection, InquirySection, ITEM_SECTIONS, LocalGuideSection, WeatherSection, LocationSection, RulesSection, SpaceSection, StorySection, UnitsSection, } from '@/sections'; import {useSite} from '@/lib/site-context'; import {isSectionEnabled} from '@/lib/derive'; /** * StorySection 이 탭으로 묶어 그리는 아이템. * * ★ `sections/StorySection.tsx` 의 STORY_KINDS 와 같은 목록이어야 한다. * 어긋나면 어떤 아이템은 탭 밖에 따로 서고, 어떤 아이템은 어디에도 안 나온다. */ const STORY_KINDS = ['songs', 'people', 'chronicle', 'literature', 'postcard', 'quiz', 'daily']; /** * 홈. * * 섹션 순서는 관리자 에디터가 정한 것(theme.sections)을 따른다. * ★ 히어로 바로 다음에 AnswerBlock 이 온다. 이 자리는 순서 설정과 무관하게 고정이다 — * AI 검색이 답을 뽑아 가는 자리라, 사장님이 아래로 내리면 인용률이 떨어진다. * * ★ variantId 는 아직 해석하지 않는다. * `SectionSetting.variantId` 에 사장님이 [디자인] 탭에서 고른 배리에이션 키 * (예: 'rules.cards')가 실려 오지만, 발행본은 그 값을 보지 않고 섹션마다 하나뿐인 * 기본 레이아웃으로 그린다. 배리에이션 40종을 발행본에 옮기는 건 별도 작업이다. * 렌더러가 모르는 키가 와도 화면이 깨지지 않아야 한다는 규칙(site-payload.ts)은 * "값을 통째로 무시한다"로 이미 지켜진다. * ★ 붙여넣기 아이템(songs·daily·…)은 배리에이션이 타입당 하나뿐이라 지금은 어긋날 것이 없다. */ export function HomePage() { const payload = useSite(); /** * 섹션 id → 발행본 컴포넌트. * * ★ 에디터의 섹션 타입(admin `builder/canvas/registry.ts`)과 이 표의 키가 어긋나면, * 사장님이 켜 둔 섹션이 아래 `if (!Component) return null` 에서 말없이 사라진다. * 실제로 rules(이용 규정)·booking·space·inquiry·exhibition 다섯이 그렇게 빠져 있었고, * 숙박 사장님이 써 넣은 환불 규정이 payload 까지 실려 오고도 발행본에 없었다. * 에디터에 섹션 타입을 늘릴 때는 여기도 같이 늘린다. */ const SECTION_COMPONENTS: Record React.ReactElement | null> = { intro: AboutSection, info: EssentialInfoSection, rooms: UnitsSection, menu: UnitsSection, programs: UnitsSection, rules: RulesSection, booking: BookingSection, space: SpaceSection, inquiry: InquirySection, exhibition: ExhibitionSection, photos: GallerySection, local: LocalGuideSection, festival: FestivalSection, weather: WeatherSection, map: LocationSection, faq: FaqSection, // 붙여넣기 아이템. 데이터가 fact 가 아니라 theme.sections[].data 의 JSON 에서 온다. // ★ 같은 컴포넌트를 두 번 그리지 않는 아래 규칙과 상관없다 — 아이템마다 컴포넌트가 다르다. ...ITEM_SECTIONS, // ★ 지역 이야기(가요·인물·연표·엽서·퀴즈·문학·일력)는 StorySection 이 탭으로 묶어 그린다. // 그래서 그 일곱은 위 표에서 **가려낸다** — 여기 남겨 두면 탭 안과 밖에 두 번 나온다. ...Object.fromEntries(STORY_KINDS.map((kind) => [kind, StorySection])), }; const rendered = new Set(); return (
{payload.theme.sections .filter((section) => section.enabled && section.id !== 'hero') .map((section) => { const Component = SECTION_COMPONENTS[section.id]; if (!Component) return null; // 같은 컴포넌트를 두 번 그리지 않는다(rooms/menu/programs 가 모두 UnitsSection). const key = Component.name; if (rendered.has(key)) return null; rendered.add(key); return ; })} {/* 섹션 설정에 없더라도 오시는 길은 항상 나간다 — 위치 질의의 근거다. */} {!isSectionEnabled(payload, 'map') && }
); }