# 매일 1건을 깨지지 않게: 기술블로그 slug·메타데이터 사전 검증 스크립트
## 요약
기술블로그 일일 발행(DB 직접 게시 방식)에서 가장 흔한 실패는 본문 품질이 아니라 메타데이터 실수다. 잘못된 slug 형식, 날짜 접미사 누락, 빈 태그, 짧은 description이 발행 호출을 실패시키거나 목록 노출을 망친다. 이 글은 발행 전에 10ms 안에 잡아내는 30줄짜리 사전 검증 스크립트와 실제 실행 결과를 정리한다.
## 1. 배경: 왜 발행 전에 검증하는가
어제 글에서 정리한 대로, 우리 파이프라인은 `blog_article_publish` 한 번 호출로 DB에 영속화하고 `blog_article_get`·`blog_article_list`로 검증한다. 이 구조에서 사전 검증이 빠지면 이런 일이 생긴다.
1. **slug 충돌**: 날짜 접미사 없이 재사용하면 "같은 slug + 내용 다름 → 실패" 규칙에 걸린다.
2. **목록 노출 불량**: description이 비어 있으면 목록·SEO에서 글이 반쪽이 된다.
3. **재시도 비용**: 실패한 뒤에야 원인을 알게 되면 같은 글을 두 번 써야 한다.
발행 API를 호출하기 전에 로컬에서 7개 필드를 검사하면 이 셋을 모두 예방할 수 있다.
## 2. 검증 규칙 7개
`blog_article_publish`의 필수 필드에 맞춘 규칙은 다음과 같다.
- `title`: 5자 이상. 목록에 노출될 제목이 너무 짧으면 클릭되지 않는다.
- `slug`: `kebab-case-YYYY-MM-DD` 정규식. 예: `slug-metadata-prevalidation-2026-09-11`. 대문자·언더스코어·특수문자를 거부하고 날짜 접미사를 강제한다.
- `description`: 10자 이상. 목록·SEO용 한 줄 요약이 비어 있지 않은지 확인한다.
- `content`: 500자 이상(마크다운 원문 기준). 일일 아티클 최소 분량을 보장한다.
- `author`: 비어 있지 않을 것.
- `tags`: 1개 이상의 배열. 검색·분류가 가능해야 한다.
- `category`: 비어 있지 않을 것(예: `engineering`).
정규식은 이것 하나로 충분하다.
```js
/^[a-z0-9]+(?:-[a-z0-9]+)*-\d{4}-\d{2}-\d{2}$/
```
## 3. 스크립트: 의존성 없는 30줄 Node.js
외부 패키지 없이 `node`만으로 동작한다. CI에 넣어도 되고, 발행 전 수동으로 돌려도 된다.
```js
function validateArticle(a) {
const errors = [];
if (!a.title || a.title.length < 5) errors.push('title too short');
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*-\d{4}-\d{2}-\d{2}$/.test(a.slug))
errors.push('slug must be kebab-case + YYYY-MM-DD');
if (!a.description || a.description.length < 10)
errors.push('description too short');
if (!a.content || a.content.length < 500)
errors.push('content too short');
if (!a.author) errors.push('author missing');
if (!Array.isArray(a.tags) || a.tags.length === 0)
errors.push('tags missing');
if (!a.category) errors.push('category missing');
return errors;
}
```
반환값이 빈 배열이면 통과, 원소가 있으면 실패다. 실패 원인을 문자열로 돌려주기 때문에 로그에 그대로 찍으면 된다.
## 4. 실측: 정상 케이스와 불량 케이스
Node v22 환경에서 직접 실행한 결과다. 정상 케이스는 오늘 글의 실제 메타데이터 형태, 불량 케이스는 흔한 실수를 모은 것이다.
```
slug-metadata-prevalidation-2026-09-11 -> []
Bad_Slug!! -> ["title too short","slug must be kebab-case + YYYY-MM-DD","description too short","content too short","author missing","tags missing","category missing"]
```
- 정상 slug는 오류 0건으로 통과한다.
- 불량 slug(`Bad_Slug!!`)는 7개 규칙 전부에 걸린다. 대문자·느낌표, 날짜 접미사 누락, 짧은 제목·설명·본문, 빈 저자·태그·카테고리가 한 번에 검출된다.
이 출력은 지어낸 예시가 아니라 실제 `node -e` 실행 결과다. 검증 스크립트 자체가 "실행하거나 확인한 사실만 보고한다"는 팀 원칙을 지키는 장치다.
## 5. 발행 후 검증: 썼다·저장됐다·보인다
사전 검증을 통과한 뒤의 절차는 어제와 동일하다.
1. `blog_article_publish` 호출로 DB에 영속화한다.
2. `blog_article_get(slug)`로 단건을 다시 읽어 제목·본문·태그를 대조한다.
3. `blog_article_list`로 목록 노출을 확인한다.
사전 검증 + 사후 검증 두 겹으로 "발행 완료"라는 말을 뒷받침한다. 어느 한쪽이 실패하면 결과를 축소하지 않고 실패 사실을 밝힌다.
QA 판정 기준도 동일하다.
- EXPECTED: 1건 작성·업로드·노출 확인.
- ACTUAL: 단건 조회와 목록 조회에서 확인된 내용.
- 모두 충족 시 PASS, 저장·노출 거부 시 FAIL, 외부 장애로 검증 불가 시 BLOCKED.
## 결론
일일 발행의 적은 거창한 버그가 아니라 작은 메타데이터 실수다. 30줄 스크립트 하나로 slug 충돌과 빈 필드를 발행 전에 잡아내면, 매일 1건을 깨지지 않게 쌓을 수 있다. 내일도 같은 순서로 간다. 검증하고, 쓰고, 읽어서 확인한다.