feat: 뷰성형외과 미팅 대비 — 데이터 갱신(2026-04-28) + URL 7분할 + 가격 개편

- mockReport.ts (view-clinic): 8채널 실측 갱신
  · YouTube · 강남언니 · 네이버 플레이스 · 네이버 블로그 → Firecrawl
  · Instagram (KR · EN) · Facebook (KR · EN) → Apify
  · createdAt 2026-04-13 → 2026-04-28
- mockPlan.ts (view-clinic): KPI 베이스라인 동기화 + scheduledDate 5월로 이동
- MultiChannelInput: 단일 textarea → 7-필드 분할 (홈페이지 · YT · IG · FB · 네이버플레이스 · 블로그 · 강남언니), 채널별 실시간 검증 아이콘
- PricingPage / FeatureComparisonTable: 49만/149만/399만 → 9만/29만/99만, 월 리포트 수 1회/4회/10회, 경쟁사 추적 1/3/5

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
main
Haewon Kam 2026-04-28 11:25:45 +09:00
parent 938ebacbf9
commit dc8a1db3f2
5 changed files with 155 additions and 151 deletions

View File

@ -1,21 +1,22 @@
/**
* MultiChannelInput Hero / CTA URL .
*
* (PART III ):
* - Hero/CTA URL input "홈페이지 URL → 자동 SNS 발견" ,
* YouTube/Instagram/FB/// URL
* (PART III + 2026-04 ):
* - YouTube/Instagram/FB/// URL
* · .
* - Hero CTA (url state + handleAnalyze + navigate) ** **
* .
* - textarea + "어떻게 입력해야 하는지"
* URL .
* - 7 "여기에 무엇을 넣어야 하는지" .
*
* :
* 1) Textarea URL //
* 2) `classifyUrls()` 7 (`useMemo`)
* 3) , unknown URL
* 4) "채널 분석 시작" onAnalyze(payload) ( navigation )
* 1) 7 URL ( )
* 2) `classifyUrls()`
* 3) : , :
* 4) 1 Analyze
* 5) Submit onAnalyze(payload) ( navigation )
*
* DS :
* - Filled Icons Only (lucide ·outlined )
* - Filled Icons Only (lucide )
* - DS Primary pill: rounded-full + gradient `from-[#4F1DA1] to-[#021341]`
* - variant='hero': ( Hero)
* - variant='cta': (CTA )
@ -31,6 +32,7 @@ import {
DatabaseFilled,
FileTextFilled,
MessageFilled,
CheckFilled,
WarningFilled,
} from './icons/FilledIcons';
import {
@ -64,137 +66,137 @@ interface MultiChannelInputProps {
onAnalyze: (payload: AnalyzePayload) => void;
}
/** 채널 칩 메타 — 렌더링 순서·아이콘·한글명. */
type ChannelKey = keyof Omit<ClassifiedUrls, 'unknown'>;
/** 채널별 메타 — 라벨/아이콘/색상/플레이스홀더 (뷰성형외과 실 URL을 데모 placeholder로). */
const CHANNEL_META: Array<{
key: keyof Omit<ClassifiedUrls, 'unknown'>;
key: ChannelKey;
label: string;
Icon: (props: { size?: number; className?: string }) => ReactElement;
color: string; // 활성화 시 텍스트/아이콘 색
color: string;
placeholder: string;
}> = [
{ key: 'homepage', label: '홈페이지', Icon: GlobeFilled, color: 'text-[#4F1DA1]' },
{ key: 'youtube', label: 'YouTube', Icon: YoutubeFilled, color: 'text-[#FF0000]' },
{ key: 'instagram', label: 'Instagram', Icon: InstagramFilled,color: 'text-[#E1306C]' },
{ key: 'facebook', label: 'Facebook', Icon: FacebookFilled, color: 'text-[#1877F2]' },
{ key: 'naverPlace', label: '네이버 플레이스', Icon: DatabaseFilled, color: 'text-[#03C75A]' },
{ key: 'naverBlog', label: '네이버 블로그', Icon: FileTextFilled, color: 'text-[#03C75A]' },
{ key: 'gangnamUnni', label: '강남언니', Icon: MessageFilled, color: 'text-[#FF5C89]' },
{ key: 'homepage', label: '홈페이지', Icon: GlobeFilled, color: 'text-[#4F1DA1]', placeholder: 'viewclinic.com' },
{ key: 'youtube', label: 'YouTube', Icon: YoutubeFilled, color: 'text-[#FF0000]', placeholder: 'youtube.com/@ViewclinicKR' },
{ key: 'instagram', label: 'Instagram', Icon: InstagramFilled,color: 'text-[#E1306C]', placeholder: 'instagram.com/viewplastic' },
{ key: 'facebook', label: 'Facebook', Icon: FacebookFilled, color: 'text-[#1877F2]', placeholder: 'facebook.com/viewps1' },
{ key: 'naverPlace', label: '네이버 플레이스', Icon: DatabaseFilled, color: 'text-[#03C75A]', placeholder: 'place.naver.com/hospital/11709005' },
{ key: 'naverBlog', label: '네이버 블로그', Icon: FileTextFilled, color: 'text-[#03C75A]', placeholder: 'blog.naver.com/viewclinicps' },
{ key: 'gangnamUnni', label: '강남언니', Icon: MessageFilled, color: 'text-[#FF5C89]', placeholder: 'gangnamunni.com/hospitals/189' },
];
type EmptyClassified = Record<ChannelKey, string>;
const EMPTY_URLS: EmptyClassified = {
homepage: '', youtube: '', instagram: '', facebook: '',
naverPlace: '', naverBlog: '', gangnamUnni: '',
};
/**
* .
* : 'empty', : 'valid', SNS : 'wrong', : 'invalid'
*/
function validateField(value: string, expected: ChannelKey): 'empty' | 'valid' | 'wrong' | 'invalid' {
if (!value.trim()) return 'empty';
const c = classifyUrls(value);
if (c[expected].length > 0) return 'valid';
// 다른 채널로 분류됐으면 'wrong', unknown이면 'invalid'
for (const k of Object.keys(c) as Array<keyof ClassifiedUrls>) {
if (k !== 'unknown' && k !== expected && c[k].length > 0) return 'wrong';
}
return 'invalid';
}
export default function MultiChannelInput({ variant = 'hero', onAnalyze }: MultiChannelInputProps) {
const [text, setText] = useState('');
const [urls, setUrls] = useState<EmptyClassified>(EMPTY_URLS);
// 실시간 분류 — text가 바뀔 때만 재계산.
const classified = useMemo(() => classifyUrls(text), [text]);
// 통합 분류 결과 — 7개 필드 값을 join해 classifyUrls에 한 번에 통과시켜 manualChannels 구성.
const aggregated = useMemo(() => {
const joined = Object.values(urls).filter(Boolean).join('\n');
return classifyUrls(joined);
}, [urls]);
const canAnalyze = hasAnalyzableChannels(classified);
const primaryUrl = pickPrimaryUrl(classified);
const canAnalyze = hasAnalyzableChannels(aggregated);
const primaryUrl = pickPrimaryUrl(aggregated);
const handleSubmit = () => {
if (!canAnalyze || !primaryUrl) return;
const payload: AnalyzePayload = {
primaryUrl,
manualChannels: {
// homepage는 primaryUrl로 들어가므로 manualChannels에서는 제외 (Edge Function에서 `url`을 쓰므로)
youtube: classified.youtube.length ? classified.youtube : undefined,
instagram: classified.instagram.length ? classified.instagram : undefined,
facebook: classified.facebook.length ? classified.facebook : undefined,
naverPlace: classified.naverPlace.length ? classified.naverPlace : undefined,
naverBlog: classified.naverBlog.length ? classified.naverBlog : undefined,
gangnamUnni: classified.gangnamUnni.length ? classified.gangnamUnni : undefined,
youtube: aggregated.youtube.length ? aggregated.youtube : undefined,
instagram: aggregated.instagram.length ? aggregated.instagram : undefined,
facebook: aggregated.facebook.length ? aggregated.facebook : undefined,
naverPlace: aggregated.naverPlace.length ? aggregated.naverPlace : undefined,
naverBlog: aggregated.naverBlog.length ? aggregated.naverBlog : undefined,
gangnamUnni: aggregated.gangnamUnni.length ? aggregated.gangnamUnni : undefined,
},
rawInput: text,
rawInput: Object.entries(urls)
.filter(([, v]) => v.trim())
.map(([k, v]) => `${k}: ${v}`)
.join('\n'),
};
onAnalyze(payload);
};
// variant별 스타일 토큰
// ▸ `text-center` 추가 (원본 Hero 단일 URL input과 일관): Form 필드가 아닌
// "검색창 같은 자유 입력" 시그널로 전환. placeholder 여러 줄도 중앙 정렬.
const isHero = variant === 'hero';
const textareaClass = isHero
? 'w-full px-6 py-4 text-sm md:text-base text-center bg-white/80 backdrop-blur-sm border border-slate-200 rounded-2xl focus:outline-none focus:ring-2 focus:ring-accent/20 focus:border-accent/40 shadow-sm text-primary-900 placeholder:text-slate-400 transition-all resize-none leading-relaxed'
: 'w-full px-6 py-4 text-sm md:text-base text-center bg-white/5 backdrop-blur-sm border border-white/15 rounded-2xl focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent/60 text-white placeholder:text-white/40 transition-all resize-none leading-relaxed';
const inputClass = isHero
? 'w-full pl-11 pr-10 py-2.5 text-sm bg-white/80 backdrop-blur-sm border border-slate-200 rounded-xl focus:outline-none focus:ring-2 focus:ring-accent/20 focus:border-accent/40 shadow-sm text-primary-900 placeholder:text-slate-400 transition-all'
: 'w-full pl-11 pr-10 py-2.5 text-sm bg-white/5 backdrop-blur-sm border border-white/15 rounded-xl focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent/60 text-white placeholder:text-white/40 transition-all';
const labelClass = isHero ? 'text-slate-600' : 'text-white/70';
const helperClass = isHero ? 'text-slate-500' : 'text-white/60';
const chipInactiveBg = isHero ? 'bg-slate-50 border-slate-100' : 'bg-white/5 border-white/10';
const chipInactiveText = isHero ? 'text-slate-400' : 'text-white/40';
const chipActiveBg = isHero ? 'bg-white border-slate-200 shadow-sm' : 'bg-white/10 border-white/25';
const chipActiveText = isHero ? 'text-primary-900' : 'text-white';
return (
<div className="w-full max-w-2xl mx-auto">
{/* URL Textarea
Placeholder : "한 줄씩" .
classifyUrls.ts ···
, "뭉치 → 자동 분류" .
: `text-center` Hero URL input , Form
"검색창 같은 자유 입력" . */}
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
placeholder={
'URL을 한 번에 붙여넣어 주세요\n홈페이지 · YouTube · Instagram · Facebook · 네이버 플레이스 · 블로그 · 강남언니를 AI가 자동으로 분류합니다'
}
rows={5}
className={textareaClass}
spellCheck={false}
/>
{/* 채널 칩 프리뷰 */}
<div className="flex flex-wrap gap-2 mt-4 justify-center">
{CHANNEL_META.map(({ key, label, Icon, color }) => {
const count = classified[key].length;
const isActive = count > 0;
{/* 7개 채널별 입력 필드 — 한 줄에 하나, 좌측 아이콘 + 우측 검증 상태 */}
<div className="flex flex-col gap-2">
{CHANNEL_META.map(({ key, label, Icon, color, placeholder }) => {
const value = urls[key];
const status = validateField(value, key);
return (
<div
key={key}
className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full border text-xs font-semibold transition-all ${
isActive ? `${chipActiveBg} ${chipActiveText}` : `${chipInactiveBg} ${chipInactiveText}`
}`}
>
<Icon size={14} className={isActive ? color : ''} />
<span>{label}</span>
{isActive && (
<span className={`ml-0.5 px-1.5 py-0.5 text-[10px] rounded-full bg-accent/10 text-accent`}>
{count}
</span>
<div key={key} className="relative">
{/* 좌측 채널 아이콘 — 입력 시 컬러 적용, 빈 칸일 땐 회색 */}
<div className="absolute left-3 top-1/2 -translate-y-1/2 pointer-events-none flex items-center gap-2">
<Icon size={16} className={value ? color : isHero ? 'text-slate-400' : 'text-white/40'} />
</div>
{/* 채널 라벨 — 좌측 상단 작은 라벨로 placeholder 위에 노출 */}
<input
type="url"
inputMode="url"
value={value}
onChange={(e) => setUrls((prev) => ({ ...prev, [key]: e.target.value }))}
placeholder={`${label} · ${placeholder}`}
aria-label={label}
className={inputClass}
spellCheck={false}
autoComplete="off"
/>
{/* 우측 검증 상태 아이콘 */}
<div className="absolute right-3 top-1/2 -translate-y-1/2 pointer-events-none">
{status === 'valid' && (
<CheckFilled size={16} className="text-emerald-500" />
)}
{(status === 'wrong' || status === 'invalid') && (
<WarningFilled size={16} className={isHero ? 'text-amber-500' : 'text-amber-300'} />
)}
</div>
</div>
);
})}
</div>
{/* Unknown URL 경고 */}
{classified.unknown.length > 0 && (
<div
className={`flex items-start gap-2 mt-3 px-4 py-3 rounded-xl border ${
isHero
? 'bg-amber-50/80 border-amber-200 text-amber-800'
: 'bg-amber-500/10 border-amber-400/30 text-amber-100'
}`}
>
<WarningFilled size={16} className={isHero ? 'text-amber-500 shrink-0 mt-0.5' : 'text-amber-300 shrink-0 mt-0.5'} />
<div className="text-xs leading-relaxed break-keep">
<span className="font-semibold"> URL {classified.unknown.length}</span> .
(· · ) URL .
</div>
</div>
)}
{/* 보조 안내 — 1개 이상 입력 시 어느 채널이 분석 대상인지 요약 */}
<p className={`text-xs font-medium mt-3 text-center leading-relaxed break-keep ${labelClass}`}>
7 URL 1 .
</p>
{/* "Analyze" pill. · CTA .
··: `rounded-full px-10 py-4 text-lg font-medium`, `max-w-md` (~448px)
(variant + ):
- hero ( ): `#4F1DA1 → #021341` + `text-white`,
hover `#AF90FF`
- cta ( `bg-primary-900` ): 3-stop warm gradient
`#fff3eb → #e4cfff → #f5f9ff` + `text-primary-900`,
Hero/CTA .
hover `hover:scale-[1.02]` + `shadow-2xl` .
disabled: HTML `disabled` , opacity (46b911d ) */}
{/* 분석 시작 버튼 — DS Primary pill */}
<button
onClick={handleSubmit}
disabled={!canAnalyze}
className={`w-full max-w-md mx-auto mt-5 px-10 py-4 text-lg font-medium rounded-full transition-all duration-150 shadow-xl hover:shadow-2xl flex items-center justify-center gap-2 group bg-gradient-to-r active:scale-[0.98] ${
className={`w-full max-w-md mx-auto mt-4 px-10 py-4 text-lg font-medium rounded-full transition-all duration-150 shadow-xl hover:shadow-2xl flex items-center justify-center gap-2 group bg-gradient-to-r active:scale-[0.98] ${
isHero
? 'from-[#4F1DA1] to-[#021341] text-white hover:from-[#AF90FF] hover:to-[#AF90FF]'
: 'from-[#fff3eb] via-[#e4cfff] to-[#f5f9ff] text-primary-900 hover:scale-[1.02]'
@ -204,7 +206,7 @@ export default function MultiChannelInput({ variant = 'hero', onAnalyze }: Multi
<ArrowRight className="w-5 h-5 group-hover:translate-x-1" />
</button>
{/* 보조 안내 */}
{/* 보조 안내 (하단) */}
<p className={`text-xs font-medium mt-3 text-center leading-relaxed break-keep ${helperClass}`}>
· · Online Presence .
</p>

View File

@ -44,9 +44,8 @@ const categories: FeatureCategory[] = [
{
label: '월 리포트 수',
insight: '1회',
intelligence: '2회 + on-demand 4회',
intelligencePlus: '월 20회',
hint: 'on-demand는 영업일 기준 24시간 내 재실행',
intelligence: '4회',
intelligencePlus: '10회',
},
{
label: '전 채널 커버리지',
@ -99,7 +98,7 @@ const categories: FeatureCategory[] = [
label: '경쟁사 추적',
insight: '1개',
intelligence: '3개',
intelligencePlus: '10개',
intelligencePlus: '5개',
},
{
label: '변동 알림',

View File

@ -5,7 +5,7 @@ export const mockPlan: MarketingPlan = {
reportId: 'view-clinic',
clinicName: '뷰성형외과의원',
clinicNameEn: 'VIEW Plastic Surgery',
createdAt: '2026-04-13',
createdAt: '2026-04-28',
targetUrl: 'https://www.viewclinic.com',
// ─── Section 1: Brand Guide ───
@ -96,7 +96,7 @@ export const mockPlan: MarketingPlan = {
},
{
channelId: 'instagram_kr', channelName: 'Instagram KR', icon: 'instagram',
currentStatus: '14K 팔로워, Reels 0개', targetGoal: '50K 팔로워, Reels 주 5개',
currentStatus: '14,047 팔로워, Reels 0개', targetGoal: '50K 팔로워, Reels 주 5개',
contentTypes: ['Reels', 'Carousel', 'Stories', 'Feed Image'],
postingFrequency: '일 1회 + Stories 일 2-3개',
tone: '차분하지만 접근 가능한 — 환자 관점의 Q&A',
@ -105,7 +105,7 @@ export const mockPlan: MarketingPlan = {
},
{
channelId: 'instagram_en', channelName: 'Instagram EN', icon: 'instagram',
currentStatus: '70K 팔로워, Reels 활발', targetGoal: '120K 팔로워',
currentStatus: '70,537 팔로워, Reels 활발', targetGoal: '120K 팔로워',
contentTypes: ['Reels', 'Before/After', 'Patient Stories'],
postingFrequency: '주 5회',
tone: 'Professional & warm — medical tourism storytelling',
@ -114,7 +114,7 @@ export const mockPlan: MarketingPlan = {
},
{
channelId: 'facebook', channelName: 'Facebook', icon: 'facebook',
currentStatus: 'KR 253명 + EN 88K, 로고 불일치', targetGoal: '통합 관리, 광고 리타겟 전용',
currentStatus: 'KR 254명 + EN 88,333명, 로고 불일치', targetGoal: '통합 관리, 광고 리타겟 전용',
contentTypes: ['광고 크리에이티브', '리타겟 콘텐츠'],
postingFrequency: '주 2-3회 (광고 소재 위주)',
tone: '신뢰 기반 — 안전, 경험, 결과 강조',
@ -123,7 +123,7 @@ export const mockPlan: MarketingPlan = {
},
{
channelId: 'naver_blog', channelName: 'Naver Blog', icon: 'globe',
currentStatus: '활성 — 550개 게시글, 월 2~3회 포스팅', targetGoal: '주 2회 포스팅, 월 30,000 방문자',
currentStatus: '활성 — 551개 게시글, 월 2~3회 포스팅 (최근 2026.4.22)', targetGoal: '주 2회 포스팅, 월 30,000 방문자',
contentTypes: ['SEO 블로그 포스트', '시술 가이드', '환자 후기'],
postingFrequency: '주 3회',
tone: '정보성 전문가 — 키워드 중심, 환자 고민 해결',
@ -266,7 +266,7 @@ export const mockPlan: MarketingPlan = {
{ id: 'a6', source: 'social', sourceLabel: '소셜미디어', type: 'photo', title: 'Instagram EN Before/After 사진', description: '@view_plastic_surgery 계정의 2,524개 게시물 중 B/A 사진', repurposingSuggestions: ['KR 계정 크로스포스팅', '유튜브 롱폼 삽입', 'Naver 블로그 활용'], status: 'collected' },
{ id: 'a7', source: 'social', sourceLabel: '소셜미디어', type: 'text', title: '강남언니 환자 리뷰 18,840건', description: '9.5점 평균, 시술별 실 환자 후기 텍스트', repurposingSuggestions: ['후기 기반 Carousel 시리즈', '블로그 환자 스토리', '광고 사회적 증거'], status: 'pending' },
{ id: 'a8', source: 'naver_place', sourceLabel: '네이버 플레이스', type: 'photo', title: '네이버 플레이스 사진', description: '병원 외관, 위치, 시설 사진', repurposingSuggestions: ['블로그 위치 안내 포스트', '구글 마이비즈니스 동기화'], status: 'pending' },
{ id: 'a9', source: 'blog', sourceLabel: '블로그', type: 'text', title: '네이버 블로그 기존 포스트 550개', description: '기존 블로그 포스트 550개 (월 2~3회 업데이트 중)', repurposingSuggestions: ['SEO 최적화 리라이팅', '영상 스크립트 소스'], status: 'collected' },
{ id: 'a9', source: 'blog', sourceLabel: '블로그', type: 'text', title: '네이버 블로그 기존 포스트 551개', description: '기존 블로그 포스트 551개 (월 2~3회 업데이트 중)', repurposingSuggestions: ['SEO 최적화 리라이팅', '영상 스크립트 소스'], status: 'collected' },
{ id: 'a10', source: 'homepage', sourceLabel: '홈페이지', type: 'video', title: '개원 20주년 기념 영상', description: '뷰성형외과 20년 역사 + 시설 소개 영상 (1:30)', repurposingSuggestions: ['브랜드 스토리 Reel', '웹사이트 히어로 영상', '신뢰 광고 소재'], status: 'collected' },
{ id: 'a11', source: 'homepage', sourceLabel: '홈페이지', type: 'photo', title: '시술별 전후 사진 갤러리', description: '눈, 코, 가슴, 윤곽 시술별 비포/애프터 사진', repurposingSuggestions: ['Instagram B/A 시리즈', 'Shorts 전환 소스', '상담 자료'], status: 'needs_creation' },
],
@ -388,7 +388,7 @@ export const mockPlan: MarketingPlan = {
channel: 'Naver Blog',
channelIcon: 'globe',
stage: 'approved',
scheduledDate: '2026-04-14',
scheduledDate: '2026-05-06',
imageTextDraft: {
type: 'blog',
headline: '눈성형 2주 후 솔직 리뷰 — VIEW 성형외과 후기',
@ -409,7 +409,7 @@ export const mockPlan: MarketingPlan = {
channel: 'TikTok',
channelIcon: 'video',
stage: 'scheduled',
scheduledDate: '2026-04-10',
scheduledDate: '2026-05-02',
videoDraft: {
script: `"21년 동안 단 한 건의 의료사고도 없었습니다."\n(숫자 카운터 애니메이션: 0 → 21)\n"이게 VIEW의 자랑입니다."\n#강남성형외과 #무사고 #VIEW성형외과`,
shootingGuide: [

View File

@ -1,8 +1,11 @@
import type { MarketingReport } from '../types/report';
// Last verified: 2026-04-28 (전 채널 실측 — Firecrawl + Apify)
// YouTube · 강남언니 · 네이버 플레이스 · 네이버 블로그: Firecrawl
// Instagram (KR · EN) · Facebook (KR · EN): Apify (apify/instagram-profile-scraper, apify/facebook-pages-scraper)
export const mockReport: MarketingReport = {
id: 'view-clinic',
createdAt: '2026-04-13',
createdAt: '2026-04-28',
targetUrl: 'https://www.viewclinic.com',
overallScore: 62,
@ -19,7 +22,7 @@ export const mockReport: MarketingReport = {
reviewCount: 1805,
},
overallRating: 9.5,
totalReviews: 19030,
totalReviews: 19177,
priceRange: { min: '97,900', max: '13,200,000+', currency: '₩' },
certifications: [
'수술실 CCTV',
@ -61,11 +64,11 @@ export const mockReport: MarketingReport = {
},
channelScores: [
{ channel: 'YouTube', icon: 'youtube', score: 68, maxScore: 100, status: 'warning', headline: '104K 구독자, 주 2-3회 업로드, 최근 조회수 하락' },
{ channel: 'Instagram KR', icon: 'instagram', score: 32, maxScore: 100, status: 'critical', headline: '14K 팔로워, Reels 0개, 모델 프사' },
{ channel: 'Instagram EN', icon: 'instagram', score: 62, maxScore: 100, status: 'warning', headline: '70K 팔로워, Reels 활발, 외국인 환자 중심' },
{ channel: 'Facebook', icon: 'facebook', score: 38, maxScore: 100, status: 'critical', headline: 'KR 253명 방치, EN 88K 활발, 3개 페이지 분산' },
{ channel: '강남언니', icon: 'star', score: 96, maxScore: 100, status: 'excellent', headline: '9.5점/10, 19,030 리뷰, 고객평가우수병원' },
{ channel: 'YouTube', icon: 'youtube', score: 68, maxScore: 100, status: 'warning', headline: '104K 구독자, 주 2-3회 업로드, 누적 1,035만 조회 (2주 +1.9%)' },
{ channel: 'Instagram KR', icon: 'instagram', score: 32, maxScore: 100, status: 'critical', headline: '14,047 팔로워, Reels 0개, 모델 프사' },
{ channel: 'Instagram EN', icon: 'instagram', score: 62, maxScore: 100, status: 'warning', headline: '70,537 팔로워, Reels 활발, 외국인 환자 중심' },
{ channel: 'Facebook', icon: 'facebook', score: 38, maxScore: 100, status: 'critical', headline: 'KR 254명 방치, EN 88K 활발, 3개 페이지 분산' },
{ channel: '강남언니', icon: 'star', score: 96, maxScore: 100, status: 'excellent', headline: '9.5점/10, 19,177 리뷰, 고객평가우수병원' },
{ channel: 'Website', icon: 'globe', score: 65, maxScore: 100, status: 'warning', headline: 'Footer SNS 5개 연결, 트래킹 6개, SEO 기본 설정 양호' },
],
@ -73,13 +76,13 @@ export const mockReport: MarketingReport = {
channelName: '뷰성형외과 VIEW Plastic Surgery',
handle: '@ViewclinicKR',
subscribers: 104000,
totalVideos: 1064,
totalViews: 9952722,
weeklyViewGrowth: { absolute: 67097, percentage: 4.09 },
totalVideos: 1100,
totalViews: 10348571,
weeklyViewGrowth: { absolute: 197925, percentage: 1.91 },
estimatedMonthlyRevenue: { min: 499, max: 1000 },
avgVideoLength: '4.4분',
uploadFrequency: '주 2~3회',
channelCreatedDate: '2015-06-29',
channelCreatedDate: '2015-06-28',
subscriberRank: '#570K',
channelDescription: '💜뷰성형외과💜\nVIEW가 예술이다! ✨\n19층 규모의 안전스마트 빌딩\n환자의 관점에서 생각하고\n환자의 입장에서 아름다움의 가치를 찾습니다.',
linkedUrls: [
@ -127,9 +130,9 @@ export const mockReport: MarketingReport = {
handle: '@viewplastic',
language: 'KR',
label: '국내 (한국어)',
posts: 1420,
followers: 14000,
following: 3822,
posts: 1429,
followers: 14047,
following: 3774,
category: 'Health/beauty',
profileLink: 'litt.ly/viewplasticsurgery',
highlights: ['ABOUT VIEW', '수술정보', '모델 모집', 'VIEW EVENT', '진료안내'],
@ -142,9 +145,9 @@ export const mockReport: MarketingReport = {
handle: '@view_plastic_surgery',
language: 'EN',
label: '국제 (영어)',
posts: 2553,
followers: 70000,
following: 2840,
posts: 2578,
followers: 70537,
following: 2849,
category: 'Health/beauty',
profileLink: 'litt.ly/viewplasticsurgeryenglish',
highlights: ['Katerina', 'Mathilde', 'Kyle & Spizee', 'Chef Rush', 'Thet San', 'Yuri', 'Liposuction', 'Why VIEW?', 'Diana', 'Anti Aging', 'Julie', 'Male', 'Stem Cell', 'Dermatology'],
@ -171,7 +174,7 @@ export const mockReport: MarketingReport = {
pageName: '뷰성형외과',
language: 'KR',
label: '국내 (한국어)',
followers: 253,
followers: 254,
following: 0,
category: '성형외과 의사',
bio: '예쁨이 일상이 되는 순간! #뷰성형외과',
@ -191,7 +194,7 @@ export const mockReport: MarketingReport = {
pageName: 'View Plastic Surgery',
language: 'EN',
label: '국제 (영어)',
followers: 88000,
followers: 88333,
following: 11,
category: '건강/뷰티',
bio: 'Official Account by VIEW Partners',
@ -283,10 +286,10 @@ export const mockReport: MarketingReport = {
otherChannels: [
{ name: '카카오톡', status: 'active', details: '플러스친구 @뷰성형외과의원 — 상담 전용 채널', url: 'https://pf.kakao.com/_xbtVxjl' },
{ name: '네이버 블로그', status: 'active', details: '공식 블로그 활성 — 총 550개 게시글, 월 2~3회 포스팅, 최근 2026.4.8 업로드', url: 'https://blog.naver.com/viewclinicps' },
{ name: '네이버 플레이스', status: 'active', details: '별점 4.41/5, 방문자리뷰 777개, 블로그리뷰 1,480개, 성형외과 전문의 14명', url: 'https://m.place.naver.com/hospital/11709005' },
{ name: '네이버 블로그', status: 'active', details: '공식 블로그 활성 — 총 551개 게시글, 월 2~3회 포스팅, 최근 2026.4.22 업로드', url: 'https://blog.naver.com/viewclinicps' },
{ name: '네이버 플레이스', status: 'active', details: '별점 4.41/5, 방문자리뷰 776개, 블로그리뷰 1,508개, 성형외과 전문의 14명', url: 'https://m.place.naver.com/hospital/11709005' },
{ name: 'TikTok', status: 'not_found', details: '계정 없음 또는 비활성' },
{ name: '강남언니', status: 'active', details: '9.5점/10, 19,030 리뷰, 25 의료진, 고객평가우수병원', url: 'https://www.gangnamunni.com/hospitals/189' },
{ name: '강남언니', status: 'active', details: '9.5점/10, 19,177 리뷰, 25 의료진, 고객평가우수병원', url: 'https://www.gangnamunni.com/hospitals/189' },
{ name: '네이버 카페', status: 'active', details: '"뷰성형외과 성형의 모든것" — 회원 5,984명, 비공개 카페, 시술별 Q&A/전후비교/수술후기 20개 게시판 운영', url: 'https://cafe.naver.com/bluectcom2' },
{ name: 'Threads', status: 'active', details: '@viewplastic + @view_plastic_surgery 계정 연동', url: 'https://www.threads.net/@viewplastic' },
{ name: 'Facebook TH', status: 'inactive', details: '태국 파트너 페이지 14K 팔로워, 2024.7월 이후 방치', url: 'https://www.facebook.com/viewplasticsurgery' },

View File

@ -56,9 +56,9 @@ const tiers: Tier[] = [
id: 'insight',
name: 'INSIGHT',
tagline: '매월 1번, 병원의 온라인 좌표를 점검하세요',
monthlyKRW: 490_000,
annualMonthlyKRW: 390_000,
annualTotalKRW: 4_700_000,
monthlyKRW: 90_000,
annualMonthlyKRW: 72_000,
annualTotalKRW: 864_000,
ctaLabel: '상담 문의',
bullets: [
'월 1회 분석 리포트',
@ -73,13 +73,13 @@ const tiers: Tier[] = [
id: 'intelligence',
name: 'INTELLIGENCE',
tagline: '경쟁사가 지금 무엇을 바꾸는지, 월 2번 확인하세요',
monthlyKRW: 1_490_000,
annualMonthlyKRW: 1_190_000,
annualTotalKRW: 14_300_000,
monthlyKRW: 290_000,
annualMonthlyKRW: 232_000,
annualTotalKRW: 2_784_000,
isPopular: true,
ctaLabel: '상담 문의',
bullets: [
'월 2회 + on-demand 재실행 (월 4회까지)',
'월 4회 분석 리포트',
'8주 콘텐츠 캘린더 + 주간 KPI 기반 조정',
'Vision AI (의료진·슬로건·인증 자동 추출)',
'경쟁사 추적 3개 · 주간 변동 알림',
@ -93,15 +93,15 @@ const tiers: Tier[] = [
id: 'intelligence-plus',
name: 'INTELLIGENCE+',
tagline: '매일 변하는 시장에 즉시 대응하세요',
monthlyKRW: 3_990_000,
annualMonthlyKRW: 3_190_000,
annualTotalKRW: 38_300_000,
monthlyKRW: 990_000,
annualMonthlyKRW: 792_000,
annualTotalKRW: 9_504_000,
ctaLabel: '상담 문의',
bullets: [
'월 20회 분석 리포트 (주간 자동 + on-demand)',
'월 10회 분석 리포트',
'12개월 로드맵 + 월간 전략 리뷰',
'최대 3개 분원 통합 대시보드',
'경쟁사 추적 10개 · 일간 변동 모니터링',
'경쟁사 추적 5개 · 일간 변동 모니터링',
'브랜드 가이드 + 콘텐츠 필러 10종',
'커스텀 리포트 템플릿 (병원 CI 반영)',
'신규 기능 베타 우선 접근',