diff --git a/docs/superpowers/plans/2026-04-27-saju-service-catalog-implementation.md b/docs/superpowers/plans/2026-04-27-saju-service-catalog-implementation.md new file mode 100644 index 0000000..25a1a6e --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-saju-service-catalog-implementation.md @@ -0,0 +1,892 @@ +# 사주 서비스 카탈로그 운영화 Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** spec `docs/superpowers/specs/2026-04-27-saju-service-catalog-design.md`에 정의된 카탈로그를 사이트·견적 시스템에 반영해, 1세트의 카탈로그로 LP·견적·결제 안내·영업용 PDF를 일관되게 굴릴 수 있는 운영 환경을 만든다. + +**Architecture:** 카탈로그 데이터를 `lib/saju-catalog.ts` 단일 모듈로 정의(SSOT). LP, 견적 에디터, 결제 안내 페이지에서 동일 모듈을 import해 일관성 유지. 견적 에디터에는 "카탈로그에서 추가" 모달 + "프리셋 적용" 드롭다운을 붙여 4종 시나리오를 원클릭으로 채운다. 카탈로그 PDF는 LP에 `@media print` 모드를 추가해 브라우저 인쇄 → PDF 저장으로 처리(별도 PDF 라이브러리 도입 X). + +**Tech Stack:** Next.js 16 App Router, TypeScript, Tailwind v4, Supabase(`/admin/quotes` 기존), 정적 페이지(결제 안내). + +**Spec 참조:** `docs/superpowers/specs/2026-04-27-saju-service-catalog-design.md` + +**테스트 전략:** 프로젝트에 테스트 프레임워크가 설치되어 있지 않다. 카탈로그 데이터 모듈은 LP·견적 에디터에서 import해 빌드되는 것으로 검증한다(`npm run build` 통과). UI 검증은 dev 서버에서 시각 확인 + 콘솔 에러 부재로 한다. 가격 합산 로직은 순수 함수로 분리하고, 임의의 모듈 조합으로 LP 시나리오 표가 spec 값(예: 풀세트 사주집 일회성 146만)과 일치하는지 시각 검증한다. + +--- + +## File Structure + +| 파일 | 용도 | 작업 | +|---|---|---| +| `lib/saju-catalog.ts` | 카탈로그 SSOT (코어·모듈·프리셋·헬퍼) | Create | +| `app/services/saju-business/page.tsx` | LP + print 모드 (영업용 PDF 겸용) | Create | +| `app/admin/quotes/[id]/CatalogPicker.tsx` | 모듈 체크 모달 컴포넌트 | Create | +| `app/admin/quotes/[id]/PresetApply.tsx` | 4종 프리셋 드롭다운 | Create | +| `app/admin/quotes/[id]/page.tsx` | 견적 에디터 — 위 두 컴포넌트 통합 | Modify | +| `app/payment-info/page.tsx` | A안 결제 안내 정적 페이지 | Create | +| `app/components/TopNav.tsx` | 사주 비즈니스 메뉴 노출(옵션) | Modify | + +--- + +## Task 1: 카탈로그 데이터 모듈 (SSOT) + +**Files:** +- Create: `lib/saju-catalog.ts` + +- [ ] **Step 1.1: 카탈로그 모듈 작성** + +`lib/saju-catalog.ts`: +```ts +// 쟁승메이드 사주 서비스 카탈로그 — Single Source of Truth +// spec: docs/superpowers/specs/2026-04-27-saju-service-catalog-design.md + +export type Persona = 'salon' | 'influencer' | 'app'; + +export interface Module { + code: string; + name: string; + description: string; + price: number; + recurring: boolean; + category: '기획' | '디자인' | '개발' | '인프라' | '유지보수' | '기타'; + optional: boolean; + mainPersonas: Persona[]; +} + +export const PERSONA_LABEL: Record = { + salon: '사주집·역술원', + influencer: '인스타·틱톡 사주', + app: '사주 앱·플랫폼', +}; + +export const CORE: Module = { + code: 'C1', + name: 'AI 사주 분석 프로그램', + description: '영구 사용권 + 원격 설치 1회 + 영상·PDF 가이드 + 1개월 카톡 응대', + price: 490000, + recurring: false, + category: '개발', + optional: false, + mainPersonas: ['salon', 'influencer', 'app'], +}; + +export const MODULES_ONETIME: Module[] = [ + { code: 'M1', name: '브랜딩 커스터마이징', description: '로고·색상·상호 적용', price: 70000, recurring: false, category: '디자인', optional: true, mainPersonas: ['salon', 'influencer'] }, + { code: 'M2', name: 'AI 분석 100건 충전', description: '정가 990,000원의 95% 할인', price: 50000, recurring: false, category: '개발', optional: true, mainPersonas: ['influencer', 'app'] }, + { code: 'M3', name: '인스타 카드뉴스 자동 생성기', description: '사주 결과 → 카드뉴스 PNG 자동 생성', price: 350000, recurring: false, category: '개발', optional: true, mainPersonas: ['influencer'] }, + { code: 'M4', name: '카카오톡 챗봇', description: '24시간 사주 접수·결제 안내 자동', price: 350000, recurring: false, category: '개발', optional: true, mainPersonas: ['salon'] }, + { code: 'M5', name: '사주집 전용 홈페이지 + 결제', description: '자체 도메인 + Next.js LP + 포트원 PG 결제', price: 600000, recurring: false, category: '개발', optional: true, mainPersonas: ['salon'] }, + { code: 'M6', name: 'TTS 음성 풀이', description: '사주 결과를 목소리로 출력', price: 250000, recurring: false, category: '개발', optional: true, mainPersonas: ['influencer'] }, + { code: 'M7', name: 'PDF 풀이지 자동 인쇄 연동', description: '손님께 종이로 출력해드리기', price: 200000, recurring: false, category: '개발', optional: true, mainPersonas: ['salon'] }, +]; + +export const MODULES_RECURRING: Module[] = [ + { code: 'M8', name: '블로그 SEO 자동 발행', description: '사주 키워드 누적 콘텐츠', price: 300000, recurring: true, category: '유지보수', optional: true, mainPersonas: ['salon', 'influencer'] }, + { code: 'M9', name: '운영 대행 풀패키지', description: 'DM·후기·콘텐츠 일괄 대행', price: 500000, recurring: true, category: '유지보수', optional: true, mainPersonas: ['salon'] }, + { code: 'M10', name: '베이직 유지보수', description: '월 업데이트 + 카톡 응대(48h) + 작은 수정 무료', price: 30000, recurring: true, category: '유지보수', optional: true, mainPersonas: ['salon', 'influencer', 'app'] }, + { code: 'M11', name: '프리미엄 유지보수', description: '24h 응대 + AI 분석 30건/월 포함', price: 70000, recurring: true, category: '유지보수', optional: true, mainPersonas: ['salon', 'influencer', 'app'] }, +]; + +export const ALL_MODULES = [...MODULES_ONETIME, ...MODULES_RECURRING]; + +export interface Preset { + key: string; + label: string; + persona: Persona; + description: string; + oneTimeCodes: string[]; // CORE는 항상 포함, 추가 모듈만 명시 + recurringCode?: string; // 추천 정기 모듈 1개 +} + +export const PRESETS: Preset[] = [ + { + key: 'salon-starter', + label: '컴맹 사주집 입문', + persona: 'salon', + description: '코어 + 브랜딩 + AI 100건 + 베이직 유지보수', + oneTimeCodes: ['M1', 'M2'], + recurringCode: 'M10', + }, + { + key: 'influencer', + label: '인스타 인플루언서', + persona: 'influencer', + description: '코어 + 카드뉴스 자동화 + TTS + 베이직 유지보수', + oneTimeCodes: ['M3', 'M6'], + recurringCode: 'M10', + }, + { + key: 'salon-full', + label: '풀세트 사주집', + persona: 'salon', + description: '코어 + 브랜딩 + 카드뉴스 + 챗봇 + PDF + 운영 대행', + oneTimeCodes: ['M1', 'M3', 'M4', 'M7'], + recurringCode: 'M9', + }, + { + key: 'app-builder', + label: '사주 앱 창업자', + persona: 'app', + description: '코어 + 카드뉴스 + 챗봇 + 홈페이지 (커스텀 별도 협의)', + oneTimeCodes: ['M3', 'M4', 'M5'], + recurringCode: undefined, + }, +]; + +// ── 헬퍼 ──────────────────────────────────────────────── +export function getModule(code: string): Module | undefined { + if (code === 'C1') return CORE; + return ALL_MODULES.find((m) => m.code === code); +} + +export function calcOneTime(codes: string[]): number { + return codes.reduce((sum, c) => sum + (getModule(c)?.price ?? 0), 0); +} + +export function calcOneYearLTV(oneTimeCodes: string[], recurringCode?: string): number { + const oneTime = CORE.price + calcOneTime(oneTimeCodes); + const monthly = recurringCode ? (getModule(recurringCode)?.price ?? 0) : 0; + return oneTime + monthly * 12; +} + +export function presetSummary(preset: Preset) { + const oneTime = CORE.price + calcOneTime(preset.oneTimeCodes); + const monthly = preset.recurringCode ? (getModule(preset.recurringCode)?.price ?? 0) : 0; + return { + oneTime, + monthly, + yearLTV: oneTime + monthly * 12, + }; +} + +// 견적 에디터에서 사용할 quote item 형태로 변환 +export interface QuoteItemSeed { + category: string; + name: string; + description: string; + quantity: number; + unitPrice: number; + optional: boolean; +} + +export function moduleToQuoteItem(m: Module): QuoteItemSeed { + return { + category: m.category, + name: m.name, + description: m.description, + quantity: 1, + unitPrice: m.price, + optional: m.optional && m.code !== 'C1', + }; +} +``` + +- [ ] **Step 1.2: 타입체크** + +Run: `npx tsc --noEmit` +Expected: 에러 없이 종료. 실패 시 표준 fix 후 재실행. + +- [ ] **Step 1.3: spec 가격 시나리오 일치 검증 (수동)** + +`presetSummary`가 spec 7번과 일치하는지 머릿속/계산기로 확인: +- 컴맹 사주집 입문: 49 + 7 + 5 = **61만 일회성 / 3만 월 / 97만 LTV** +- 풀세트 사주집: 49 + 7 + 35 + 35 + 20 = **146만 일회성 / 50만 월 / 746만 LTV** + +수치가 어긋나면 lib 데이터를 spec 기준으로 수정. + +- [ ] **Step 1.4: commit** + +```bash +git add lib/saju-catalog.ts +git commit -m "feat(saju): 카탈로그 SSOT 모듈 — 코어 + 모듈 11종 + 프리셋 4종" +``` + +--- + +## Task 2: 사주 비즈니스 LP 페이지 + +**Files:** +- Create: `app/services/saju-business/page.tsx` + +- [ ] **Step 2.1: LP 페이지 작성** + +`app/services/saju-business/page.tsx` (Server Component, 카탈로그 import해서 렌더): + +```tsx +import { Metadata } from 'next'; +import Link from 'next/link'; +import { + CORE, MODULES_ONETIME, MODULES_RECURRING, PRESETS, + PERSONA_LABEL, presetSummary, +} from '@/lib/saju-catalog'; + +export const metadata: Metadata = { + title: '사주 비즈니스 카탈로그 | 쟁승메이드', + description: '사주집·역술원·인스타 사주 인플루언서를 위한 AI 사주 솔루션. 49만원에 시작, 운영 락인까지 풀세트.', +}; + +const fmtKRW = (n: number) => `${n.toLocaleString()}원`; + +export default function SajuBusinessPage() { + return ( +
+ {/* Hero */} +
+

