GEO
스터디에서 여러번 언급된 SEO, next를 쓰는 이유 중 하나이기도 할 정도로 중요한데 이제는 하나 더 고려할 게 생김 -> GEO(Generative Engine Optimization)
요즘은 구글링보다 ai에게 질문하며 검색하는 사람이 많음 → AI에게 선택되려면 어떻게 해야할까 가 중요해짐

GEO(Generative Engine Optimization) : 생성형 엔진 최적화 생성형 AI가 답변을 만들 때 내 콘텐츠를 참고·인용·언급하도록 돕는 최적화
SEO가 전통적으로 검색 결과 페이지에서의 발견과 클릭을 중심으로 했다면,
GEO는 그 기반 위에서 AI 답변 안에서의 인용과 기여까지 함께 봄
GEO 노출
1. 콘텐츠가 웹에서 발견되고 이해돼야 함
AI 답변도 결국 웹의 콘텐츠를 참고해야 하니까, 기초 SEO가 안 되어 있으면 GEO도 어려움
2. AI가 답변을 만들 때 “쓰기 좋은 정보 단위”여야 함
AI에 잘 쓰이는 콘텐츠는 대체로
질문에 답: FAQ, How-to 가이드
문단 구조가 명확: 명확한 소제목, 번호 매긴 리스트, 표로 정리된 데이터
정의·비교·가이트 콘텐츠가 분명:

근거가 드러나는 글:

이 기준에 맞을수록 AI가 인용할 확률 Up
AI는 전통 검색처럼 “페이지 전체를 하나의 랭킹 객체”로만 보지 않고, 특정 문단, 정의, 요약, 절차, 비교, 근거 문장 같은 더 잘게 쪼개진 정보 단위를 활용함
특히 생성형 AI는 사용자의 질문 의도와 맥락을 파악해 연속적인 대화를 이어가기 때문에 실제 고객이 자주 묻는 질문 등, 소비자의 관점에서 콘텐츠를 만들면 AI가 그 내용을 더 잘 이해하고 답변에 인용할 확률 Up
예시로

