페이지
페이지는 사이트의 HTML 페이지가 되는 마크다운 파일입니다. 이 문서는 콘텐츠 작성 방법, 즉 프론트 매터 필드, 마크다운 문법, 파일 구성을 다룹니다. 이 필드를 템플릿에서 읽는 방법은 데이터 모델을 참고합니다.
기본 구조
+++
title = "My Page"
date = "2024-01-15"
+++
Your content in **Markdown**.
+++ 블록이 TOML 프론트 매터입니다. YAML(--- 구분자)과 JSON(파일 맨 앞의 최상위 {...} 객체)도 지원합니다. 그 아래 내용이 HTML로 변환됩니다.
---
title: "My Page"
date: "2024-01-15"
---
Your content in **Markdown**.
{
"title": "My Page",
"date": "2024-01-15"
}
Your content in **Markdown**.
JSON은 파일 맨 앞에서 처음으로 짝이 맞는 {...}가 프론트 매터가 되며, 별도 구분자가 필요 없습니다. 파일은 반드시 {로 시작해야 합니다(앞 공백 불가).
프론트 매터
필수
| 필드 | 타입 | 설명 |
|---|---|---|
| title | string | 페이지 제목 |
주요 필드
| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
| date | string | — | 발행일. YYYY-MM-DD, 시간 포함 가능(YYYY-MM-DD HH:MM:SS 또는 RFC 3339 datetime). 따옴표 없는 TOML/YAML 날짜도 허용 |
| description | string | — | SEO 설명 |
| draft | bool | false | 프로덕션 빌드에서 제외 |
| template | string | "page" | 사용할 템플릿 |
| weight | int | 0 | 정렬 순서(낮을수록 앞) |
| image | string | — | 소셜 공유용 대표 이미지 |
| tags | array | [] | 태그 택소노미 항목 |
| categories | array | [] | 카테고리 택소노미 항목 |
전체 필드
| 필드 | 타입 | 설명 |
|---|---|---|
| updated | string | 마지막 수정일 |
| slug | string | 커스텀 URL 슬러그 |
| path | string | 커스텀 URL 경로 |
| aliases | array | 이 페이지로 리다이렉트할 URL 목록 |
| authors | array | 작성자 이름 |
| toc | bool | 목차 표시 |
| in_search_index | bool | 검색 포함 여부 |
| in_sitemap | bool | 사이트맵 포함 여부 |
| insert_anchor_links | bool | 헤딩 앵커 추가 |
| redirect_to | string | 페이지를 이 URL로 리다이렉트 |
| render | bool | 페이지를 출력으로 렌더링(기본값: true) |
| expires | date | 이 날짜 이후 자동 제외 |
| series | string | 묶음용 시리즈 이름 |
| series_weight | int | 시리즈 내 정렬 순서 |
| extra | table | 커스텀 메타데이터 |
예시
블로그 글
+++
title = "Getting Started with Crystal"
date = "2024-01-15"
description = "Learn Crystal programming basics"
tags = ["crystal", "tutorial"]
authors = ["Alice Smith"]
image = "/images/crystal-guide.png"
+++
Crystal is a fast, compiled language...
초안
+++
title = "Work in Progress"
draft = true
+++
Not visible in production.
초안 포함 빌드: hwaro build --drafts
만료 콘텐츠
+++
title = "Limited Time Offer"
expires = 2025-12-31
+++
Automatically excluded from builds after the expiry date.
만료된 콘텐츠 포함 빌드: hwaro build --include-expired
만료까지 7일 이내인 페이지는 빌드 경고를 냅니다.
미래 날짜 콘텐츠
date가 미래인 페이지는 빌드에서 자동으로 빠집니다. 콘텐츠 예약 발행에 유용합니다.
+++
title = "Coming Soon"
date = 2099-01-01
+++
Published only after the date arrives.
미래 콘텐츠 포함 빌드: hwaro build --include-future
시리즈 글
+++
title = "Part 1: Introduction"
series = "Crystal Tutorial"
series_weight = 1
+++
First part of the series.
config.toml에서 [series]를 켜면 빌드 시점에 page.series_index(1부터 시작하는 위치)와 page.series_pages(시리즈의 전체 페이지)가 계산됩니다. 둘 다 템플릿 전용 값이며 프론트 매터 필드가 아닙니다. 데이터 모델을 참고합니다.
커스텀 템플릿
+++
title = "Landing Page"
template = "landing"
+++
Uses `templates/landing.html` instead of `page.html`.
가중치 정렬
+++
title = "Introduction"
weight = 1
+++
+++
title = "Getting Started"
weight = 2
+++
weight가 낮을수록 앞에 옵니다.
URL 별칭
+++
title = "New Page"
aliases = ["/old-url/", "/another-old-url/"]
+++
Redirects from old URLs to this page.
각 alias는 이 사이트 안의 경로입니다. 빌드가 해당 위치에 리다이렉트
스텁을 씁니다. 절대 URL(https://example.com/old/)이나 프로토콜 상대
URL(//example.com/old/)은 이 빌드가 게시할 수 있는 대상이 아니므로
경고와 함께 건너뜁니다.
커스텀 메타데이터
+++
title = "Product Review"
[extra]
rating = 4.5
featured = true
pros = ["Fast", "Reliable"]
+++
템플릿에서 접근: {{ page.extra.rating }}
전체 프론트 매터 레퍼런스
모든 필드를 한 블록에 모았습니다. 복사한 뒤 필요 없는 항목을 지우면 됩니다.
+++
title = "Page Title"
date = "2024-01-15"
updated = "2024-02-01"
description = "SEO description"
draft = false
template = "page"
weight = 0
slug = "custom-slug"
path = "custom/path"
aliases = ["/old-url/"]
image = "/images/cover.png"
tags = ["tag1", "tag2"]
categories = ["category1"]
authors = ["Author Name"]
toc = true
in_search_index = true
in_sitemap = true
insert_anchor_links = true
render = true
redirect_to = ""
expires = 2025-12-31
series = "Series Name"
series_weight = 1
[extra]
custom_field = "value"
+++
콘텐츠 요약
page.summary는 목록 페이지, 피드, 소셜 카드에 표시되는 글의 요약입니다. 다음 소스 중 먼저 존재하는 것으로 채워집니다:
<!-- more -->마커 앞의 콘텐츠 (HTML로 렌더링)- 프론트 매터의
description필드 - 렌더링된 본문에서 자동으로 잘라낸 발췌문
<!-- more -->로 요약 범위를 명시적으로 지정합니다:
+++
title = "Long Article"
+++
This is the summary shown in listings.
<!-- more -->
The full article continues here...
자동 요약
마커도 description도 없는 페이지에는 자동 요약이 생성됩니다. 렌더링된 본문의 처음 70단어를 <p> 하나로 감싸고, 텍스트가 잘린 경우에만 말줄임표(…)를 붙입니다. 발췌문은 렌더링된 HTML에서 만들어지므로 숏코드는 확장되고 코드 블록, 헤딩, 이미지, figure는 건너뛰어 자연스러운 산문으로 읽힙니다.
단어가 공백으로 구분되지 않는 CJK 위주 텍스트(한국어, 일본어, 중국어)는 설정값의 2배를 글자 수로 셉니다. summary_length = 70이면 최대 140자입니다. 절단은 단어 경계(CJK는 글자 경계)에서 이루어지며 엔티티나 멀티바이트 문자를 쪼개지 않습니다.
config.toml에서 조정하거나 끌 수 있습니다:
[content]
summary_length = 70 # 단어 수 (CJK: 글자 수 ×2); 0이면 자동 요약 비활성화
summary_ellipsis = "…" # 텍스트가 실제로 잘린 경우에만 추가
page.summary_truncated는 자동 발췌문이 잘렸을 때만 true이므로, 필요한 곳에만 "더 읽기" 링크를 표시할 수 있습니다. 자동 요약은 마커나 description을 대체하지 않으므로 이미 요약이 있는 페이지의 출력은 그대로 유지됩니다.
마크다운 문법
텍스트
**bold** and *italic*
`inline code`
[link](https://example.com)

목록
- Unordered
- Items
1. Ordered
2. Items
코드 블록
```javascript
console.log("Hello");
```
표
| Header | Header |
|--------|--------|
| Cell | Cell |
표 셀 안에서도 인라인 마크다운이 동작합니다: 굵게, 기울임, 코드 스팬, [links](url), , 취소선.
| Feature | Example |
|----------------|----------------------------------|
| Bold | **important** |
| Italic | *emphasis* |
| Code | `config.toml` |
| Link | [Hwaro](https://example.com) |
| Image |  |
| Strikethrough | ~~deprecated~~ |
내부 링크
@/를 사용하면 다른 콘텐츠 페이지를 소스 경로로 링크할 수 있습니다. Hwaro가 빌드 시점에 올바른 출력 URL로 변환합니다.
[Read the post](@/blog/my-post.md)
[About section](@/about/_index.md)
[With anchor](@/blog/my-post.md#introduction)
최종 URL을 몰라도 된다는 점이 장점입니다. Hwaro가 콘텐츠 경로에서 URL을 계산합니다. 대상 페이지가 없으면 링크는 그대로 남고 빌드 중 경고가 기록됩니다.
| 문법 | 변환된 URL |
|---|---|
@/blog/post.md |
/blog/post/ |
@/blog/_index.md |
/blog/ |
@/blog/post.md#section |
/blog/post/#section |
엄격 모드
경고에서 그치지 않고 빌드를 실패시키려면 config.toml에서 켭니다:
[links]
broken_internal = "error" # 기본값: "warn"
error 모드에서는 모든 페이지의 미해결 @/ 링크를 모아 하나의 목록(source.md → @/target (reason))으로 출력하며 빌드가 실패하고, CI용 종료 코드 5로 매핑됩니다. hwaro serve 중에는 실패가 에러 오버레이에 표시되고 서버는 계속 동작합니다.
--cache 사용 시 주의: 웜 빌드에서는 다시 렌더링된 페이지만 재검사하므로, 변경되지 않은 페이지 안의 깨진 링크는 그 페이지가 다시 렌더링될 때까지 드러나지 않습니다. 완전한 검사를 위해 CI에서는 --cache 없이 콜드 빌드를 실행합니다.
인용문
> Quote text
어드모니션
GitHub 스타일 알림 블록(admonition)이 스타일이 적용된 콜아웃으로 렌더링됩니다. 인식하는 타입: NOTE, TIP, IMPORTANT, WARNING, CAUTION.
> [!NOTE]
> Pay attention to this paragraph.
> [!WARNING]
>
> Body can also live in its own paragraph.
> [!TIP] 커스텀 제목
> 마커와 같은 줄에 적은 텍스트는 제목이 됩니다(Obsidian / Hugo 문법).
출력은 <div class="admonition admonition-{type}">이며, 제목 문단(<p class="admonition-title">) 뒤에 본문이 이어집니다. 커스텀 제목이 없으면 타입 이름(Note, Tip, …)이 제목으로 쓰입니다. 스타일은 사이트 CSS에서 지정합니다. Hwaro는 시맨틱 마크업만 내보냅니다.
어드모니션과 일반 인용문 안에서도 테이블을 쓸 수 있습니다. 평소처럼 각 행 앞에 >를 붙이면 됩니다(> | a | b |, > |---|---|).
config.toml의 [markdown] 아래에 admonitions = false를 두면 비활성화됩니다.
제한 사항: 타입은 대소문자를 구분하며([!NOTE]만 인식, [!note]는 안 됨), 어드모니션 본문 안에 중첩된 인용문이 있으면 바깥 어드모니션이 일찍 닫힙니다. 인라인 이스케이프는 없습니다. 백슬래시 이스케이프(\[!NOTE\])도 같은 문자를 렌더링하면서 여전히 어드모니션을 발동시키므로, 토큰을 문자 그대로 출력해야 한다면 기능 자체를 끄면 됩니다.
커스텀 헤딩 ID
헤딩 줄 끝에 {#custom-id}를 붙이면 자동 생성 슬러그를 덮어씁니다. 제목을 고쳐도 깨지지 않는 안정적인 앵커 URL이 필요할 때 유용합니다.
## Installation Guide {#install}
<h2 id="install">Installation Guide</h2>로 렌더링됩니다. 목차와 [link](#install) 링크 모두 커스텀 id를 사용합니다.
id에 허용되는 문자: 영문자, 숫자, _, -, :. id는 영문자로 시작해야 합니다. CommonMark는 ATX 헤딩 앞에 공백을 3칸까지 허용하며, 더 깊이 들여 쓰면 그 줄이 코드 블록이 되어 {#id}가 적용되지 않습니다.
config.toml의 [markdown] 아래에 heading_ids = false를 두면 비활성화됩니다.
커스텀 헤딩 ID에는 markdown.safe = false가 필요합니다. safe 모드에서는 {#id} 문법이 출력에서 제거되고 id도 적용되지 않습니다. safe 모드와 명시적 id가 동시에 필요하면 raw HTML 헤딩을 사용합니다. 한 페이지에 같은 {#id}를 두 번 쓰면 id 속성이 중복되며, 앵커는 첫 번째가 우선합니다.
{#id} 단축 문법은 더 일반적인 {#id .class key=val} 속성 블록([markdown] attributes = true)의 특수한 경우이며, 속성 블록은 인라인 이미지에도 적용됩니다. 마크다운 확장을 참고합니다.
정의 목록
Term
: Definition body
Another term
: Definition with **bold**, *italic*, `code`, [a link](https://example.com), and ~~strikethrough~~
: A second definition for the same term
용어와 정의 양쪽에서 인라인 마크다운이 동작합니다. raw HTML은 안전을 위해 이스케이프됩니다.
에셋 코로케이션
이미지, PDF 같은 관련 에셋을 콘텐츠 파일과 같은 디렉터리에 함께 둘 수 있습니다. 이를 **페이지 번들(Page Bundle)**이라고 부릅니다.
이 기능을 쓰려면 마크다운 파일 이름을 index.md(일반 페이지) 또는 _index.md(섹션 페이지)로 바꾸고, 페이지 이름을 딴 디렉터리 안에 넣습니다.
구조 예시:
content/
└── blog/
├── my-trip/
│ ├── index.md <-- The page content
│ ├── photo.jpg <-- Asset
│ └── data.json <-- Asset
└── _index.md
Hwaro는 페이지 번들 디렉터리의 마크다운이 아닌 파일 전부를 상대 경로를 유지한 채 출력 디렉터리로 복사합니다.
마크다운에서는 상대 경로로 이 에셋에 링크하면 됩니다:

[Download Data](data.json)
템플릿에서 에셋 접근
템플릿에서는 page.assets로 함께 둔 에셋 목록에 접근합니다. 파일의 상대 경로 배열을 반환합니다.
{% for asset in page.assets %}
{% if asset is matching("[.](jpg|png)$") %}
<img src="{{ get_url(path=asset) }}" alt="Gallery Image">
{% endif %}
{% endfor %}
URL 매핑
| 파일 | URL |
|---|---|
| content/index.md | / |
| content/about.md | /about/ |
| content/blog/post.md | /blog/post/ |