FOR SAJU BUSINESS

+

+ 사주 운영을 위한 AI 솔루션 —
+ 49만원에 시작, 운영 락인까지 풀세트 +

+

+ 7년차 백엔드 개발자가 직접 만든 검증된 만세력 + AI 분석 엔진. + 컴맹도 5영업일 안에 매출 시작. 모듈식 구조로 필요한 만큼만 추가하세요. +

+
+ 상담 문의 + +
+
+ + {/* 코어 */} +
+

코어 패키지

+
+
+

{CORE.name}

+

{fmtKRW(CORE.price)}VAT 포함

+
+

{CORE.description}

+

납기: 착수 후 5영업일

+
+
+ + {/* 일회성 모듈 */} +
+

일회성 모듈

+
+ {MODULES_ONETIME.map((m) => ( +
+
+

+ {m.code} + {m.name} +

+

{m.description}

+

+ 추천: {m.mainPersonas.map((p) => PERSONA_LABEL[p]).join(' · ')} +

+
+

+{fmtKRW(m.price)}

+
+ ))} +
+
+ + {/* 정기형 모듈 */} +
+

정기형 모듈 (월정액)

+
+ {MODULES_RECURRING.map((m) => ( +
+
+

+ {m.code} + {m.name} +

+

{m.description}

+
+

{fmtKRW(m.price)}/월

+
+ ))} +
+
+ + {/* 시나리오 */} +
+

