Hexo 머리말(front-matter) YAML 때문에 글 한 편이 사라진 날

10편을 썼는데 9편만 나왔다

하루에 글 10편을 올리는 날이었다. 로컬에서 빌드하고 나서 습관처럼 오늘 날짜 폴더의 글 수를 셌는데 9개였다. 에러 메시지는 분명 떴는데 빌드는 끝까지 돌아서, 그냥 지나치면 한 편이 빠진 채로 푸시할 뻔했다.

로그를 올려 보니 이랬다.

1
2
ERROR {
err: YAMLException: bad indentation of a mapping entry (4:22)

Hexo는 글 하나의 머리말이 YAML로 읽히지 않아도 프로세스를 멈추지 않고 그 글만 건너뛴다. 그래서 빌드 성공처럼 보이는 게 함정이다.

범인은 작은따옴표 한 글자

문제의 글은 이런 머리말을 갖고 있었다.

1
description: '국민 아버지'로 불린 배우가 별세했습니다. ...

YAML에서 값이 '나 "로 시작하면 문자열 전체가 따옴표로 감싸진 값이라고 해석한다. 그런데 '국민 아버지' 뒤에 글자(로 불린...)가 더 붙어 있으니 문법이 깨진다. 제목 말머리에 인용부호를 쓰고 싶어서 습관처럼 쓴 게 화근이었다.

고치는 방법은 둘이다. 따옴표로 시작하지 않게 문장을 바꾸거나, 아예 값 전체를 따옴표로 감싸고 안쪽 따옴표를 이스케이프하는 것이다. 나는 앞의 방법을 골랐다.

1
description: 국민 아버지로 불린 배우가 별세했습니다. ...

같은 계열로 터지는 것들

겪어 보니 머리말 값에서 조심해야 할 건 대략 이렇다.

상황 결과
값이 ' 또는 "로 시작 따옴표 짝이 안 맞으면 파싱 실패
값 중간에 : (콜론+공백) 새 키로 해석돼 구조가 깨짐
값이 #로 시작하거나 중간에 # 뒤쪽이 주석으로 잘림
값이 *, &, !, %, @로 시작 YAML 특수 문자로 해석
탭 문자 들여쓰기 YAML은 탭 들여쓰기를 허용하지 않음

제목에 : 가 필요하면 —(줄표)나 -로 바꾸는 게 제일 간단했다. 이 블로그는 글이 많아서 아예 규칙으로 정해 뒀다. 제목과 설명은 따옴표로 시작하지 않고, 값 중간에 콜론+공백을 쓰지 않는다.

재발 방지: 푸시 전 두 가지 확인

1. 글 수를 센다. 하루에 올리는 글 수가 정해져 있으니 빌드 뒤에 개수만 비교해도 빠진 글을 바로 알 수 있다.

1
2
npx hexo clean && npx hexo generate
ls -d public/2026/10/30/*/ | wc -l

2. YAML을 직접 파싱해 본다. Hexo가 쓰는 js-yaml을 그대로 써서 머리말만 검사하는 스크립트다. 에러가 난 글 이름을 바로 알려 준다.

1
2
3
4
5
6
7
8
9
10
11
12
const fs = require('fs');
const yaml = require('js-yaml');

for (const f of fs.readdirSync('source/_posts').filter(f => f.endsWith('.md'))) {
const src = fs.readFileSync('source/_posts/' + f, 'utf8').replace(/\r/g, '');
const head = src.split(/^---\s*$/m)[1];
try {
yaml.load(head);
} catch (e) {
console.log(f, '→', e.message.split('\n')[0]);
}
}

글이 수백 편이어도 몇 초면 끝난다. 이걸 푸시 전에 한 번 돌리는 습관을 들였다.

마무리

에러가 났는데도 빌드가 “성공”하는 도구는 로그를 안 보면 놓친다. 사람이 눈으로 확인하는 대신 글 수와 YAML 검사 같은 기계적인 체크를 걸어 두는 게 마음이 편하다. 내 경우에는 글을 빨리 많이 쓰다 보니 그냥 지나칠 위험이 높아서 더 그랬다.