feat: Phase 2 — SEO 인프라 + GA 이벤트 + 콘텐츠 엔진 구축

[SEO 인프라]
- app/sitemap.ts: Next.js App Router 사이트맵 자동 생성 (/sitemap.xml)
- app/robots.ts: 크롤러 허용/차단 규칙 + 사이트맵 경로 등록
- app/layout.tsx: JSON-LD 구조화 데이터 추가 (Person + LocalBusiness + OfferCatalog 스키마)
- GA4 config 업데이트 (send_page_view, custom_map)

[서비스 페이지 SEO 메타태그 강화]
- automation: 'AI 업무 자동화 외주' 키워드 12종 최적화
- prompt: 'ChatGPT 프롬프트 잘 쓰는 법', '이미지 생성 프롬프트' 등 구매형 키워드 추가
- website: '소상공인 홈페이지 제작 외주', '홈페이지 제작 비용' 등 롱테일 키워드 추가

[GA 이벤트 트래킹]
- ContactModal: contact_attempt / generate_lead / contact_error 이벤트 추가
  (전환 추적 핵심 — 어떤 서비스에서 문의가 오는지 GA에서 확인 가능)

[홈페이지 콘텐츠 위젯]
- 'AI 자동화 실전 팁' 블로그 포스트 3종 카드 섹션 추가 (블로그 연동 준비)

[콘텐츠 자산 (CONTENT/ 폴더)]
- brand-story.md: 풀/숏/초단문/유튜브 채널 소개용 4종 브랜드 스토리 원고
- youtube-scripts.md: 유튜브 숏츠 스크립트 10편 (훅→문제→시연→CTA 구조)
- sns-calendar.md: 30일 SNS 포스팅 캘린더 (블로그·스레드·카카오·블라인드 채널별)
- blog-drafts.md: 네이버 블로그 SEO 초안 10편 (키워드·소제목·본문 완성)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-24 11:31:20 +09:00
parent 2dd42c7f6b
commit 9be23a5d00
13 changed files with 1119 additions and 33 deletions

View File

@@ -63,10 +63,23 @@ export default function ContactModal({
setFormData((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};
// GA4 이벤트 헬퍼
const trackEvent = (eventName: string, params?: Record<string, string>) => {
if (typeof window !== 'undefined') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const w = window as any;
if (typeof w.gtag === 'function') {
w.gtag('event', eventName, params);
}
}
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setStatus('loading');
setErrorMessage('');
// 문의 시도 이벤트
trackEvent('contact_attempt', { service: formData.service });
try {
const response = await fetch('/api/contact', {
method: 'POST',
@@ -76,9 +89,16 @@ export default function ContactModal({
const data = await response.json();
if (!response.ok) throw new Error(data.error || '문의 전송에 실패했습니다.');
setStatus('success');
// 문의 성공 이벤트 (전환 추적 핵심)
trackEvent('generate_lead', {
event_category: 'contact',
event_label: formData.service,
value: '1',
});
} catch (error) {
setStatus('error');
setErrorMessage(error instanceof Error ? error.message : '문의 전송에 실패했습니다.');
trackEvent('contact_error', { service: formData.service });
}
};