대표 시나리오

+
+ + + + + + + + + + + {PRESETS.map((p) => { + const s = presetSummary(p); + return ( + + + + + + + ); + })} + +
구성일회성월정액1년 LTV
+

{p.label}

+

{p.description}

+
{fmtKRW(s.oneTime)}{s.monthly ? fmtKRW(s.monthly) : '협의'}{fmtKRW(s.yearLTV)}
+
+
+ + {/* 결제·약관 */} +
+

결제·약관 표준

+
    +
  • • 결제 조건: 선금 50% (착수 시) / 잔금 50% (납품 완료 시)
  • +
  • • 결제 수단: 계좌이체 + 카카오페이 송금 (자세한 안내는 결제 안내)
  • +
  • • 무상 AS: 납품 후 30일
  • +
  • • 무료 수정: 디자인 2회 / 기능 1회
  • +
  • • 저작권: 프로그램 저작권 쟁승메이드 보유, 고객은 본인 사업장 1곳에 한해 영구 사용권 (재배포·양도 불가)
  • +
  • • AI API 사용료(Gemini 월 1~2만)는 고객 부담
  • +
  • • 1건 100만 이상은 PDF 계약서 필수
  • +
+
+ + {/* CTA */} +
+
+

상담 문의 + 견적서 발급

