Files
document-haness/.agents/skills/writing-tech-log-records/references/body-syntax.md
T
DongHyeonkaandClaude Opus 5 ab59130196 chore: 이전 세션이 남긴 변경을 커밋한다
이번 파이프라인 작업과 무관하게 작업 트리에 남아 있던 것을 그대로 올린다.
사용자가 「전부 커밋」으로 정했고, 이번 작업과 섞이지 않게 커밋만 나눴다.

대부분은 clean-architecture-backend-template 의 그림 정본 재배치다 —
final/assets/diagrams/<이름>/ 에 있던 것이 CLAUDE.md 가 적은 배치인
final/assets/<이름>/ 로 옮겨졌고 .techviz/<이름>/ 이 함께 들어왔다.
삽입 줄의 대부분(3.15M)이 그 .techviz context.json 이다.

그 밖에 ca-tmpl·document-haness 의 정리, .claude/agents/ 열한 개,
writing-practitioner-guides 스킬, .playwright-mcp 세션 산출물,
scripts/check-ssot-facts.py 와 그 시험이 들어 있다.

이 커밋의 내용은 내가 만든 것이 아니라 이전 세션이 남긴 것이고 검증하지 않았다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 11:02:02 +09:00

4.7 KiB

Case 본문 문법

본문 Markdown 칸에만 해당한다. 다른 종류의 칸은 평문이다.

본문은 자유 Markdown이 아니라 화이트리스트로 좁힌 Markdown이다. 공개 사이트가 raw HTML이 아니라 타입 블록을 렌더링하기 때문에, 유니온에 없는 문법은 렌더링할 대상이 없어 거절된다. 벗어나면 CONTENT_FORMAT_INVALID로 게시가 막힌다.

쓸 수 있는 것

문법
제목 # ~ ###### (1~6단계)
문단 그냥 쓴다
강조 **굵게** *기울임* `코드`
링크 [문구](/경로) · [문구](https://…)
목록 - 항목 · 1. 항목
인용 > 한 문단
수평선 ---
코드블록 ```java
파이프 표. 감싸지 않는다
그림 ![대체 텍스트](/api/v1/public/media/…)
callout :::note :::tip :::warning :::danger
증거 이미지 :::evidence key="…" alt="…" caption="…" zoom="true"

제목에 고정 id를 주려면 ## 측정 결과 {#measurement}.

쓸 수 없는 것

  • raw HTML<div>, <br>, <img> 모두 거절
  • 각주[^1]
  • 체크박스 목록- [ ] 할 일
  • 중첩 목록 — 목록 항목 안에 목록
  • 중첩 인용> >
  • 취소선~~지움~~
  • 링크 title[문구](/경로 "설명")
  • 외부 스킴javascript:, data:, //다른호스트

인용과 callout은 문단을 정확히 하나만 담는다. 목록 항목도 문단 하나만 담는다. 여러 문단이 필요하면 블록을 나눈다.

링크와 이미지 주소

허용되는 주소는 넷뿐이다.

#앵커
/상대경로
https://…  또는  http://…
mailto:…

//호스트로 시작하는 주소는 거절된다. 프로토콜 상대 주소는 어느 사이트를 가리키는지 원문만 보고 알 수 없기 때문이다.

object storage 주소를 본문에 직접 쓰지 않는다. 만료되는 presigned URL이 원문에 박히면 나중에 깨진다. 이미지는 Asset으로 올리고 /api/v1/public/media/{assetId} 또는 :::evidence로 가리킨다.

directive 쓰는 법

세 개뿐이다: table, callout, evidence. 그리고 서버가 아는 이름 넷: note, tip, warning, danger.

속성은 정확히 맞아야 한다 — 하나라도 빠지거나 남으면 거절된다.

:::table id="…" caption="…" rowHeaderColumn="1"|"none"
:::callout tone="warning"|"info" label="…"
:::evidence key="…" alt="…" caption="…" zoom="true"|"false"

note·tip·warning·danger는 이름이 곧 성격이라 속성을 받지 않는다.

:::note

참고할 내용 한 문단.

:::

여는 줄과 닫는 ::: 사이에 빈 줄을 둔다. 붙여 쓰면 문단으로 인식되지 않는다.

자주 나오는 거절과 원인

메시지 원인
unsupported block syntax: html raw HTML을 썼다
unsupported inline syntax: image 문단 안에 글과 그림을 섞었다
unknown block directive: … 위 일곱 이름이 아니다
table directive must contain exactly one GFM table :::table 안에 표가 없거나 둘이다
callout directive must contain exactly one paragraph callout에 문단이 없거나 둘 이상이다
list items must contain exactly one paragraph 목록을 중첩했다
unsafe link URL: … 허용되지 않는 스킴이나 // 주소
duplicate explicit ID: … 같은 id를 두 번 썼다
unknown inline directive: 27 문단에 시각을 그냥 썼다 — 아래를 본다

거절 메시지에는 줄·칸 번호가 붙는다. 그 줄을 먼저 본다.

문단 안의 시각은 백틱으로 감싼다

: 뒤에 글자가 붙으면 파서가 인라인 directive 로 읽는다. 그래서 문단에 08:20:27 을 그냥 쓰면 :27 에서 막힌다. 00/12:00:00 같은 설정값도 같다.

새 인증서가 08:20:27 에 기록됐다.      ← FAIL  unknown inline directive: 27
새 인증서가 `08:20:27` 에 기록됐다.    ← PASS

이것이 조용한 결함이 되는 경로가 있다. 막히면 시각을 빼고 넘어가게 되고, 그러면 검사기는 통과하는데 기록에서 수치가 사라진다. 실제로 한 회차에서 두 편이 각각 시각 둘과 타이머 설정값을 빼고 통과시켰다. 수치·날짜·시각은 보호 구간이다 — 빼지 말고 감싼다.

표와 코드블록 안은 걸리지 않는다. 그래서 같은 프로젝트의 다른 기록이 통과하는 것이 「이 문법이 괜찮다」는 뜻이 아니다 — 그쪽은 시각이 전부 표 안에 있었을 뿐이다.