[fix] site: sitemap.xml 오리진이 임의의 옛 사이트로 고정되는 버그 수정

킹서버에서 방금 발행한 사이트도 sitemap.xml 에 죽은 옛 호스트(w4ai.o2o.kr)로 나가고
있었다 — Google Search Console 스윕에 URL_NOT_IN_SITEMAP 으로 잡혀서 발견했다.

원인: solution-worker 는 기동할 때마다(=배포할 때마다) `--seed-assets` 로 prerender 를
한 번 돌리는데, 이 경로는 payload 를 안 읽어서 오리진을 얻을 곳이 없다 — 그래서
findBakedOrigin() 이 out/s/ 를 훑어 **가장 먼저 발견한** 사이트의 baked canonical 을 그대로
쓴다. readdirSync 순서는 보장이 없고, SITE_PUBLIC_HOST 를 web4ai.o2osolution.ai 로 옮긴 뒤
한 번도 재발행 안 한 옛 사이트(목업 포함) 하나가 그 자리에 걸리면, 통합 sitemap.xml
전체가 죽은 옛 호스트로 통째로 구워진다 — 오늘 새로 발행한 사이트(sono, 자기 페이지의
canonical 은 정상)까지 사이트맵에서는 옛 호스트로 나갔다.

고침: 처음 찾은 것 대신 **가장 최근에 구워진(mtime 최신)** 사이트의 오리진을 쓴다 —
최근에 발행된 사이트일수록 지금 SITE_PUBLIC_HOST 를 반영했을 확률이 높다.

검증: tsc --noEmit 통과. 킹서버 solution-worker 재시작 후 sitemap.xml 재확인 예정.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Mina Choi 2026-09-18 11:16:30 +09:00
parent 114a6b47d3
commit 13cc8cc830

View File

@ -1056,21 +1056,31 @@ function writePreviewShell(outRoot: string, assets: {script: string; css: string
/**
* 이미 구워져 있는 사이트에서 오리진을 알아낸다. 한 장도 없으면 빈 문자열.
*
* ★ payload 를 읽지 않는 경로(`--refresh-assets` 기동)에서 루트 색인 파일을 다시 쓰려면
* ★ payload 를 읽지 않는 경로(`--seed-assets` 기동)에서 루트 색인 파일을 다시 쓰려면
* 오리진이 필요한데, 그 값은 payload 에만 있다. 디스크에 있는 페이지가 자기 canonical 로
* 선언해 둔 것을 쓴다(readBakedOrigin 주석).
* ★ **가장 최근에 구워진 페이지**의 오리진을 쓴다 — 처음 만난 것을 쓰면(readdirSync 순서는
* 보장이 없다) 호스트를 옮긴 뒤 한 번도 재발행 안 한 옛 사이트(목업 포함) 하나가 걸릴 때
* 전체 sitemap.xml 이 죽은 옛 호스트로 통째로 구워진다. 실측(2026-09-18): SITE_PUBLIC_HOST
* 를 web4ai.o2osolution.ai 로 옮긴 지 오래인데, solution-worker 가 재시작(배포)될 때마다
* 이 함수가 옛 호스트(w4ai.o2o.kr)로 구운 사이트 하나를 집어 사이트맵 전체가 죽은 주소로
* 덮였다 — 오늘 새로 발행한 사이트까지 사이트맵에서 옛 호스트로 나갔다. 갓 구운 페이지일수록
* 지금 SITE_PUBLIC_HOST 를 반영했을 확률이 높다.
*/
function findBakedOrigin(outRoot: string): string {
const sitesDir = join(outRoot, SITE_DIR);
if (!existsSync(sitesDir)) return '';
let newest: {origin: string; mtimeMs: number} | null = null;
for (const entry of readdirSync(sitesDir, {withFileTypes: true})) {
if (!entry.isDirectory()) continue;
const file = join(sitesDir, entry.name, 'index.html');
if (!existsSync(file)) continue;
const origin = readBakedOrigin(readFileSync(file, 'utf-8'));
if (origin) return origin;
if (!origin) continue;
const mtimeMs = statSync(file).mtimeMs;
if (!newest || mtimeMs > newest.mtimeMs) newest = {origin, mtimeMs};
}
return '';
return newest?.origin ?? '';
}
function writeRootMachineFiles(outRoot: string, origin: string) {