+

5분 안에 도입 가능 여부 + 정확한 견적을 드립니다.

+ 상담 신청 +
+
+ + {/* print 전용 푸터 */} +
+

쟁승메이드 (JaengseungMade) — 박재오

+

이메일: bgg8988@gmail.com · 전화: 010-3907-1392

+

문서 발행일: 2026-04-27

+
+
+ ); +} +``` + +**주의:** 이 페이지는 ` + ); +} +``` + +그리고 page.tsx의 ` + +

선택한 항목을 견적서 항목으로 추가합니다. 정기 모듈은 향후관리 탭이 아닌 견적 항목으로 들어갑니다.

+
+
+
+
+ + +
+ + + ); +} +``` + +- [ ] **Step 3.2: page.tsx에 CatalogPicker 통합** + +`app/admin/quotes/[id]/page.tsx` 수정: +1. 파일 상단 import 추가: + ```tsx + import CatalogPicker from './CatalogPicker'; + ``` +2. 컴포넌트 함수 안 상태 추가 (다른 useState 옆): + ```tsx + const [catalogOpen, setCatalogOpen] = useState(false); + ``` +3. `function addItem()` 정의 바로 아래에 헬퍼 추가: + ```tsx + function addItemsFromCatalog(seeds: { category: string; name: string; description: string; quantity: number; unitPrice: number; optional: boolean }[]) { + setField('items', [ + ...form.items, + ...seeds.map((s) => ({ id: newId(), ...s })), + ]); + } + ``` +4. 견적항목 탭(JSX 안의 `{tab === '견적항목' && (...)}` 블록) 상단에 "+ 항목 추가" 버튼 옆에 카탈로그 버튼 추가. 견적항목 탭 헤더가 다음과 비슷하게 생겼다면: + ```tsx + + ``` + 다음으로 교체: + ```tsx +
+ + +
+ ``` +5. 컴포넌트 return 의 가장 바깥쪽 `
` 안 마지막에 모달 추가: + ```tsx + setCatalogOpen(false)} + onApply={addItemsFromCatalog} + /> + ``` + +**주의**: 견적항목 탭의 정확한 JSX 구조는 page.tsx를 직접 열어 확인 후 수정. "+ 항목 추가" 버튼이 위 예시와 다르게 생겼다면 그 자리에 카탈로그 버튼을 함께 배치. + +- [ ] **Step 3.3: 시각 검증** + +dev 서버 띄운 상태에서: +1. `/admin/login`으로 로그인 +2. `/admin/quotes` → 새 견적서 작성 +3. 견적항목 탭 진입 +4. "📚 카탈로그에서 추가" 클릭 → 모달 열림 +5. 코어 + M3 + M6 체크 → "선택 항목 추가 (3)" 클릭 +6. items에 3건이 추가되는지 + 합계가 49+35+25 = 109만원으로 표시되는지 확인 + +- [ ] **Step 3.4: commit** + +```bash +git add app/admin/quotes/\[id\]/ +git commit -m "feat(quotes): 견적 에디터에 카탈로그 모달 — 모듈 다중 선택 추가" +``` + +--- + +## Task 4: 견적 에디터 — 프리셋 적용 드롭다운 + +**Files:** +- Create: `app/admin/quotes/[id]/PresetApply.tsx` +- Modify: `app/admin/quotes/[id]/page.tsx` + +- [ ] **Step 4.1: PresetApply 컴포넌트 작성** + +`app/admin/quotes/[id]/PresetApply.tsx`: +```tsx +'use client'; +import { useState } from 'react'; +import { + CORE, ALL_MODULES, PRESETS, moduleToQuoteItem, + type Preset, type QuoteItemSeed, +} from '@/lib/saju-catalog'; + +interface MaintenanceSeed { + name: string; + period: string; + monthlyFee: number; + includes: string[]; + recommended: boolean; +} + +interface Props { + onApply: (data: { items: QuoteItemSeed[]; maintenance: MaintenanceSeed[]; title: string }) => void; +} + +export default function PresetApply({ onApply }: Props) { + const [open, setOpen] = useState(false); + + const apply = (preset: Preset) => { + if (!confirm(`프리셋 "${preset.label}"을(를) 적용합니다.\n현재 견적 항목과 향후관리는 덮어써집니다. 진행할까요?`)) return; + + const items: QuoteItemSeed[] = [ + moduleToQuoteItem(CORE), + ...preset.oneTimeCodes + .map((c) => ALL_MODULES.find((m) => m.code === c)) + .filter((m): m is NonNullable => Boolean(m)) + .map(moduleToQuoteItem), + ]; + + const maintenance: MaintenanceSeed[] = preset.recurringCode + ? (() => { + const m = ALL_MODULES.find((x) => x.code === preset.recurringCode); + if (!m) return []; + return [{ + name: m.name, + period: '월간', + monthlyFee: m.price, + includes: [m.description], + recommended: true, + }]; + })() + : []; + + onApply({ + items, + maintenance, + title: `사주 비즈니스 ${preset.label} 패키지`, + }); + setOpen(false); + }; + + return ( +
+ + {open && ( +
+ {PRESETS.map((p) => ( + + ))} +
+ )} +
+ ); +} +``` + +- [ ] **Step 4.2: page.tsx에 PresetApply 통합** + +1. import 추가: + ```tsx + import PresetApply from './PresetApply'; + ``` +2. 헬퍼 함수 추가 (addItemsFromCatalog 옆): + ```tsx + function applyPreset(data: { items: QuoteItemSeed[]; maintenance: MaintenanceSeed[]; title: string }) { + setForm((f) => ({ + ...f, + title: data.title, + items: data.items.map((s) => ({ id: newId(), ...s })), + maintenance: data.maintenance.map((s) => ({ id: newId(), ...s })), + })); + } + ``` + 타입 import도 추가: + ```tsx + import type { QuoteItemSeed } from '@/lib/saju-catalog'; + interface MaintenanceSeed { name: string; period: string; monthlyFee: number; includes: string[]; recommended: boolean; } + ``` +3. 상단 바(`
` 안 오른쪽 버튼 묶음)에 "프리셋 적용" 추가 — `저장` 버튼 왼쪽에: + ```tsx + + ``` + +- [ ] **Step 4.3: 시각 검증** + +1. 새 견적서 작성 +2. 상단 "⚡ 프리셋 적용" 클릭 → 4개 프리셋 드롭다운 +3. "풀세트 사주집" 선택 → 확인 → items 5개(코어 + M1·M3·M4·M7), maintenance 1개(M9 운영 대행) 자동 채워지는지 확인 +4. 합계가 146만원으로 표시되는지 + +- [ ] **Step 4.4: commit** + +```bash +git add app/admin/quotes/\[id\]/ +git commit -m "feat(quotes): 견적 에디터에 프리셋 4종 원클릭 적용" +``` + +--- + +## Task 5: 결제 안내 페이지 (A안) + +**Files:** +- Create: `app/payment-info/page.tsx` + +- [ ] **Step 5.1: page.tsx 작성** + +`app/payment-info/page.tsx`: +```tsx +import { Metadata } from 'next'; + +export const metadata: Metadata = { + title: '결제 안내 | 쟁승메이드', + description: '계좌이체와 카카오페이 송금으로 결제하실 수 있습니다.', +}; + +export default function PaymentInfoPage() { + return ( +
+
+

결제 안내

+

계좌이체 또는 카카오페이 송금으로 결제하실 수 있습니다.

+
+ +
+
+

1. 계좌이체

+
+
은행
(견적서 발급 시 별도 안내)
+
계좌번호
(견적서 발급 시 별도 안내)
+
예금주
박재오
+
+

입금 시 입금자명에 견적서명 또는 회사명 기입 부탁드립니다.

+
+ +
+

2. 카카오페이 송금

+

카카오톡 채팅에서 박재오(010-3907-1392) 검색 후 송금

+
+ +
+

3. 결제 조건

+
    +
  • • 선금 50%: 작업 착수 시
  • +
  • • 잔금 50%: 납품 완료 시
  • +
  • • 100만원 이상: PDF 계약서 작성 후 결제
  • +
  • • 환불: 착수 전 100% / 착수 후 진행률 차감 / 납품 후 환불 불가
  • +
+
+ +
+

세금계산서 발행

+

사업자 등록 고객님께는 입금 확인 후 영업일 기준 3일 안에 발행해드립니다. 사업자등록증 사본을 bgg8988@gmail.com 으로 보내주세요.

+
+
+
+ ); +} +``` + +**주의:** 계좌번호는 깃 저장소에 박지 않는다. "견적서 발급 시 별도 안내"로 두고, 실제 계좌번호는 견적서 notes 또는 이메일로만 전달. + +- [ ] **Step 5.2: 시각 검증** + +`http://localhost:3000/payment-info` 열어 디자인·레이아웃 확인. + +- [ ] **Step 5.3: commit** + +```bash +git add app/payment-info/ +git commit -m "feat(payment): 결제 안내 페이지 — 계좌이체 + 카카오페이" +``` + +--- + +## Task 6: 견적서 notes 표준 문구에 결제 안내 링크 삽입 + +**Files:** +- Modify: `scripts/insert-saju-quote.mjs` (이미 존재하는 등록 스크립트) + +- [ ] **Step 6.1: notes 표준 문구에 결제 안내 페이지 링크 추가** + +`scripts/insert-saju-quote.mjs`의 `notes` 상수 안 `[결제 방법]` 블록을 다음으로 교체: + +```js +[결제 방법] +- 계약금 50% 입금 → 작업 시작 +- 잔금 50% → 사용 가이드 모두 전달한 다음 입금 +- 결제 수단 안내: https://jaengseung-made.com/payment-info +- 100만원 이상 결제 시 PDF 계약서 작성 후 진행 +``` + +- [ ] **Step 6.2: commit** + +```bash +git add scripts/insert-saju-quote.mjs +git commit -m "chore(quotes): notes 표준 문구에 결제 안내 페이지 링크" +``` + +--- + +## Task 7: TopNav 메뉴 노출 (CEO 결정 — 옵션) + +**주의:** 현재 사이트 메시지가 음악 팩 중심으로 정렬되어 있어, 사주 비즈니스 메뉴를 TopNav에 직접 박으면 메시지가 분산될 수 있다. **1차 영업은 직링크(`/services/saju-business`)를 카톡·DM으로 보내는 방식으로 진행**하고, 본 task는 매출 검증 후(예: 사주 카탈로그로 수주 2건 이상 발생) 실행한다. + +매출 검증되었다고 판단될 때만 다음 단계 진행: + +**Files:** +- Modify: `app/components/TopNav.tsx` + +- [ ] **Step 7.1: TopNav LINKS 배열에 사주 비즈니스 추가** + +```tsx +const LINKS = [ + { href: '/', label: '홈' }, + { href: '/services/music/samples', label: '샘플' }, + { href: '/services/music', label: '팩 상세' }, + { href: '/services/saju-business', label: '사주 비즈니스' }, + { href: '/studio', label: '스튜디오' }, +]; +``` + +- [ ] **Step 7.2: 모바일 오버레이도 동일 LINKS 사용 중인지 확인** + +이미 동일 배열을 매핑하므로 별도 수정 불필요. + +- [ ] **Step 7.3: 시각 검증** + +데스크톱·모바일 양쪽에서 메뉴가 보이고 활성 표시(border-bottom)가 정상 작동하는지. + +- [ ] **Step 7.4: commit** + +```bash +git add app/components/TopNav.tsx +git commit -m "feat(nav): 사주 비즈니스 메뉴 노출" +``` + +--- + +## Self-Review 체크리스트 (이 plan 작성 후 자체 점검 결과) + +1. **Spec 커버리지**: + - spec §11 LP `/services/saju-business` → Task 2 ✅ + - spec §11 모듈 프리셋 4종 → Task 4 ✅ + - spec §11 모듈 선택 UI → Task 3 ✅ + - spec §11 카탈로그 PDF → Task 2 (인쇄 모드 통합) ✅ + - spec §11 결제 안내 페이지 → Task 5 + Task 6 ✅ + - spec §8 결제 표준(A안 → B안 트리거) → Task 5 (A안 안내 명시), B안 전환은 별도 spec으로 미루는 것이 spec §12 비범위에 명시됨 + +2. **Placeholder scan**: TBD/TODO 없음. 결제 안내 페이지의 계좌번호는 의도적으로 "견적서 발급 시 별도 안내"로 둠 (보안 사유, plan에 이유 명시). + +3. **Type 일관성**: + - `QuoteItemSeed`는 lib에서 export하고 page.tsx에서 import — 일관 ✅ + - `Module.code`는 string ('C1', 'M1'…) 통일 ✅ + - `getModule('C1')` 호출이 CORE를 반환하도록 헬퍼에서 조건 분기 ✅ + - `MaintenanceSeed`는 page.tsx 내부 인터페이스로 정의, PresetApply에서도 동일 형태 — Step 4.2에 명시 ✅ + +4. **Scope**: 단일 plan으로 적절. PG B안 도입(포트원 SDK 활용)은 별도 spec/plan으로 분리. + +--- + +## 마무리 — 1차 영업 흐름 + +이 plan 완료 후 실제 영업은 다음 흐름: + +``` +DM·카톡으로 /services/saju-business 직링크 발송 + ↓ +고객 관심 → /freelance?service=saju-business 문의 + ↓ +HR(/hr) 응대 → /admin/quotes 새 견적서 → 프리셋 1클릭 + ↓ +고객용 링크 발송 + /payment-info 안내 + ↓ +계약금 입금 → 납품 +``` + +3개월 누적 월 매출 100만 도달 시 → 별도 spec으로 PG B안(포트원 통합) 도입 plan 작성.