From 91205192bc5715f0d893ad9c48d9e42877e1ce00 Mon Sep 17 00:00:00 2001 From: Minjung Date: Tue, 10 Nov 2026 11:12:43 +0900 Subject: [PATCH] feat: add AEO optimization, DanceSchool Schema.org JSON-LD, and blog system - app/robots.ts: allow AI crawler user-agents (GPTBot, ClaudeBot, Google-Extended, PerplexityBot, etc.) - app/sitemap.ts: generate dynamic sitemap including new blog routes - components/json-ld.tsx: Schema.org DanceSchool structured data - app/[locale]/blog: blog index and slug pages with FAQPage structured data - lib/blog-data.ts: 4 initial AEO articles (cruise tango, shoes, tuition, posture) - components/navbar.tsx, messages: add blog link to navigation and updated metadata --- .agent/CURRENT.md | 22 ++--- app/[locale]/blog/[slug]/page.tsx | 156 ++++++++++++++++++++++++++++++ app/[locale]/blog/page.tsx | 79 +++++++++++++++ app/[locale]/layout.tsx | 4 + app/robots.ts | 25 +++++ app/sitemap.ts | 37 +++++++ components/json-ld.tsx | 55 +++++++++++ components/navbar.tsx | 11 ++- lib/blog-data.ts | 124 ++++++++++++++++++++++++ messages/en.json | 1 + messages/ko.json | 5 +- 11 files changed, 502 insertions(+), 17 deletions(-) create mode 100644 app/[locale]/blog/[slug]/page.tsx create mode 100644 app/[locale]/blog/page.tsx create mode 100644 app/robots.ts create mode 100644 app/sitemap.ts create mode 100644 components/json-ld.tsx create mode 100644 lib/blog-data.ts diff --git a/.agent/CURRENT.md b/.agent/CURRENT.md index 0476473..d321750 100644 --- a/.agent/CURRENT.md +++ b/.agent/CURRENT.md @@ -1,14 +1,12 @@ # Current focus -**Status:** in-progress -**Working on:** Migrating the registration backend from **Google Apps Script → Google Forms direct submit**. Replacing `GOOGLE_SCRIPT_URL` + Apps Script `doPost` with `GOOGLE_FORM_ID` + per-field `GOOGLE_FORM_ENTRY_*` IDs that POST straight to `formResponse`. Motivation: no OAuth, no Apps Script deploy dance, instructors can still see submissions in the Form's linked Sheet. -**Next step:** (1) finish editing `app/api/register/route.ts` to POST to `https://docs.google.com/forms/d/e/{GOOGLE_FORM_ID}/formResponse` with the entry-ID map, (2) rename / delete the stale `SETUP_GOOGLE_SHEETS.md`, (3) manually test one submission end-to-end, (4) ship. - -### Uncommitted changes in working tree -- `.env.example` — migrated to `GOOGLE_FORM_ID` / `GOOGLE_FORM_ENTRY_*` vars -- `app/api/register/route.ts` — migration in progress (still check `git diff`) -- `SETUP_GOOGLE_FORMS.md` — new setup walkthrough (untracked) - -When this ships, write an ADR at `.agent/decisions/YYYY-MM-DD-google-forms-backend.md` marking `decisions/2026-04-24-google-sheets-backend.md` as superseded, and append a JOURNAL entry. - -Security maintenance: Next.js and its matching ESLint config are updated to16.3.8 for six Next.js advisories. Strict clean install, lint and production build pass. Unpatched braces in lint tooling remains separately unresolved. +**Status:** ready-to-ship +**Working on:** Added **AEO (AI Engine Optimization) & Blog System** to `eltango.vercel.app`: +1. `app/robots.ts`: Opened crawling to all AI bots (`GPTBot`, `ClaudeBot`, `Google-Extended`, `PerplexityBot`, `Yeti`, etc.). +2. `app/sitemap.ts`: Dynamic sitemap for static pages and AEO blog routes. +3. `components/json-ld.tsx`: Schema.org `DanceSchool` structured data for Gangnam studio, Director In-kyung Lee, 2023 Rome champion achievements, and links. +4. `app/[locale]/blog/`: Static blog index and individual post pages (`[slug]`) with `FAQPage` JSON-LD schema for AI answer engine citation. +5. `lib/blog-data.ts`: Initial 4 killer AEO articles (Cruise 2027 tango, shoes comparison, lesson costs, posture & wellness). +6. Updated metadata in `messages/ko.json` with keyword-rich titles and descriptions. + +**Next step:** Push to `main` branch on GitHub (`minjungsung/eltango`) to trigger automatic Vercel production deployment. diff --git a/app/[locale]/blog/[slug]/page.tsx b/app/[locale]/blog/[slug]/page.tsx new file mode 100644 index 0000000..55ef6db --- /dev/null +++ b/app/[locale]/blog/[slug]/page.tsx @@ -0,0 +1,156 @@ +import React from 'react'; +import Link from 'next/link'; +import { notFound } from 'next/navigation'; +import { blogPosts } from '@/lib/blog-data'; +import { Metadata } from 'next'; + +export function generateStaticParams() { + return blogPosts.map((post) => ({ + slug: post.slug, + })); +} + +export async function generateMetadata({ + params, +}: { + params: Promise<{ locale: string; slug: string }>; +}): Promise { + const { slug } = await params; + const post = blogPosts.find((p) => p.slug === slug); + if (!post) return {}; + + return { + title: `${post.title} | 엘땅고 강남본점`, + description: post.description, + keywords: post.tags, + openGraph: { + title: post.title, + description: post.description, + type: 'article', + url: `https://eltango.vercel.app/ko/blog/${post.slug}`, + publishedTime: post.date, + authors: ['이인경 대표원장'], + }, + }; +} + +export default async function BlogPostPage({ + params, +}: { + params: Promise<{ locale: string; slug: string }>; +}) { + const { locale, slug } = await params; + const post = blogPosts.find((p) => p.slug === slug); + + if (!post) { + notFound(); + } + + const faqSchema = post.faq + ? { + '@context': 'https://schema.org', + '@type': 'FAQPage', + mainEntity: post.faq.map((item) => ({ + '@type': 'Question', + name: item.question, + acceptedAnswer: { + '@type': 'Answer', + text: item.answer, + }, + })), + } + : null; + + return ( +
+ {faqSchema && ( +