Compare commits
1 Commits
main
...
Productize
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13f8c362c2 |
59
index.html
59
index.html
@ -1,9 +1,64 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>INFINITH - Infinite Marketing</title>
|
||||
<title>INFINITH - 성형외과 AI 마케팅 전략 플랫폼</title>
|
||||
<meta name="description" content="INFINITH는 성형외과·피부과의 유튜브, 인스타그램, 페이스북, 네이버 블로그, 네이버 플레이스, 강남언니 채널을 AI로 분석해 마케팅 진단 리포트, 실행 로드맵, KPI를 제안하는 전략 기획 엔진입니다." />
|
||||
<meta name="keywords" content="성형외과 마케팅, 피부과 마케팅, 병원 마케팅 분석, AI 마케팅 리포트, 의료 마케팅 전략, INFINITH" />
|
||||
<meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large" />
|
||||
<link rel="canonical" href="https://infinith-demo.vercel.app/" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:site_name" content="INFINITH" />
|
||||
<meta property="og:locale" content="ko_KR" />
|
||||
<meta property="og:title" content="INFINITH - 성형외과 AI 마케팅 전략 플랫폼" />
|
||||
<meta property="og:description" content="병원 채널 URL만 입력하면 유튜브·인스타그램·네이버·강남언니를 AI가 분석해 마케팅 진단 리포트와 실행 로드맵을 만듭니다." />
|
||||
<meta property="og:url" content="https://infinith-demo.vercel.app/" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:title" content="INFINITH - 성형외과 AI 마케팅 전략 플랫폼" />
|
||||
<meta name="twitter:description" content="병원 채널 URL만 입력하면 유튜브·인스타그램·네이버·강남언니를 AI가 분석해 마케팅 진단 리포트와 실행 로드맵을 만듭니다." />
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@graph": [
|
||||
{
|
||||
"@type": "Organization",
|
||||
"@id": "https://infinith-demo.vercel.app/#organization",
|
||||
"name": "INFINITH",
|
||||
"url": "https://infinith-demo.vercel.app/",
|
||||
"description": "성형외과·피부과 대상 AI 마케팅 분석 및 전략 기획 플랫폼",
|
||||
"areaServed": "KR",
|
||||
"knowsLanguage": ["ko", "en"]
|
||||
},
|
||||
{
|
||||
"@type": "SoftwareApplication",
|
||||
"@id": "https://infinith-demo.vercel.app/#software",
|
||||
"name": "INFINITH",
|
||||
"applicationCategory": "BusinessApplication",
|
||||
"operatingSystem": "Web",
|
||||
"url": "https://infinith-demo.vercel.app/",
|
||||
"publisher": { "@id": "https://infinith-demo.vercel.app/#organization" },
|
||||
"description": "성형외과·피부과의 유튜브, 인스타그램, 페이스북, 네이버 블로그, 네이버 플레이스, 강남언니 채널을 AI로 분석해 마케팅 진단 리포트, 실행 로드맵, KPI를 제안합니다.",
|
||||
"featureList": [
|
||||
"다채널 마케팅 데이터 자동 수집 (YouTube, Instagram, Facebook, 네이버 블로그, 네이버 플레이스, 강남언니)",
|
||||
"AI 마케팅 진단 리포트 생성",
|
||||
"채널별 변환 전략 및 실행 로드맵 제안",
|
||||
"KPI 설계 및 성과 분석",
|
||||
"AI 콘텐츠 스튜디오 (이미지 생성)"
|
||||
],
|
||||
"offers": { "@type": "Offer", "url": "https://infinith-demo.vercel.app/pricing", "priceCurrency": "KRW" }
|
||||
},
|
||||
{
|
||||
"@type": "WebSite",
|
||||
"@id": "https://infinith-demo.vercel.app/#website",
|
||||
"url": "https://infinith-demo.vercel.app/",
|
||||
"name": "INFINITH",
|
||||
"inLanguage": "ko",
|
||||
"publisher": { "@id": "https://infinith-demo.vercel.app/#organization" }
|
||||
}
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<!-- Self-hosted Pretendard Variable preload (CDN 차단 망 대비) -->
|
||||
<link rel="preload" href="/fonts/PretendardVariable.woff2" as="font" type="font/woff2" crossorigin />
|
||||
<!-- Google Fonts: Inter + Playfair Display (영문 — fallback 안전) -->
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --port=3000 --host=0.0.0.0",
|
||||
"build": "vite build",
|
||||
"build": "vite build && vite build --ssr src/entry-server.tsx --outDir dist/server && tsx scripts/prerender.ts",
|
||||
"preview": "vite preview",
|
||||
"clean": "rm -rf dist",
|
||||
"lint": "tsc --noEmit",
|
||||
|
||||
25
public/llms.txt
Normal file
25
public/llms.txt
Normal file
@ -0,0 +1,25 @@
|
||||
# INFINITH
|
||||
|
||||
> 성형외과·피부과를 위한 AI 마케팅 분석 및 전략 기획 플랫폼. 병원의 유튜브, 인스타그램, 페이스북, 네이버 블로그, 네이버 플레이스, 강남언니 채널을 자동 수집·분석해 마케팅 진단 리포트, 채널별 변환 전략, 실행 로드맵, KPI를 제안합니다.
|
||||
|
||||
## 핵심 기능
|
||||
|
||||
- 다채널 자동 수집: 병원 홈페이지 URL 하나로 SNS 채널을 발견하고 YouTube Data API, Instagram/Facebook, Naver API, 강남언니 데이터를 실측 수집
|
||||
- AI 진단 리포트: 채널별 감사(Audit), 강점·약점, 경쟁 포지셔닝을 정량 수치와 함께 제시
|
||||
- 전략 기획: 채널별 변환 전략, 90일 실행 로드맵, KPI 설계
|
||||
- 콘텐츠 스튜디오: AI 이미지 생성으로 콘텐츠 제작 지원
|
||||
- 성과 분석: 배포 이후 채널 성과 추적
|
||||
|
||||
## 대상
|
||||
|
||||
- 서울 강남권을 포함한 국내 성형외과·피부과
|
||||
- 프리미엄 의료 마케팅을 내부 역량으로 운영하려는 병원 마케팅 담당자와 원장
|
||||
|
||||
## 페이지
|
||||
|
||||
- [홈](https://infinith-demo.vercel.app/): 서비스 소개, 대상, 문제 정의, 해결 방식, 핵심 모듈, 활용 사례
|
||||
- [요금제](https://infinith-demo.vercel.app/pricing): 월간·연간 요금
|
||||
|
||||
## 연락
|
||||
|
||||
- 이메일: team.backstageai@gmail.com
|
||||
28
public/robots.txt
Normal file
28
public/robots.txt
Normal file
@ -0,0 +1,28 @@
|
||||
# INFINITH — AI 크롤러 및 검색엔진 전면 허용
|
||||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /report/loading
|
||||
Disallow: /api-dashboard
|
||||
Disallow: /data-validation
|
||||
|
||||
# AI 답변엔진 크롤러 (명시 허용)
|
||||
User-agent: GPTBot
|
||||
Allow: /
|
||||
User-agent: OAI-SearchBot
|
||||
Allow: /
|
||||
User-agent: ChatGPT-User
|
||||
Allow: /
|
||||
User-agent: ClaudeBot
|
||||
Allow: /
|
||||
User-agent: anthropic-ai
|
||||
Allow: /
|
||||
User-agent: PerplexityBot
|
||||
Allow: /
|
||||
User-agent: Google-Extended
|
||||
Allow: /
|
||||
User-agent: Bingbot
|
||||
Allow: /
|
||||
User-agent: Yeti
|
||||
Allow: /
|
||||
|
||||
Sitemap: https://infinith-demo.vercel.app/sitemap.xml
|
||||
15
public/sitemap.xml
Normal file
15
public/sitemap.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://infinith-demo.vercel.app/</loc>
|
||||
<lastmod>2026-08-28</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://infinith-demo.vercel.app/pricing</loc>
|
||||
<lastmod>2026-08-28</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
19
scripts/prerender.ts
Normal file
19
scripts/prerender.ts
Normal file
@ -0,0 +1,19 @@
|
||||
// vite build(클라이언트) + vite build --ssr(서버) 이후 실행.
|
||||
// dist/server/entry-server.js 의 render()로 랜딩 HTML을 만들어 dist/index.html 의 #root 에 주입한다.
|
||||
import { readFileSync, writeFileSync, rmSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
const dist = resolve(process.cwd(), 'dist');
|
||||
const template = readFileSync(resolve(dist, 'index.html'), 'utf-8');
|
||||
const { render } = await import(resolve(dist, 'server/entry-server.js'));
|
||||
|
||||
const routes = ['/'];
|
||||
for (const url of routes) {
|
||||
const appHtml = render(url);
|
||||
const html = template.replace('<div id="root"></div>', `<div id="root">${appHtml}</div>`);
|
||||
const out = url === '/' ? 'index.html' : `${url.replace(/^\//, '')}/index.html`;
|
||||
writeFileSync(resolve(dist, out), html);
|
||||
const words = appHtml.replace(/<[^>]*>/g, ' ').split(/\s+/).filter(Boolean).length;
|
||||
console.log(`[prerender] ${url} -> dist/${out} (${words} words)`);
|
||||
}
|
||||
rmSync(resolve(dist, 'server'), { recursive: true, force: true });
|
||||
96
src/components/Badge.tsx
Normal file
96
src/components/Badge.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
/**
|
||||
* Badge — 작은 상태/라벨 칩 컴포넌트.
|
||||
*
|
||||
* 3종 variant:
|
||||
* - 'available' → Product 1.0에서 이미 동작하는 기능 표시 (Modules·Pricing 공용)
|
||||
* - 'roadmap' → Q4 2026 예정 기능 (Mock/미구현) 표시
|
||||
* - 'popular' → Pricing 카드 중 메인 추천 티어 하이라이트
|
||||
*
|
||||
* 사용처:
|
||||
* - src/components/Modules.tsx — 각 모듈 카드 우측 상단
|
||||
* - src/pages/PricingPage.tsx — INTELLIGENCE 카드 "가장 인기있는 플랜"
|
||||
*/
|
||||
|
||||
import { CheckCircle2, Clock, Sparkles } from 'lucide-react';
|
||||
|
||||
export type BadgeVariant = 'available' | 'roadmap' | 'popular';
|
||||
|
||||
interface BadgeProps {
|
||||
variant: BadgeVariant;
|
||||
/** 직접 라벨을 주면 variant 기본값을 오버라이드 */
|
||||
label?: string;
|
||||
size?: 'sm' | 'md';
|
||||
className?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* variant별 스타일 & 기본 라벨 매핑.
|
||||
*
|
||||
* ⚠️ 이 매핑은 "랜딩 전체의 시각적 위계"를 결정하는 핵심 지점입니다.
|
||||
* - available: 편안한 성공 톤 (눈이 지치지 않게, 하지만 '확인됨' 신호 명확)
|
||||
* - roadmap: 낮은 채도 (주목받지 말아야 함 — 메인 톤을 갉아먹지 않도록)
|
||||
* - popular: accent 그라디언트 (시선 끌기, 단 1개 카드에만 사용 전제)
|
||||
*
|
||||
* 아래 기본값은 starting point입니다. 랜딩 톤과 맞춰 조정하세요.
|
||||
*/
|
||||
const variantStyles: Record<
|
||||
BadgeVariant,
|
||||
{ bg: string; text: string; border: string; Icon: typeof CheckCircle2; defaultLabel: string }
|
||||
> = {
|
||||
available: {
|
||||
// TODO(user): 초록 계열 vs accent 파스텔 톤 중 선택. 현재는 청록 파스텔.
|
||||
bg: 'bg-emerald-50/80',
|
||||
text: 'text-emerald-700',
|
||||
border: 'border-emerald-200/60',
|
||||
Icon: CheckCircle2,
|
||||
defaultLabel: 'Available Now',
|
||||
},
|
||||
roadmap: {
|
||||
// TODO(user): 회색 톤 강도. 너무 흐리면 안 보이고, 너무 진하면 주목받음.
|
||||
bg: 'bg-slate-100/80',
|
||||
text: 'text-slate-500',
|
||||
border: 'border-slate-200/60',
|
||||
Icon: Clock,
|
||||
defaultLabel: 'Q4 2026 Roadmap',
|
||||
},
|
||||
popular: {
|
||||
// TODO(user): 단색 vs 그라디언트. Modules와 겹치지 않게 Pricing 전용 포인트 컬러 권장.
|
||||
bg: 'bg-gradient-to-r from-accent/10 to-purple-200/40',
|
||||
text: 'text-accent',
|
||||
border: 'border-accent/30',
|
||||
Icon: Sparkles,
|
||||
defaultLabel: '가장 인기있는 플랜',
|
||||
},
|
||||
};
|
||||
|
||||
const sizeStyles = {
|
||||
sm: 'text-xs px-2 py-0.5 gap-1',
|
||||
md: 'text-sm px-3 py-1 gap-1.5',
|
||||
} as const;
|
||||
|
||||
const iconSize = { sm: 12, md: 14 } as const;
|
||||
|
||||
export default function Badge({
|
||||
variant,
|
||||
label,
|
||||
size = 'sm',
|
||||
className = '',
|
||||
}: BadgeProps) {
|
||||
const { bg, text, border, Icon, defaultLabel } = variantStyles[variant];
|
||||
|
||||
return (
|
||||
<span
|
||||
className={[
|
||||
'inline-flex items-center rounded-full border backdrop-blur-sm font-medium',
|
||||
bg,
|
||||
text,
|
||||
border,
|
||||
sizeStyles[size],
|
||||
className,
|
||||
].join(' ')}
|
||||
>
|
||||
<Icon size={iconSize[size]} strokeWidth={2.5} />
|
||||
<span>{label ?? defaultLabel}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
23
src/entry-server.tsx
Normal file
23
src/entry-server.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
// 빌드 시 랜딩(/)만 정적 HTML로 프리렌더하는 서버 엔트리.
|
||||
// AI 크롤러(GPTBot/ClaudeBot/PerplexityBot)는 JS를 실행하지 않으므로
|
||||
// 본문이 HTML에 직접 들어 있어야 인용 가능하다. scripts/prerender.ts에서 호출.
|
||||
import { StrictMode } from 'react';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { StaticRouter } from 'react-router';
|
||||
import { Routes, Route } from 'react-router';
|
||||
import App from './App.tsx';
|
||||
import LandingPage from './pages/LandingPage.tsx';
|
||||
|
||||
export function render(url: string): string {
|
||||
return renderToString(
|
||||
<StrictMode>
|
||||
<StaticRouter location={url}>
|
||||
<Routes>
|
||||
<Route element={<App />}>
|
||||
<Route index element={<LandingPage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
</StaticRouter>
|
||||
</StrictMode>,
|
||||
);
|
||||
}
|
||||
14
src/main.tsx
14
src/main.tsx
@ -1,5 +1,5 @@
|
||||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { createRoot, hydrateRoot } from 'react-dom/client';
|
||||
import { BrowserRouter, Routes, Route } from 'react-router';
|
||||
import App from './App.tsx';
|
||||
import LandingPage from './pages/LandingPage.tsx';
|
||||
@ -17,7 +17,8 @@ import ApiDashboardPage from './pages/ApiDashboardPage.tsx';
|
||||
import LoginPage from './pages/LoginPage.tsx';
|
||||
import './index.css';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
const container = document.getElementById('root')!;
|
||||
const app = (
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<Routes>
|
||||
@ -39,5 +40,12 @@ createRoot(document.getElementById('root')!).render(
|
||||
</Route>
|
||||
</Routes>
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
// 랜딩(/)은 빌드 시 프리렌더된 HTML이 들어 있으므로 hydrate, 나머지는 클라이언트 렌더
|
||||
if (container.hasChildNodes()) {
|
||||
hydrateRoot(container, app);
|
||||
} else {
|
||||
createRoot(container).render(app);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user