이런 대화형 콘텐츠를 웹에 삽입해 놓으면 유리함
3. 인용·언급 가능성이 중요해짐
GEO에서 노출의 단위는 몇 위냐뿐 아니라 얼마나 자주 답변의 근거로 쓰이느냐
좋은 예로, Bing AI Performance는 이제 AI-generated answers에서 total citations(내 사이트 총 인용 횟수), cited pages(인용된 내 사이트의 특정 페이지), grounding query phrases(AI가 내 콘텐츠를 끌어오는 데 사용한 검색/질문 표현들) 등으로 사이트 인용을 측정함
내 블로그에 적용해보자 🚀
1️⃣ 각 포스트 메타데이터에 FAQ 추가
---
title: 'FTP 사용법 알아보자'
date: '2025-02-27'
description: FTP 개념, mac에서 사용법 알아보기
tags: ['FTP']
// frontmatter에 faq 필드 추가
faq:
- q: 'FTP와 SFTP의 차이점은 무엇인가요?'
a: 'FTP는 암호화 없이 데이터를 전송하지만, SFTP는 SSH 기반으로 모든 데이터를 암호화하여 전송합니다. 또한 FTP는 명령 채널(21번)과 데이터 채널(20번) 두 개의 포트를 사용하지만, SFTP는 22번 포트 하나만 사용합니다.'
- q: 'Active 모드와 Passive 모드의 차이는 무엇인가요?'
a: 'Active 모드는 서버가 클라이언트에 역으로 접속해 데이터를 전송합니다. 클라이언트 방화벽에 막힐 수 있어 Passive 모드가 표준으로 사용됩니다. Passive 모드에서는 서버가 포트를 열면 클라이언트가 직접 접속하므로 방화벽 문제가 발생하지 않습니다.'
---2️⃣ JSON-LD 구조화 데이터 추가
JSON for Linking Data: 웹페이지의 내용을 기계가 이해할 수 있는 형태로 설명하는 데이터
사람이 읽는 것 → 본문 텍스트 기계가 읽는 것 → JSON-LD
// app/layout.tsx
// WebSite + Person 스키마
const websiteJsonLd = {
'@context': 'https://schema.org',
'@graph': [
{
'@type': 'WebSite', // 이 사이트 전체를 설명
'@id': `${SITE_URL}/#website`, // 사이트의 고유 ID (URI 형태)
url: SITE_URL,
name: 'JoyLog',
description: '프론트엔드 개발자 Joy의 기술 블로그 및 포트폴리오입니다.',
inLanguage: 'ko-KR',
// AI가 "이 블로그는 Joy라는 사람이 운영한다"는 것을 이해하게 함
publisher: { '@id': `${SITE_URL}/#person` }, // 아래 Person을 참조
},
{
'@type': 'Person',
'@id': `${SITE_URL}/#person`,
name: 'Joy',
url: SITE_URL,
jobTitle: '프론트엔드 개발자',
// AI가 "프론트엔드 개발 관련 질문이 들어오면 이 사람 블로그를 참고하자"고 판단하는 근거가 됨
knowsAbout: ['프론트엔드 개발', 'React', 'Next.js', 'TypeScript', 'JavaScript'],
},
],
};
// ...
// head 태그, <script type="application/ld+json"> 안에 삽입
// -> 실행도 파싱도 안 함, 크롤러가 와서 읽어감
<head>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteJsonLd) }}
/>
</head>
/*
<script type="application/ld+json">
{
"@type": "BlogPosting",
"headline": "제목",
"datePublished": "2025-04-19"
}
</script>
*/// app/posts/[slug]/page.tsx
// TechArticle + FAQPage 스키마
const articleNode = {
'@type': 'TechArticle',
'@id': `${SITE_URL}/posts/${slug}#article`,
headline: post.metadata.title,
description: post.metadata.description,
datePublished: post.metadata.date,
dateModified: post.metadata.date,
author: {
'@type': 'Person',
name: 'Joy',
url: SITE_URL,
},
publisher: {
'@type': 'Person',
name: 'Joy',
url: SITE_URL,
},
url: `${SITE_URL}/posts/${slug}`,
mainEntityOfPage: `${SITE_URL}/posts/${slug}`,
keywords: post.metadata.tags.join(', '),
inLanguage: 'ko-KR',
timeRequired: `PT${post.metadata.readingTime}M`,
};
const graph = post.metadata.faq?.length
? [
articleNode,
{
'@type': 'FAQPage',
'@id': `${SITE_URL}/posts/${slug}#faq`,
mainEntity: post.metadata.faq.map((item: FaqItem) => ({
'@type': 'Question',
name: item.q,
acceptedAnswer: { '@type': 'Answer', text: item.a },
})),
},
]
: [articleNode];
const articleJsonLd = {
'@context': 'https://schema.org',
'@graph': graph,
};
// ...
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>3️⃣ llms.txt / llms-full.txt 추가
llms.txt → 목록 (가볍게 전체 파악) llms-full.txt → 전체 글 (깊게 읽을 때)
// app/llms.txt
// AI용 사이트맵
// AI는 사이트 파악할때 sitemap.xml보다 llms.txt를 훨씬 잘 이해함
// XML이 아닌 자연어 + 마크다운이기 때문
import { getAllPosts } from '@libs/posts';
import { SITE_URL } from '@constants/metadata';
// 빌드 타임에 정적으로 생성
export const dynamic = 'force-static';
export async function GET() {
const posts = await getAllPosts();
// - [포스트 제목](URL): 한 줄 설명
// 형태로 포스트 목록 제공
const postList = posts
.map((post) => `- [${post.metadata.title}](${SITE_URL}/posts/${post.slug}): ${post.metadata.description}`)
.join('\n');
const content = `# JoyLog
> 프론트엔드 개발자 Joy의 기술 블로그 및 포트폴리오
## 블로그 소개
JoyLog는 프론트엔드 개발자 Joy가 학습하고 경험한 내용을 정리하는 기술 블로그입니다.
React, Next.js, TypeScript 등 웹 프론트엔드 기술을 주로 다룹니다.
## 페이지
- [홈](${SITE_URL}): 전체 포스트 목록
- [포트폴리오](${SITE_URL}/portfolio): 프로젝트 소개
## 포스트 목록
${postList}
## 추가 정보
- llms-full.txt: ${SITE_URL}/llms-full.txt
- sitemap: ${SITE_URL}/sitemap.xml
`;
// 접근시 목록 제공
return new Response(content, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
// app/llms-full.txt
// 모든 포스트의 전체 본문을 하나의 파일로 제공함
// AI가 검색 없이 직접 읽을 수 있는 형태임
import fs from 'fs/promises';
import path from 'path';
import matter from 'gray-matter';
import { getAllPosts } from '@libs/posts';
import { SITE_URL } from '@constants/metadata';
export const dynamic = 'force-static';
const postsDirectory = path.join(process.cwd(), 'src/content/posts');
export async function GET() {
const posts = await getAllPosts();
const sections: string[] = [
`# JoyLog - 전체 콘텐츠`,
``,
`> 프론트엔드 개발자 Joy의 기술 블로그`,
`> URL: ${SITE_URL}`,
``,
`---`,
``,
];
// 모든 포스트를 하나의 string 배열로 합침
for (const post of posts) {
const fullPath = path.join(postsDirectory, `${post.slug}.mdx`);
const fileContents = await fs.readFile(fullPath, 'utf8');
const { content } = matter(fileContents);
sections.push(`## ${post.metadata.title}`);
sections.push(`URL: ${SITE_URL}/posts/${post.slug}`);
sections.push(`날짜: ${post.metadata.date}`);
sections.push(`태그: ${post.metadata.tags.join(', ')}`);
if (post.metadata.description) {
sections.push(`요약: ${post.metadata.description}`);
}
sections.push(``);
sections.push(content.trim());
sections.push(``);
sections.push(`---`);
sections.push(``);
}
// 접근시 전체 글 제공
return new Response(sections.join('\n'), {
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
});
}
결론
GEO는 아직 명확한 해답이 있는 영역은 아님. 생성형 AI 기술이 빠르게 발전하면서, 정답도 하나가 아니고 접근 방식도 계속 바뀌고 있음. 당장 우리 사이트에서 적용할 수 있는 작은 시도부터 차근차근 해봐야함