diff --git a/solution/frontend/index.html b/solution/frontend/index.html index f5ab125..6c3f519 100644 --- a/solution/frontend/index.html +++ b/solution/frontend/index.html @@ -3,8 +3,25 @@ - - + + + + + + + + + + + + diff --git a/solution/site/scripts/prerender.ts b/solution/site/scripts/prerender.ts index a42c424..8c6df86 100644 --- a/solution/site/scripts/prerender.ts +++ b/solution/site/scripts/prerender.ts @@ -26,6 +26,7 @@ import { renderLlmsTxt, renderRootRobotsTxt, renderSiteUrlset, + type SiteEntry, verifyGeo, verifyJsonLd, } from '@/seo'; @@ -509,21 +510,29 @@ function writeRootMachineFiles(outRoot: string, origin: string) { const sitesDir = join(outRoot, SITE_DIR); if (!existsSync(sitesDir)) return; - const entries = readdirSync(sitesDir, {withFileTypes: true}) + const entries: SiteEntry[] = readdirSync(sitesDir, {withFileTypes: true}) .filter((entry) => entry.isDirectory()) .map((entry) => ({slug: entry.name, file: join(sitesDir, entry.name, 'index.html')})) // index.html 이 없으면 발행이 끝나지 않은(또는 실패한) 디렉토리다. 사이트맵에 넣지 않는다. .filter((entry) => existsSync(entry.file)) .map((entry) => ({ - loc: joinUrl(origin, SITE_DIR, entry.slug) + '/', + // ★ 끝 슬래시를 붙이지 않는다. 페이지의 canonical 은 `/s/` 다(shared/lib/slug.ts + // publishUrl). 사이트맵이 `/s//` 로 어긋나 있던 동안 서치콘솔은 제출한 URL 을 + // 전부 "대체 페이지(적절한 표준 태그가 있음)" 로 분류했다 — 색인은 되는데 제출분은 + // 0건으로 보이는, 눈으로 원인을 못 찾는 종류다. + loc: joinUrl(origin, SITE_DIR, entry.slug), // 페이지는 그 사이트를 구울 때마다 다시 쓰인다 — 파일 mtime 이 곧 마지막 발행 시각이다. lastmod: statSync(entry.file).mtime.toISOString(), })) .sort((a, b) => a.loc.localeCompare(b.loc)); + // ★ 오리진 루트(랜딩)도 담는다. 이 호스트에 들어오는 링크가 없어 크롤러의 유일한 문이 + // 사이트맵인데, 정작 그 문을 여는 첫 페이지가 빠져 있었다. + entries.unshift({loc: origin + '/'}); + writeFileSync(join(outRoot, 'robots.txt'), renderRootRobotsTxt(origin), 'utf-8'); writeFileSync(join(outRoot, 'sitemap.xml'), renderSiteUrlset(entries), 'utf-8'); - console.log(` ✓ 루트 robots.txt · sitemap.xml (사이트 ${entries.length}개)`); + console.log(` ✓ 루트 robots.txt · sitemap.xml (랜딩 + 사이트 ${entries.length - 1}개)`); writeIndexNowKey(outRoot); }