
글에 넣는 그림을 그림판 대신 HTML로 씁니다
이 블로그에는 글마다 도식이 한두 장씩 들어갑니다. 열일곱 편을 쓰는 동안 스물몇 장이 됐습니다.
그림판이나 포토샵으로 그렸으면 진작 포기했을 겁니다. 전부 HTML로 씁니다.
그림 한 장이 파일 한 개입니다
도식마다 .html 파일을 하나 만듭니다. 안에 든 건 평범한 웹 페이지입니다.
<meta charset="utf-8" />
<link rel="stylesheet" href="base.css" />
<style>
.grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.card {
background: var(--card);
border-radius: 14px;
padding: 18px;
}
</style>
<div class="wrap">
<div class="kicker">같은 칸, 두 가지 형식</div>
<h1>한쪽만 깨지면 더 오래 못 찾습니다</h1>
...
</div>
표를 그리고 싶으면 grid를 쓰고, 카드가 필요하면 border-radius를 줍니다. 평소에 쓰던 CSS 그대로입니다. 새로 배울 게 없다는 게 이 방식의 전부입니다.
찍는 건 열 줄입니다
Playwright로 브라우저를 띄우고 스크린샷을 찍습니다.
import { chromium } from 'playwright';
const b = await chromium.launch();
const p = await b.newPage({
viewport: { width: 1600, height: 640 },
deviceScaleFactor: 2,
});
for (const name of ['two-date-formats', 'title-to-url']) {
await p.goto(`file:///.../${name}.html`);
await p.evaluate(() => document.fonts.ready);
await p.waitForTimeout(1400);
await p.screenshot({ path: `${name}.png` });
}
await b.close();
deviceScaleFactor: 2가 중요합니다. 1600×640짜리 화면을 2배로 찍어 3200×1280 PNG가 나옵니다. 고해상도 화면에서 글자가 흐려지지 않습니다.
base.css 하나로 묶습니다
색, 글꼴, 캔버스 크기는 공통 파일에 둡니다.
:root {
--bg: #1e1f2e;
--card: #2a2b3f;
--fg: #f3f0e7;
--accent: #e3b567;
}
html, body { width: 1600px; height: 640px; }
.wrap { padding: 40px 56px 34px; height: 640px;
display: flex; flex-direction: column; }
그림마다 제목 위치가 제각각이면 글을 읽을 때 눈이 피곤합니다. 캔버스 크기와 여백을 여기서 고정해두니 스물몇 장이 한 세트처럼 보입니다.
색을 바꾸고 싶으면 이 파일 하나만 고치면 됩니다. 스물몇 장이 같이 바뀝니다.
이 방식이 좋은 진짜 이유
무엇보다 고치기가 쉽습니다.
도식에서 오타를 하나 발견했다고 해봅시다. 이미지 파일이라면 원본을 찾아서 편집기를 열고 같은 글꼴을 찾아 다시 쓰고 다시 내보내야 합니다. 원본을 잃어버렸으면 처음부터입니다.
여기서는 한 줄입니다.
sed -i 's/막한/막힌/' title-to-url.html && node shot.mjs
그리고 그림이 텍스트 파일이니 git에 그대로 들어갑니다. 커밋 기록에 뭐가 바뀌었는지 글자로 남습니다. 이미지 파일은 “바뀜”이라고만 나오죠.
겪은 것 다섯 가지
여기부터가 실제로 시간을 쓴 부분입니다.

