질문에 답하는 콘텐츠와 출처 작성하기
답에 필요한 대상·조건·예외·시점을 본문에 담고 출처와 연결한다. 작성자·발행 주체·문서 대상을 구분하고 재게시와 개편 뒤에도 날짜와 근거를 유지한다.
콘텐츠의 답·조건·근거를 함께 고친다
본문의 첫 문단에 질문의 답을 적고, 이어서 적용 대상·조건·절차·예외·기준 시점·근거를 설명한다. 운영 시간 안내라면 시간·휴일·적용 시작일을 함께 제공한다.
가상 도서관 안내의 수정 전: “누구나 편리하게 이용할 수 있는 공간입니다.” 수정 후: “일요일에는 오전 10시부터 오후 5시까지 운영합니다. 매월 첫째 일요일과 공휴일은 쉽니다. 이 시간표는 2026년 10월부터 적용합니다.” 바로 뒤에 해당 운영 시간 안내의 근거 링크를 붙인다. 이용료·대상·신청 방법도 같은 방식으로 빠진 조건을 채운다.
원고 데이터에는 게시일, 실질 수정일, 사실의 발생일, 적용 시작일을 구분해 저장한다. 날짜 필드 하나를 모두에 재사용하면 과거 사실이 새 사건처럼 보일 수 있다. 변경된 안내에는 무엇이 언제 바뀌었는지 쓰고 이전 공지에 최신 안내 링크를 연결한다. 작성자·기관·근거 문서 이름은 실제 확인 가능한 값으로 넣는다.
같은 판단에 필요한 내용을 한 페이지에서 설명한다. 제목과 소제목에는 실제 주제를 쓰고, 조건 비교는 표로, 절차는 순서대로 제시한다. FAQ는 독자의 실제 질문이 있을 때 쓴다. 각 답변에는 대상·조건·예외와 근거를 포함하고, 배포 후 실제 질문에서 해당 답이 사용되는지 관측한다.
“누구나 편리하게 이용할 수 있다”는 문장에는 일요일 방문 여부를 결정할 사실이 없다. 수정 문장에는 시간·예외·적용일이 있어 이용자가 날짜와 대조할 수 있다. 검색·답변 도구가 이 구절을 읽을 때도 같은 조건을 얻는다. 답을 짧게 쓰더라도 예외를 함께 두는 이유는 문장 일부만 가져왔을 때 의미가 달라지는 일을 줄이기 위해서다.
질문의 종류에 맞춰 답과 근거를 작성한다
질문에 따라 설명의 순서가 달라진다. 정의형은 뜻과 구분, 방법형은 순서와 실패, 비교형은 같은 기준, 조건형은 대상과 예외, 시점형은 적용 기간을 설명한다. 다음 표는 도서관 안내를 이 다섯 유형으로 고친 예다.
원자료·경험·작성 주체를 확인할 수 있게 쓴다
entity는 구분 가능한 사람·기관·장소·문서 같은 대상을 뜻한다. 같은 이름의 도서관이 있다면 지역·공식 주소·운영 주체로 식별한다. 작성자 author, 발행 주체 publisher, 설명 대상 about, 같은 대상의 식별 링크 sameAs, 주장 근거 citation을 실제 관계에 맞춰 사용한다.
E-E-A-T는 Experience·Expertise·Authoritativeness·Trustworthiness, 즉 경험·전문성·권위·신뢰를 가리킨다. Google은 이를 직접적인 단일 순위 요소로 설명하지 않는다. 경험의 범위·주장 근거·작성 주체를 적고 독자가 확인할 자료를 연결한다. 콘텐츠 품질 안내
경험을 설명할 때는 수행한 일과 그때의 조건을 함께 쓴다. 예를 들어 이미지 압축 결과에는 원본·변환본의 용량과 같은 기기에서 측정한 표시 시간을 붙인다. 독자는 파일이 작아진 정도와 화면이 빨라진 정도를 각각 읽을 수 있다. 논문을 인용할 때도 대상·실험 조건·측정 지표를 붙인다.
작성자·대상·발행 주체와 출처를 구분한다
소개 대상과 글쓴이가 다르면 author와 about으로 나눈다. 글을 발행한 기관은 publisher에 넣는다. 예를 들어 도서관 직원이 작가 초청 행사를 소개했다면 author는 직원, about은 행사, publisher는 도서관이다.
사이트, 발행 기관, 소개 대상, 문서에는 일관된 식별자를 사용한다. 문서의 @id에 매번 새 게시 revision 번호를 붙이면 수정할 때마다 다른 문서처럼 표현될 수 있다. 예를 들어 문서 ID를 기준으로 https://example.com/#document-123을 유지하고, 날짜와 본문만 현재 게시본으로 갱신한다. 같은 식별자를 유지하면 여러 구조화 데이터에서 동일한 문서를 참조할 수 있다.
현재 이름·역할·운영 주체·주소를 본문에서 설명하고 해당하는 구조화 데이터에도 같은 값을 사용한다. 과거 역할이나 이전 운영 방식은 기간을 붙여 구분한다. 동명이인이나 비슷한 서비스 이름이 있다면 독자가 식별할 수 있는 지역·업무·공식 소개 링크를 제공한다.
sameAs는 같은 대상을 식별하는 주소를 연결한다. 기관의 공식 소개, Wikipedia 문서, Wikidata 항목을 사용할 수 있다. 기관의 휴관 발표 기사는 해당 주장의 citation으로 연결한다. 대상의 신원과 개별 주장의 출처를 각각 표현하는 속성이다. Schema.org sameAs
근거 문서는 주장 옆에 연결하고 자료의 날짜·절·항목을 적는다. 이관한 글이면 원자료를 isBasedOn 관계로, 운영 시간의 근거면 citation 관계로 표현할 수 있다. 독자가 링크를 열어 같은 기간의 같은 주장을 읽을 수 있는지 확인한다.
운영 주체와 문의·정정 경로는 독자가 실제로 찾을 수 있는 위치에 둔다. 글의 작성자, 자료를 발행한 기관, 사이트 운영자를 혼동하지 않게 이름과 역할을 표시한다. 개인정보 안내가 필요한 기능은 실제 수집·이용 방식에 맞춰 연결한다. 문의 링크는 실제 연락 화면으로 연결하고, 정정 요청을 받은 뒤 본문을 갱신할 담당자와 처리 경로를 정한다.
같은 이름의 도서관 두 곳이 서로 다른 시간에 문을 연다면 이름만 있는 인용으로는 어느 곳의 시간인지 확인하기 어렵다. 지역·주소·운영 주체와 공식 안내 링크를 함께 적으면 대상을 구분할 근거가 생긴다. source나 citation 링크에서는 실제로 같은 지점·기간의 운영 조건이 나오는지 읽는다. 작성자 이름과 링크의 개수를 늘리는 것보다 이 관계가 정확해야 자료를 검증할 수 있다.
정정 요청을 받을 때는 어떤 문장이 어떤 근거와 어긋나는지 확인한다. “첫째 일요일도 운영한다”는 오류를 찾았다면 원문에 휴관 예외를 고치고 변경 이유와 적용일을 남긴다. 관련 글·공유 설명·자체 답변 자료도 같은 게시본에서 갱신한다. 작성 주체와 문의 경로는 독자가 이 오류를 전달하고 수정된 사실을 다시 확인하는 데 쓰인다.
날짜·관련 글·공유 이미지를 재게시에도 보존한다
외부 글을 가져오는 수집 시각은 원문 게시일이나 사건 발생일과 다르다. 수집기가 새로 실행됐다는 이유로 dateModified와 사이트맵 lastmod를 갱신하지 않는다. 목록 전체의 수정일을 개별 글의 최대 수정일로 계산할 때도 주의한다. 글 철회·순서 변경·소개 수정까지 반영하지 못한다면 목록의 실제 수정 기록을 만들거나 lastmod를 생략한다.
사건 날짜를 보충할 때는 확인한 날짜와 그 근거 URL을 함께 저장한다. 날짜만 알려진 사실에 임의의 시각을 붙이지 않는다. 시간대가 있는 게시 시각은 명시한 시간대로 변환하되, YYYY-MM-DD의 사건 날짜를 시간대 변환으로 전날이나 다음 날로 바꾸지 않는다. 본문의 날짜 라벨도 사건·보도·게시를 구분한다.
과거 글에 새 사실을 보충할 때는 원래 내용 옆에 변경 시점과 후속 안내를 붙인다. 예를 들어 2025년 신청 안내에는 당시 자격을 보존하고, “2026년 신청 자격” 링크를 추가한다. 이 관계를 revision에 저장하면 재수집·이관 뒤에도 보충 설명을 유지할 수 있다.
관련 글은 주소 문자열을 오래 저장하는 방식보다 콘텐츠 ID로 연결하고 표시 시점의 활성 게시본에서 제목·URL을 조회할 수 있다. 글이 철회되면 연결도 함께 제외한다. 링크를 고치기 위해 철회한 과거 글을 임의로 다시 공개하지 않는다.
대표 이미지가 있는 글은 해당 글의 공개 이미지와 설명을 공유 카드에 연결한다. 모든 글에 같은 기본 이미지를 써야 하는지 확인하고, 이미지가 없을 때의 기본값을 둔다. 수집형 게시물은 원문 URL과 원문 게시일을 보존하고 원문에 맞는 SocialMediaPosting 등 유형을 검토한다. 사진만 있는데 VideoObject를 추가하지 않는다.
화면의 게시일은 읽을 수 있는 문장과 기계가 해석할 날짜를 함께 제공한다. 예를 들어 저장된 시각이 2026-10-23T09:00:00+09:00이고 한국 시간으로 표시한다면 아래처럼 time의 dateTime과 가시 날짜를 맞춘다. 사건 날짜·게시일·수정일은 서로 다른 라벨로 표시한다.
<p>게시일 <time dateTime="2026-10-23T09:00:00+09:00">2026년 10월 23일</time></p>
Article·NewsArticle에 대표 이미지가 있다면 image 속성에도 해당 글의 공개 이미지 URL을 연결한다. URL만 존재하는지 보지 말고 비로그인 요청과 크롤러 접근, 이미지의 실제 내용을 확인한다. Article 속성 안내를 기준으로 author·datePublished·dateModified·image를 본문과 대조한다.
현재 안내와 과거 시간표의 관계를 글에 남긴다
가상 도서관의 현재 안내는 “일요일 10시부터 17시까지, 첫째 일요일과 공휴일 휴관, 2026년 10월부터 적용”으로 작성한다. 이전 안내에는 당시 적용 기간과 현재 안내 링크를 붙인다. 현재 페이지의 작성일을 과거 문서에 덮어쓰지 않는다.
질문이 “이번 일요일 이용할 수 있는가”라면 날짜가 첫째 일요일이나 공휴일인지도 판단해야 한다. 본문에 시간만 있으면 필요한 조건이 빠진다. 운영 시간 안내·휴관 공지·적용 기간을 함께 연결하고, 외부 답변이 그 예외까지 설명하는지 관측한다.
출처 링크는 실제 운영 시간 안내의 해당 부분으로 연결한다. 운영 시간 안내가 바뀌면 현재 본문과 수정일을 갱신하고 과거 문서는 기록으로 유지한다. 두 페이지가 같은 사실의 서로 다른 유효 기간을 다룬다는 관계를 독자와 수집기가 읽을 수 있게 한다.
조작을 위한 문장·링크·날짜를 피한다
키워드 스터핑(keyword stuffing)은 순위를 노리고 같은 단어나 지역명을 부자연스럽게 반복하는 행위다. 클로킹(cloaking)은 검색 순위를 조작하거나 이용자를 속이려고 사람과 검색엔진에 다른 내용을 보여주는 행위다. 숨긴 키워드, 관련 없는 리다이렉트, 순위 조작용 링크 거래, 독자 가치가 없는 대량 문서는 Google의 스팸 정책과 연결된다. 검색 스팸 정책
AI가 만든 문장도 원자료와 대조해 날짜·대상·예외를 고친다. 같은 문장을 지역명만 바꿔 대량으로 만들면 각 지역에서 필요한 주소·일정·신청 조건이 빠지기 쉽다. 독자가 방문이나 신청을 결정할 수 있도록 해당 지역의 정보를 채운다.
Schema.org의 FAQPage 유형과 검색엔진의 표시 기능은 별개다. Google은 2026년 5월 7일부터 FAQ 리치 결과를 표시하지 않으며 관련 문서를 제거했다고 공지했다. FAQ는 독자의 실제 질문에 답할 때 작성하고 과거의 표시 자격 설명을 현재 지원으로 옮기지 않는다. 화면에 없는 FAQ·리뷰·상품·영상을 마크업하지 않는다. llms.txt도 이를 사용하는 소비자를 확인할 선택 항목이며 Google 검색에서는 필요하지 않다. Google 변경 기록 · 생성형 검색 지침
브리프를 실제 안내문으로 작성한다
기획편의 브리프를 안내문으로 옮기면 다음과 같다. 운영계획 A의 대상·일정, 접수안내 B의 기한·정원, 취소안내 C의 취소 방법을 신청 순서에 맞춰 배치했다.
2026년 11월 가족 독서 프로그램 신청 안내
초등학교 1~3학년 자녀와 보호자가 함께 책을 읽는 프로그램입니다. 11월 8일과 15일 일요일 오후 2~3시에 어린이실에서 진행합니다. 자녀 1명과 보호자 1명이 두 회차에 함께 참여합니다. 참가비는 없으며 읽고 싶은 책 한 권을 가져오세요. 대상·일정·동반 조건의 근거는 운영계획 A입니다.
접수는 10월 26일 오전 9시부터 11월 5일 오후 6시까지입니다. 모든 시각은 한국 표준시입니다. 선착순 10가족을 모집하며 정원이 차면 일찍 마감합니다. 프로그램 목록에서 이 안내의 “신청하기”를 열고 참가자 정보를 제출하세요. 신청 번호가 표시된 접수 확인 화면까지 확인해야 접수가 완료됩니다. 접수 기간·정원·완료 기준의 근거는 접수안내 B입니다.
참여하기 어려워지면 11월 6일 오후 6시까지 신청 확인 화면에서 취소할 수 있습니다. 취소 화면을 사용할 수 없거나 동반 조건을 확인하려면 프로그램 담당자 문의 경로를 이용하세요. 취소 기한과 문의 담당의 근거는 취소안내 C입니다.
게시일은 2026년 10월 24일입니다. 이 안내는 2026년 11월 프로그램에 적용합니다. 모집이 끝나면 접수 종료를 표시하고 다음 모집 안내가 있을 때 연결합니다.
첫 문단에서 자녀의 학년과 동반 가능 여부를 판단한다. 다음 문단에서 접수 기한과 정원을 읽고 신청한다. 신청 번호는 접수 완료를 알려 준다. 모집이 끝난 뒤에도 적용 연도와 일정을 남겨 두면 이전 프로그램을 찾는 사람이 당시 조건을 확인할 수 있다.
이 안내를 읽는 답변 도구는 “초등학교 2학년 자녀와 둘째 일요일에 갈 수 있는가”라는 질문에서 학년·요일·동반 조건을 대조할 수 있다. 두 회차 모두 참여해야 한다는 문장이 빠지면 한 회차만 가능한 가족에게 잘못 소개할 수 있다. 첫 문단에 세 조건을 함께 둔 이유다. 게시 뒤 같은 질문의 답과 인용 구절에서 이 조건이 유지되는지 확인한다.
영상의 구간·원문·요약을 연결한다
영상 제목만 인용하면 그 영상의 어느 부분이 주장을 뒷받침하는지 확인하기 어렵다. 발언 영상은 발언자·기록일·구간 시작과 끝·전사 문장·원본 주소를 저장한다. 요약은 별도 필드로 두고 조건이 빠졌는지 원문과 비교한다. 텍스트를 읽는 검색·답변 경로에는 이 문장이 자료가 되고, 독자는 구간 링크에서 직접 확인할 수 있다.
다음 실습은 두 장의 안내문을 4초씩 보여주는 무음 영상이다. 첫 장에는 일요일 10~17시, 둘째 장에는 첫째 일요일과 공휴일 휴관을 적는다. 본문의 “영상에 표시한 문장”은 화면의 원문이며 “내용 요약”은 두 구간을 합친 한국어 설명이다. 발언을 전사할 때도 원문과 요약을 이렇게 분리한다.
이미지 실습 폴더에 아래 make-video-demo.mjs와 video.html을 각각 저장한다. ffmpeg가 PATH에 있어야 한다. node make-video-demo.mjs를 실행하면 talk.mp4가 만들어진다. 이미지 서버를 켜고 http://127.0.0.1:3031/video.html?t=4를 열면 둘째 구간으로 이동한다.
import sharp from 'sharp';
import { mkdtemp, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawnSync } from 'node:child_process';
// Node.js 22, sharp, PATH에 등록된 ffmpeg 필요. 음성 없는 안내 영상 8초를 만든다.
const dir = await mkdtemp(join(tmpdir(), 'notes-video-'));
try {
const lines = ['Sunday: 10:00–17:00.', 'Closed: first Sunday and public holidays.'];
for (const [i, line] of lines.entries()) {
await sharp(Buffer.from(`<svg width="1280" height="720">
<rect width="1280" height="720" fill="#172d48"/>
<text x="640" y="360" text-anchor="middle" fill="white" font-family="sans-serif" font-size="48">${line}</text>
</svg>`)).png().toFile(join(dir, `${i}.png`));
}
const result = spawnSync('ffmpeg', ['-hide_banner', '-loglevel', 'error', '-y',
'-loop', '1', '-t', '4', '-i', join(dir, '0.png'),
'-loop', '1', '-t', '4', '-i', join(dir, '1.png'),
'-filter_complex', '[0:v][1:v]concat=n=2:v=1:a=0[v]', '-map', '[v]',
'-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-movflags', '+faststart',
fileURLToPath(new URL('./talk.mp4', import.meta.url))], { stdio: 'inherit' });
if (result.error || result.status !== 0) throw result.error ?? new Error('ffmpeg failed');
} finally { await rm(dir, { recursive: true, force: true }); }
<!doctype html><html lang="ko"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>일요일 운영 안내 영상</title>
<style>body{max-width:50rem;margin:24px auto;padding:0 16px;font-family:sans-serif;line-height:1.7}video{width:100%}</style>
</head><body><h1>일요일 운영 안내 영상</h1>
<p>가상 도서관 편집팀 · 제작 <time datetime="2026-10-01">2026년 10월 1일</time>
· 2026년 10월부터 적용</p>
<video id="video" controls preload="metadata" src="/talk.mp4"></video>
<h2>영상에 표시한 문장</h2>
<p><a href="?t=0" data-start="0">0~4초</a> Sunday: 10:00–17:00.</p>
<p><a href="?t=4" data-start="4">4~8초</a> Closed: first Sunday and public holidays.</p>
<h2>내용 요약</h2>
<p>일요일은 10~17시에 엽니다. 첫째 일요일과 공휴일은 쉽니다.</p>
<p>두 장의 안내문을 영상으로 만든 실습입니다. 소리는 없습니다.</p>
<p><a id="share" href="?t=0">선택한 구간 주소</a></p>
<script>
const video = document.querySelector('#video');
const share = document.querySelector('#share');
function seek(value) {
const seconds = Number(value);
if (!Number.isFinite(seconds) || seconds < 0 || seconds >= video.duration) return;
video.currentTime = seconds;
const url = new URL(location.href);
url.searchParams.set('t', String(seconds));
history.replaceState(null, '', url);
share.href = url.href;
}
video.addEventListener('loadedmetadata', () => seek(new URL(location.href).searchParams.get('t') ?? 0));
document.querySelectorAll('[data-start]').forEach(link => link.addEventListener('click', event => {
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
if (video.readyState < 1) return; // metadata가 없으면 일반 링크로 이동한다.
event.preventDefault(); seek(link.dataset.start);
}));
</script></body></html>
make-video-demo는 sharp로 안내문 두 장을 만들고 ffmpeg의 concat 필터로 연결한다. spawnSync에는 인자 배열을 전달하므로 글자를 셸 명령으로 실행하지 않는다. video.html의 loadedmetadata는 영상 길이를 읽을 수 있는 시점이다. seek는 유효한 초인지 확인하고 currentTime을 바꾼다. 구간 링크를 누르면 재생 위치와 공유 주소의 t 값이 함께 바뀐다. 주소를 새 창에서 열어도 loadedmetadata 뒤 같은 위치로 이동한다.
“일요일마다 언제나 이용할 수 있다”라는 요약은 첫 화면의 시간만 읽고 둘째 화면의 예외를 빠뜨린다. 0초 링크만 제시하면 이 잘못을 찾기 어렵다. 시간 주장에는 첫 구간을, 휴관 예외에는 둘째 구간을 연결하고 전체 답변은 두 조건을 함께 사용한다. 링크가 실제로 이동하는지와 그 구간이 문장을 지지하는지는 서로 다른 검사다.
video.html은 영상과 설명을 보는 페이지 URL이고 talk.mp4는 미디어 파일 URL이다. 외부 플레이어를 iframe으로 넣을 때의 embed URL도 따로 있다. VideoObject의 contentUrl에는 미디어 파일을, embedUrl에는 플레이어 주소를 넣는다. 영상 검색을 목표로 한다면 영상이 주요 콘텐츠인 시청 페이지, 안정된 썸네일과 영상 접근, 관련 메타데이터를 함께 준비한다. Google 영상 안내
초 단위 이동에서는 브라우저가 미디어의 일부 바이트를 요청할 수 있다. 실습 서버는 Range를 읽어 해당 구간을 206과 Content-Range로 응답하고 파일 밖 요청은 416으로 돌려준다. 전체 파일만 200으로 보내던 서버에서는 currentTime을 지정해도 재생 위치가 돌아갈 수 있다. 영상 이동 코드와 파일 제공 경로를 함께 확인해야 한다. HTTP Range 요청