/aeo-geo-bestpractices — SEO + AEO + GEO + Lighthouse 100 플레이북
모든 페이지나 SPA를 유기적 검색(SEO), 응답 엔진(AEO), 생성형/에이전트 AI(GEO)를 위한 발견 머신으로 변환하는 완전한 워크플로우로, 동시에 Google Lighthouse 13(데스크탑 및 모바일)의 5개 카테고리(Performance, Accessibility, Best Practices, SEO, Agentic Browsing)에서 98~100점을 보장합니다.
경로: <skill>은 이 SKILL.md 파일이 위치한 폴더입니다.
0. 실행 전 빠른 정렬
0.1. 스택 감지 및 해당 가이드 읽기
이 파일의 규칙은 모든 사이트에 적용됩니다. 프레임워크 간 차이점(<head> 위치, 공개 폴더, 사전 렌더링 방법, 이미지 최적화 및 TBT 방지 방법, 미리보기 실행 명령어)은 references/frameworks/에 있습니다. 프로젝트에서 스택을 식별하고 1~5단계를 적용하기 전에 가이드를 읽으십시오:
| 프로젝트 신호 | 스택 | 가이드 |
|---|---|---|
angular.json 또는 @angular/core | Angular | references/frameworks/angular.md |
next.config.* 또는 next | Next.js | references/frameworks/nextjs.md |
nuxt.config.* 또는 nuxt / vite.config.* + vue | Nuxt / Vue + Vite | references/frameworks/vue-nuxt.md |
astro.config.* 또는 astro | Astro | references/frameworks/astro.md |
vite.config.* + react | Vite + React | references/frameworks/vite-react.md |
index.html (프레임워크 없음) | 정적 HTML | references/frameworks/html-estatico.md |
스택이 목록에 없으면(예: SvelteKit, Remix, Gatsby, WordPress) 이 파일의 일반 규칙을 적용하고 가장 유사한 가이드를 참조하십시오: SSR/SSG가 있는 프레임워크는 Next/Nuxt 로직을 따르고, SPA는 Vite + React를 따릅니다.
0.2. 사용자 확인
코드를 변경하기 전에 다음을 확인(또는 이미 명시된 경우 프로젝트에서 추출)하십시오:
- 프로덕션 표준 도메인(예:
https://meusite.com.br/). 아직 도메인이 정의되지 않은 경우 질문하거나 명확한 플레이스홀더를 사용하고 사용자에게 알리십시오. - FAQ 표시 vs. 비표시(AEO/GEO 전용):
- 사용자가 페이지 레이아웃에 자주 묻는 질문(FAQ) 섹션을 표시(
<details>/<summary>)할지 아니면 비표시로만(JSON-LD@graph+llms.txt+<noscript>내부) 유지하여 시각적 디자인을 변경하지 않을지 질문하십시오.
- 사용자가 페이지 레이아웃에 자주 묻는 질문(FAQ) 섹션을 표시(
- 훈련을 위한 추적 선호도(
robots.txt생성 또는 변경 시):- 사이트가 이미 선호도를 정의했는지 확인하십시오. 명확한 선택이 없으면 두 가지 옵션을 제공하십시오: 검색을 허용하고 OpenAI 및 Anthropic의 훈련 추적을 차단하거나, 훈련 추적도 허용합니다. 사이트의 다른 규칙은 유지하십시오.
1. 완전한 기술 SEO (index.html, robots.txt, sitemap.xml)
준비된 템플릿은 references/templates.md에서 확인하십시오.
<head>의 필수 메타 태그:<html lang="pt-BR">(또는 프로젝트 언어).- 강력한
<title>(50-65자): 브랜드/전문가 이름 + 주요 제안 포함. <meta name="description">(140-165자): 무엇인지, 누구를 위한 것인지, 무엇을 포함하는지 명확히 응답.<meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" /><link rel="canonical" href="https://dominio.com/" />+<link rel="alternate" hreflang="pt-BR" ... />및x-default.- 완전한 Open Graph(
og:type,og:locale,og:site_name,og:title,og:description,og:url,og:image,og:image:width,og:image:height,og:image:alt) 및 Twitter Cards(summary_large_image).
- 검색 엔진 및 AI 친화적인
public/robots.txt:- ChatGPT 및 Claude 검색에 표시될 가능성을 유지하려면
OAI-SearchBot및Claude-SearchBot을 허용하십시오.Claude-User는 사용자 요청에 응답하여 페이지를 검색할 수 있습니다.ChatGPT-User도 사용자에 의해 트리거되지만 ChatGPT 검색 포함 여부를 제어하지는 않습니다. GPTBot및ClaudeBot은 훈련에 콘텐츠를 사용할 가능성이 있는 문서화된 봇입니다. 사용자가 이 추적을 차단하기로 선택한 경우, 검색 봇을 차단하지 않고 두 봇 모두에 대해Disallow: /가 포함된 자체 그룹을 사용하십시오. 허용하기로 선택한 경우, 둘 다에 대해Allow: /를 사용하십시오. 두 변형은references/templates.md에서 확인하십시오.Google-Extended와 같은 다른 제공업체의 컨트롤은 별도로 처리하십시오. 검색 봇에 대한 액세스를 허용하면 자격이 향상되지만 응답에 인용 또는 표시를 보장하지는 않습니다. 봇 목록을 업데이트할 때 OpenAI 지침 및 Anthropic 지침을 참조하십시오.Sitemap: https://dominio.com/sitemap.xml을 가리키십시오.
- ChatGPT 및 Claude 검색에 표시될 가능성을 유지하려면
- 풍부한
public/sitemap.xml(이미지 + 비디오):- 주요 이미지 및 판매/프레젠테이션 비디오가 있는 경우 네임스페이스
xmlns:image="http://www.google.com/schemas/sitemap-image/1.1"및xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"를 포함하십시오.
- 주요 이미지 및 판매/프레젠테이션 비디오가 있는 경우 네임스페이스
2. AEO — 응답 엔진 최적화 (Featured Snippets, 음성 및 JavaScript 없는 크롤러)
응답 엔진에는 연결된 엔터티와 JavaScript 실행 없이 읽을 수 있는 의미론적 HTML이 필요합니다:
- Schema.org JSON-LD의 연결된
@graph:- 분리된 JSON-LD 스크립트 대신 엔터티를
@id로 연결하는 단일<script type="application/ld+json">를@graph와 함께 생성하십시오(모델은references/templates.md참조):WebSite(#website)Person또는Organization(#person/#org): 공식 소셜 네트워크에 대한sameAs포함WebPage(#webpage):about및mainEntity포함Product/SoftwareApplication/Service(#product):offers포함Course(#course): 과정/훈련/교육 보너스가 있는 경우VideoObject(#video): VSL 또는 프레젠테이션 비디오가 있는 경우FAQPage(#faq): 4~6개의 실제 높은 검색 의도 질문과 직접적이고 완전한 답변(각 40-70단어) 포함.
- 분리된 JSON-LD 스크립트 대신 엔터티를
- SPA(React / Vue / Vite)를 위한 의미론적
<noscript>폴백:- 사전 렌더링이 없는 SPA에만 적용됩니다. Next.js, Nuxt, Astro, SSR/prerender가 있는 Angular 및 정적 HTML에서는 HTML이 이미 완전하게 제공되므로
<noscript>가 불필요합니다(스택 가이드 참조). - LLM 크롤러(
GPTBot,ClaudeBot,PerplexityBot,OAI-SearchBot)는 종종 원시 HTML만 가져오고 JavaScript 번들을 실행하지 않습니다. <div id="root"></div>가 비어 있는 SPA 프로젝트에서는index.html의<noscript><main>...</main></noscript>블록에<h1>, 제공 요약, 전달 항목 목록(<ul><li>) 및 CTA/체크아웃에 대한 직접 링크를 추가하십시오.- 성능에 중요:
<noscript>를 사용하십시오(createRoot().render()를 사용할 때<div id="root">내부의 요소가 아님), SSR 없이#root내부에 DOM을 배치하면 React가 마운트 시 전체 트리를 분해하고 다시 생성하여 LCP를 지연시키고 TBT를 생성하기 때문입니다.
- 사전 렌더링이 없는 SPA에만 적용됩니다. Next.js, Nuxt, Astro, SSR/prerender가 있는 Angular 및 정적 HTML에서는 HTML이 이미 완전하게 제공되므로
3. GEO 및 Agentic Browsing (Lighthouse 13에서 100점)
Google Lighthouse 13은 자율 에이전트 및 LLM(llms-txt 및 ard-schema)을 위해 페이지가 준비되었는지 감사하는 Agentic Browsing 카테고리를 도입했습니다.
public/llms.txt+<head>의 링크:- 구조화된 마크다운으로
public/llms.txt를 생성하십시오(references/templates.md참조):# 제품/브랜드 이름> 가치 제안의 한 문장 요약- 명확한 섹션:
## 소개,## 포함 항목,## 보너스/조건,## 자주 묻는 질문 (AEO / GEO),## 공식 링크.
index.html의<head>에 다음을 추가하십시오:<link rel="help" type="text/plain" href="/llms.txt" title="LLMs.txt — AI를 위한 구조화된 정보" />
- 구조화된 마크다운으로
public/.well-known/ard.json(Agent Resource Discovery v0.91):- SPA(Vite, Next 등)에서
/.well-known/ard.json과 같은 존재하지 않는 경로는 폴백으로index.html(<!doctype html>)을 반환하여 Lighthouse 13의ard-schema감사가 JSON 파싱 오류로 실패하게 만듭니다. - 항상 공식 ARD v0.91 스키마에 따라
public/.well-known/ard.json파일을 생성하십시오(정확한 모델은references/templates.md참조):ardVersion: "0.91.0"및identifier가urn:air:<dominio>:<tipo>:<slug>형식인entries,displayName,type: "application/agent-skills+md",/llms.txt를 가리키는url,description및representativeQueries를 포함해야 합니다.
- SPA(Vite, Next 등)에서
4. Google Lighthouse 100/100: 성능 및 접근성
모바일(시뮬레이션된 4G + 4x CPU 조절)에서 98-100점 및 데스크탑에서 100점을 달성하려면 다음 규칙을 엄격히 적용하십시오:
4.1. LCP (모바일에서 2.1초 미만의 Largest Contentful Paint)
<head>상단의 사전 로드:<link rel="preload" as="image" ... fetchpriority="high">를<title>바로 아래(index.html의 처음 15줄 내)에 배치하십시오. 수십 개의 Open Graph 메타 태그 및 JSON-LD보다 먼저 와야 합니다.
- Lighthouse 모바일 DPR
1.75x에 대한srcSet및sizes보정:- Lighthouse 모바일은 **Moto G Power(
412px너비,devicePixelRatio: 1.75)**를 에뮬레이트합니다. - 이미지에
sizes="(max-width: 767px) 260px, 500px"가 있는 경우 Chrome은260 * 1.75 = 455w를 계산합니다.srcSet의 모바일 버전이260w(455w보다 작음)로 선언된 경우 Chrome은 모바일 이미지를 무시하고 무거운 데스크탑 버전을 다운로드합니다! - 황금률:
srcSet의 모바일 이미지를>= ceil(largura_css_mobile * 1.8)너비로 선언하십시오(예:240px이미지의 경우450w, 모바일에서 전체 너비360px이미지의 경우680w) 및 WebP로 압축하십시오(< 30 KB).
- Lighthouse 모바일은 **Moto G Power(
- 배경 장식 이미지가 LCP를 "도용"하는 것을 방지:
- Hero 섹션에 주요 초상화나
<h1>보다 큰 경계 상자(boundingRect)가 있는 장식용 배경 사진(opacity: 0.15)이 있는 경우 Chrome은 이를 LCP 요소로 간주합니다. - 모바일(
@media (max-width: 767px))에서는 순전히 장식용인 거대한 배경 사진을 숨기거나(display: none) 주요 요소(fetchpriority="high"로 사전 로드됨)가 유일한 LCP 후보가 되도록 치수를 줄이십시오.
- Hero 섹션에 주요 초상화나
- SPA에서 중요한 JS/CSS와 경쟁하는 글꼴을 사전 로드하지 마십시오:
@font-face에서font-display: swap을 사용하고 브라우저가 보조 글꼴보다 먼저 CSS 및 초기 JS 번들 다운로드를 우선시하도록 하십시오.
4.2. TBT 제로 (0ms 총 차단 시간)
- 지속적인 JS 애니메이션을 CSS 컴포지터 +
IntersectionObserver로 대체:- 초기 번들의 전체 애니메이션 라이브러리는
50-150 KB의 JS를 추가하고 수화 시 메인 스레드를 차단합니다. - 스크롤 표시의 경우, GPU(
translate3d)로 가속화된transition: opacity, transform과 함께.is-visible클래스를 전환하는 네이티브IntersectionObserver를 사용하십시오. - 입자 또는 지속적인 회전의 경우,
transform: translate3d(...)/rotate(...)및will-change: transform과 함께 CSS에서@keyframes를 사용하십시오.
- 초기 번들의 전체 애니메이션 라이브러리는
- 로드 시 SVG 필터(
<feTurbulence>,drop-shadow) 및 SMIL<animate>주의:- 고정 치수 없이 전체 화면에
<feTurbulence>를 절대 사용하지 마십시오(CPU에서 수백만 픽셀의 Perlin 노이즈를 다시 계산하여300ms+의 긴 작업을 생성함). CSS에서 가벼운radial-gradient를 선호하십시오. - SVG 하위 요소가 초기 로드 시 매 프레임마다 형상 변경을 겪는 상위 요소에
filter: drop-shadow(...)를 절대 결합하지 마십시오.<animate attributeName="d">를 통한<path>모핑이 있는 경우, 첫 번째 사용자 상호 작용(pointermove,touchstart,wheel) 이후 또는 초기 로드 이후(setTimeoutof8000ms)에만 모핑을 활성화하고, 로드 중에는 CSS 회전만 GPU에서 유지하십시오.
- 고정 치수 없이 전체 화면에
- 부드러운 스크롤(
Lenis)의 코드 분할:Lenis를 첫 번째wheel,touchstart또는keydown이벤트에서만 동적으로 로드하십시오(await import("lenis")). ({ passive: true, once: true }). 이렇게 하면 초기 로드 시0 KB및0msCPU가 소모됩니다.
- 타이머(
setInterval/ 긴급 카운터)를 리프 컴포넌트로 격리:- 절대 루트 컴포넌트
<App />에useCountdown훅(setInterval1초)을 배치하지 마십시오. 이렇게 하면 Lighthouse 테스트 중에 React가 매초마다 전체 페이지를 다시 렌더링합니다. 시계 상태를 작은<CountdownClock />컴포넌트로 격리하여 숫자만 렌더링하십시오.
- 절대 루트 컴포넌트
4.3. 접근성 100점 (WCAG AA)
- 색상 대비 (
color-contrast):- 어두운 배경(
#10100f/#171512)의 작은 텍스트는 대비 비율>= 4.5:1이 필요합니다. 보조 텍스트에는 밝은 색상(#c4beb4,#d6c5b2)을 사용하고 어두운 배경의 강조에는 밝은 주황색(#ff9452,#ffa36b)을 사용하십시오. - 밝은 주황색 배경(
#ff772f)의 버튼은 최대 대비를 위해 어두운 텍스트(#140c06)를 사용해야 합니다.
- 어두운 배경(
- 표시 텍스트와 접근 가능한 이름 간의 일관성 (
label-content-name-mismatch):aria-label을 포함하는 링크/버튼 내에 정확히 동일한 표시 텍스트가 없는 경우aria-label을 절대 배치하지 마십시오(예: 로고가CF ZANELLA.라고 표시되면aria-label="Início"를 배치하지 마십시오).
- 의미론 및 키보드:
<body>의 첫 번째 포커스로<a class="skip-link" href="#conteudo">콘텐츠로 건너뛰기</a>를 포함하십시오.- 엄격한 제목 계층 구조(
h1->h2->h3)를 사용하고 모든<section>에aria-labelledby를, 상태 선택기에aria-pressed를 사용하십시오.
5. Google Lighthouse를 사용한 자동화된 검증
스킬의 자동화된 스크립트를 실행하여 변경 사항을 항상 검증하십시오:
# 로컬 프로젝트: 스택 감지, `npm run build` 실행(있는 경우) 및 포트 4173에서 미리보기 실행
node <skill>/scripts/run-lighthouse.mjs
# 빌드 건너뛰기 (빌드 출력이 이미 최신 상태인 경우)
...[번역을 위해 잘림]