quartz/components/Head.tsx 파일은 정적 사이트 생성기 Quartz에서 각 HTML 페이지의 <head> 태그를 구성하는 핵심 컴포넌트다. 이 파일은 Markdown 소스 파일의 frontmatter와 빌드 설정(cfg)을 조합하여 브라우저가 인식할 수 있는 메타데이터를 생성한다. 페이지의 구조화 데이터와 메타데이터가 만들어지는 지점은 이 컴포넌트의 렌더링 로직 내부에 위치한다.
페이지 제목과 설명의 우선순위
페이지의 제목(title)과 설명(description)은 서로 다른 소스에서 값을 가져오며, 특정 우선순위 규칙이 적용된다. 이 로직은 Head 함수의 시작 부분에서 정의된다.
const titleSuffix = cfg.pageTitleSuffix ?? ""
const title =
(fileData.frontmatter?.title ?? i18n(cfg.locale).propertyDefaults.title) + titleSuffix
const description =
fileData.frontmatter?.socialDescription ??
fileData.frontmatter?.description ??
unescapeHTML(fileData.description?.trim() ?? i18n(cfg.locale).propertyDefaults.description)- 제목: 먼저
frontmatter의title을 확인한다. 없으면 로캘(locale) 설정의 기본 제목을 사용한다. 마지막으로cfg.pageTitleSuffix가 설정되어 있다면 이를 뒤에 붙인다. - 설명:
socialDescription이 우선한다. 없으면description을 사용한다. 이 두 값이 모두 없으면, Markdown 파일의 본문에서 추출된fileData.description을 사용한다. 마지막으로 기본값을 사용한다.
Canonical URL과 구조화 데이터 판단
Quartz는 각 페이지의 고유 URL을 생성하고, 해당 페이지가 블로그 글인지 아닌지를 판단하여 구조화 데이터(JSON-LD)의 타입을 결정한다.
- Canonical URL:
canonicalUrl함수는baseUrl과slug를 조합하여 절대 URL을 생성한다. - Article 판별:
isArticlePage함수는frontmatter.type이article또는post인지 확인하거나, 슬러그가 날짜 접두사(YYYY-MM-DD-)로 시작하는지 확인하여 블로그 글임을 식별한다.
export function isArticlePage(fileData: QuartzComponentProps["fileData"]): boolean {
const slug = fileData.slug
if (!slug || !fileData.frontmatter?.date) {
return false
}
// ... (중략)
// Existing posts use a date-prefixed slug. This keeps dated utility, policy,
// and section landing pages from being mislabeled as BlogPosting objects.
return ARTICLE_SLUG_PATTERN.test(slug)
}이 판단 결과는 BlogPosting 또는 WebPage 스키마로 구조화 데이터를 주입하는 데 사용된다. 라이브 페이지의 JSON-LD를 확인하면 글 페이지는 "@type":"BlogPosting"으로 나온다.
RSS 피드 및 아이콘 링크
페이지의 루트 경로에 따라 RSS 피드 링크와 아이콘 경로가 동적으로 결정된다.
- RSS 피드:
ContentIndex플러그인이/index.xml에 피드를 생성하므로,Head컴포넌트는 현재 페이지의 베이스 경로를 기준으로index.xml링크를 삽입한다. 이는 크롤러와 피드 리더가 피드를 발견할 수 있게 한다. - 아이콘: 404 페이지가 아닌 경우, 현재 페이지의 루트 경로에서
static/icon.png를 아이콘 경로로 설정한다.
const baseDir = fileData.slug === "404" ? path : pathToRoot(fileData.slug!)
const iconPath = joinSegments(baseDir, "static/icon.png")
const rssPath = joinSegments(baseDir, "index.xml")외부 리소스 및 OG 이미지
페이지에 필요한 CSS, JS, 그리고 추가적인 헤드 태그는 빌드 과정에서 수집된 externalResources 객체를 통해 주입된다.
- CSS/JS:
externalResources.css와externalResources.js는 빌드 시 생성된 정적 파일 링크다. - OG 이미지:
CustomOgImagesEmitterName플러그인을 통해 생성된 OG 이미지 링크가 추가된다. - Google Fonts:
googleFontHref와googleFontSubsetHref함수를 통해 폰트 리소스가 로드된다.
const { css, js, additionalHead } = externalResources검증 절차: 메타데이터 생성 확인
quartz/components/Head.tsx의 로직이 실제로 메타데이터를 생성하는지 확인하려면 빌드된 HTML 파일을 직접 검토해야 한다.
- 빌드 실행:
npx quartz build명령을 실행한다. - HTML 파일 열기: 빌드 출력 디렉토리(기본적으로
public)에서 임의의.html파일을 연다. <head>태그 확인:<title>태그에 frontmatter의 제목과 suffix가 올바르게 결합되었는지 확인한다.<meta name="description">태그에 socialDescription 또는 description이 입력되었는지 확인한다.<link rel="canonical">태그에 올바른 절대 URL이 있는지 확인한다.<link rel="alternate" type="application/rss+xml">태그에 RSS 피드 링크가 있는지 확인한다.<script type="application/ld+json">태그에@type이Article또는WebPage로 설정되었는지 확인한다.
| 시점 | 실행할 명령 | 결과가 나쁘면 할 일 |
|---|---|---|
| 빌드 전 | grep -r "socialDescription" content/ | frontmatter에 socialDescription이 누락되었으면 추가한다. |
| 빌드 후 | grep -o "<title>.*</title>" public/*.html | 제목이 중복되거나 누락되었으면 cfg.pageTitleSuffix를 확인한다. |
| 빌드 후 | grep -o "application/ld+json" public/*.html | 구조화 데이터가 누락되었으면 isArticlePage 로직을 검토한다. |
| 빌드 후 | grep -o "rss+xml" public/*.html | RSS 링크가 누락되었으면 ContentIndex 플러그인 활성화 여부를 확인한다. |
공식 문서 참조
Quartz의 메타데이터 처리 로직은 Markdown 파싱과 정적 사이트 생성기의 일반적인 원리를 따르지만, 구체적인 구현은 Quartz의 코드베이스에 의존한다. 관련 표준 및 도구의 공식 문서는 다음과 같다.
적용 범위와 제약
이 분석은 Quartz v4의 Head.tsx 컴포넌트를 기준으로 한다. 다른 버전의 Quartz나 포크된 저장소는 로직이 다를 수 있다. 또한, externalResources의 내용은 빌드 플러그인의 설정에 따라 달라지므로, 커스텀 플러그인을 사용하는 경우 메타데이터 생성 로직이 확장될 수 있다. isArticlePage 함수의 날짜 패턴 매칭은 특정 슬러그 형식을 사용하는 블로그에 최적화되어 있으며, 다른 형식의 콘텐츠에는 적합하지 않을 수 있다.