relatedSlugs가 ‘있는데 안 보일’ 때…스키마는 통과하고 UI만 비는 구멍
Astro 컬렉션은 relatedSlugs 길이만 검사한다. RelatedPosts는 없는 슬러그를 조용히 걸러 빌드가 성공해도 관련 글이 사라진다. 실측으로 확인한 운영 메모.

증상은 빌드 로그에 없었다. npm run build는 초록이고, 카테고리 페이지에도 새 글이 올라왔다. 그런데 본문 하단 ‘관련 글’이 비어 있거나, 기대보다 한 칸이 줄었다. 프론트매터에는 relatedSlugs가 분명히 있었다. 배포는 성공했는데 연결만 사라진 상태였다.
사실: 스키마가 보는 것과 컴포넌트가 하는 일
src/content.config.ts의 Zod 스키마는 relatedSlugs를 z.array(z.string()).min(2).max(3)로만 둔다. 길이 2~3이면 통과다. 대상 슬러그가 실제로 존재하는지는 검사하지 않는다. 오타·삭제된 글·카테고리 이동으로 사라진 id를 넣어도 Astro 빌드는 멈추지 않는다.
렌더 쪽은 더 관대하다. RelatedPosts.astro는 컬렉션 전체를 읽은 뒤 slugs.map(...).filter(Boolean)으로 없는 항목을 버린다. 매칭이 0개면 관련 글 <aside> 자체를 그리지 않는다. 즉 검증 실패가 아니라 표시 생략이다. 운영자 눈에는 “관련 글 기능이 꺼진 것 같다”로만 보인다.
이 레포에서 전 포스트의 relatedSlugs를 파싱해 존재 여부를 대조해 보니(열람: 2026-08-01), 깨진 참조는 0건이었다. 지금 당장 장애는 아니다. 다만 구조상 내일 오타 하나면 같은 침묵이 재현된다. 스키마가 막아 주지 않기 때문이다.
이유: ‘배열 길이’와 ‘그래프 무결성’을 같은 층에 두지 않았다
콘텐츠 컬렉션 스키마는 YAML 형태를 빠르게 맞추는 데 강하다. 반면 관련 글은 문서 간 외래키에 가깝다. 길이 검사만 두면 CI는 초록을 유지하고, UI는 부분 결손을 흡수한다. 정적 사이트에서는 그 조합이 특히 위험하다. 런타임 404 모니터링이 관련 글 칸까지 잘 안 보기 때문이다.
시니어 IT 실무자 시각으로 정리하면, 이 구멍은 “버그”라기보다 의도된 관대함의 부작용이다. 관련 글이 하나라도 있으면 섹션을 살리고, 없으면 숨기는 편이 UX상 깔끔하다. 문제는 그 관대함이 작성자 피드백 루프를 끊는다는 점이다. 배포 직후 HTML만 훑으면 “문제 없음”으로 끝난다.
평가: 어디에 쓰고, 어디에 안 쓰는지
쓰는 읽기: 멀티 카테고리 동시 발행·자동화 글쓰기처럼 relatedSlugs를 자주 손대는 워크플로. 안 쓰는 읽기: “스키마만 통과하면 SEO·내부링크가 자동으로 건강하다”는 가정. Zod min(2)는 작성 형식이지 링크 그래프 검증이 아니다.
실무 체크는 단순하다. 빌드 전에 슬러그 집합을 만들고, 각 글의 relatedSlugs가 그 집합의 부분집합인지 확인한다. 실패하면 배포를 멈춘다. 컴포넌트의 filter(Boolean)은 남겨 두되, 침묵을 CI 단계에서 소리 나게 바꾸는 쪽이 안전하다.
한 가지 더. 관련 글은 같은 카테고리를 우선하라는 글쓰기 기준과도 맞물린다. 존재하지 않는 슬러그를 넣으면 카테고리 교차 실수보다 먼저 빈 칸이 된다. 각도·유사도 게이트를 통과한 글을 올려도, 독자 동선은 관련 글에서 끊길 수 있다.
부연하면, 이번에 확인한 0건 깨짐은 “지금은 깨끗하다”는 스냅샷일 뿐이다. 글이 늘어날수록 오타 확률은 올라간다. 배포 파이프라인이 Actions든 Wrangler든, 콘텐츠 그래프 검증은 빌드 앞단에 두는 편이 낫다.
실측을 한 줄로 더 남긴다. 컬렉션 id는 파일 stem과 같아야 RelatedPosts의 p.id === slug 비교가 산다. 확장자·경로를 섞어 쓰면 파일이 있어도 매칭이 실패한다. 자동화로 글을 대량 추가할수록, “길이 2 이상”만 보는 스키마와 “없으면 숨김” UI의 조합을 전제로 한 사전 존재 검증이 배포 체크리스트의 고정칸이 돼야 한다. 빌드가 초록인 날일수록, 관련 글 HTML을 한 번 열어보는 습관이 싸게 먹힌다.
배포 직후 스모크 테스트 목록에 /posts/<slug>/ HTML에서 ‘관련 글’ 헤딩 존재 여부를 넣으면, Actions 침묵·Wrangler 수동 배포 날에도 같은 체크가 산다. 콘텐츠 파이프라인은 빌드 성공만으로 끝내지 않는다.
출처
- 내부 실측:
src/content.config.tsrelatedSlugs Zod 규칙,src/components/RelatedPosts.astro의 map→filter(Boolean) 동작, 전 포스트 relatedSlugs 존재 대조(깨짐 0건, 2026-08-01) - Notion 시드: «리뷰올 오프아이스 구성 설명» — 운영/로컬 환경 분리·검증 누락을 공개 가능한 교훈으로만 재구성 (비밀·제품 실명 제외)