1. 글꼴이 늦게 오면 다른 글꼴로 찍힙니다
웹폰트를 CDN에서 받아 쓰는데, 폰트가 도착하기 전에 스크린샷을 찍으면 대체 글꼴로 찍혀버립니다. 화면에서는 멀쩡해 보이는데 PNG만 이상합니다.
await p.evaluate(() => document.fonts.ready);
await p.waitForTimeout(1400);
fonts.ready만으로는 가변 폰트가 실제로 그려지기 전에 넘어갈 때가 있어서 여유를 좀 더 뒀습니다. 1.4초는 감으로 정한 값입니다.
2. 캔버스를 넘으면 조용히 잘립니다
내용을 추가하다 보면 640px를 넘어갑니다. 그런데 스크린샷은 정해진 크기로 찍히니까 넘친 부분이 그냥 없어집니다. 경고도 없습니다.
그래서 찍을 때마다 잽니다.
const o = await p.evaluate(() => ({
h: document.body.scrollHeight,
w: document.body.scrollWidth,
}));
const over = o.h > 640 || o.w > 1600;
console.log(name, o, over ? '⚠ 넘침' : 'ok');
이 두 줄이 없었으면 잘린 그림을 몇 번은 올렸을 겁니다. 실제로 이번 글 도식도 처음 찍었을 때 744px이 나왔습니다.
3. flex 안에서는 굵은 글씨가 줄을 바꿉니다
표 칸을 세로 가운데 정렬하려고 이렇게 했습니다.
.td {
display: flex;
flex-direction: column;
justify-content: center;
}
그랬더니 칸 안의 내부 링크를 <b>최종 주소로</b> 적기가 세 줄로 쪼개졌습니다. flex 컨테이너 안에서는 텍스트 조각과 <b>가 각각 별개 항목이 되어 세로로 쌓입니다.
<span>으로 한 번 감싸면 됩니다.
<div class="td">
<span>내부 링크를 <b>최종 주소로</b> 적기</span>
</div>
한 번 알고 나면 별것 아닌데, 모르면 “왜 여기만 줄이 바뀌지” 하면서 한참 봅니다.
4. 인코딩을 브라우저 추측에 맡기지 않습니다
한 번은 PNG에서 한글이 깨져 나왔습니다. <meta charset="utf-8" />을 안 적었더군요. 추가하니 사라졌습니다.
그런데 이 글을 쓰면서 다시 재현해보려니 안 깨졌습니다. 브라우저가 내용을 보고 인코딩을 알아서 맞춘 겁니다.
바로 이게 무서운 부분입니다. 대체로 맞으니까 평소엔 모르고, 추측이 빗나가는 날에만 깨집니다. 한 줄 적어두면 추측할 일이 없어집니다.
5. 어떤 한글은 굵게 하면 겹쳐 보입니다
도식 아래 설명에 “여러 번 실행돼도 괜찮게”라고 굵게 썼는데, ‘찮’의 받침이 겹쳐 보였습니다. 본문 굵기에서는 멀쩡한데 800 무게에서만 그랬습니다.
글꼴 문제라 제가 고칠 수 있는 게 아니어서 문구를 바꿨습니다. “문제없게”로요. 뜻이 같으면 이게 제일 빠릅니다.
찍고 나서 눈으로 한 번 보는 과정이 필요한 이유이기도 합니다. 수치로는 안 걸립니다.
이 방식이 못 하는 것
솔직하게 적어두겠습니다.
자유롭게 그리는 건 못 합니다. 곡선 화살표, 손그림 느낌, 일러스트 같은 건 CSS로 하려면 고생스럽습니다. 제 도식이 죄다 네모와 표인 건 취향이기도 하지만 이 방식이 잘하는 게 그거라서이기도 합니다.
그림 속 글자는 검색도 복사도 안 됩니다. PNG니까요. 독자가 도식의 코드 조각을 복사하고 싶어도 못 합니다.
그래서 도식에 들어간 내용은 본문에도 글로 한 번 더 적습니다. 그림만 보면 알 수 있는데 글에는 없는 정보가 생기지 않게요.
그리고 대체 텍스트를 길게 씁니다. 이 블로그의 글머리에는 대표 이미지 설명 칸이 따로 있습니다.
heroImageAlt: 도식을 만드는 과정.
그림마다 HTML 파일 하나를 쓰고
공통 base.css 를 불러와 ...
화면 낭독기를 쓰는 분과 검색엔진이 읽는 자리입니다. 도식이 정보를 담고 있으면 그 정보를 글로도 풀어놔야 합니다. 장식용 그림이면 비워두는 게 맞고요.
정리
도구를 새로 배우지 않아도 되는 쪽을 골랐습니다. CSS는 어차피 쓰던 거고, 그리기 도구는 새로 익혀야 합니다. 잘 만들어진 도구들이 많지만, 저는 일주일에 두 장씩 그리는 사람이라 익히는 시간이 안 빠집니다.
고치는 비용이 낮으면 더 많이 고칩니다. 오타 하나 고치자고 편집기를 열어야 하면 그냥 둡니다. sed 한 줄이면 고칩니다. 도구가 결과물의 완성도를 정하는 지점이 여기라고 생각합니다.
눈으로 보는 단계는 못 없앱니다. 넘침은 숫자로 잡히지만 글자가 겹쳐 보이는 건 안 잡힙니다. 자동화해도 마지막엔 사람이 한 번 봐야 합니다.
참고한 문서
- Playwright 문서 — browser.newPage() (viewport, deviceScaleFactor) — 2배 해상도로 찍는 설정
- Playwright 문서 — page.screenshot() — fullPage가 기본으로 꺼져 있어 캔버스 밖은 잘림
- MDN — FontFaceSet.ready — 글꼴 로딩을 기다리는 방법
- W3C CSS Flexbox 1 — Flex Items — flex 안에서 글자 조각과
<b>가 따로 떨어지는 이유
댓글
로그인도 이메일도 필요 없습니다. 이름만 적고 편하게 남겨주세요.
지금은 댓글을 불러올 수 없습니다. 잠시 뒤에 다시 시도해 주세요. 급한 내용이면 ryuma@kakao.com로 보내주셔도 됩니다.