2분, 명령 하나
이 페이지의 마지막 수정은 편집기에서 나와 명령 하나를 거쳐 약 2분 뒤에 공개되었습니다. 그 사이에 로그인을 요구한 화면은 없었고, 발행 버튼을 누른 사람도 없습니다.
그게 핵심입니다. 우리는 인스타그램을 크롤링하는 이야기, 크리에이터를 정렬하는 기준, 새벽 세 시에 깨지는 것들에 대해 씁니다. 글 쓰는 비용이 그 일보다 비싸면 안 됩니다. 그래서 블로그도 우리가 하루 종일 쓰는 도구 위에서 돌아갑니다. 텍스트 파일, git, 그리고 빌드.
37초
전체 빌드
3
언어
0
데이터베이스
글 하나가 폴더 하나
글 하나에 필요한 모든 것이 디렉터리 하나에 들어 있고, 저장소의 나머지는 그 존재를 몰라도 됩니다.
apps/blog/content/how-virev-blog-works/
├── en.md
├── ko.md
├── ja.md
├── components/
│ └── DeployFlow.svelte
├── topology.svg
└── style.css로케일 파일이 본문입니다. 그 옆에는 이 글에 필요한 것만 놓습니다. 다이어그램 하나를 위해 쓴 스벨트 컴포넌트, 이 페이지에서만 불러오는 스타일시트, 이 차트만 읽는 CSV 파일.1 글이 끝나면 그 코드는 다른 누구에게도 더는 부담이 되지 않습니다.
언어는 파일이 있고 프런트매터에 draft: true가 없을 때 공개됩니다. 이 규칙 하나가 목록과 사이트맵, RSS 피드, 언어 링크를 모두 결정합니다. 그래서 en.md만 있는 폴더는 영어 글이고, 따로 설정할 것이 없습니다.
CMS가 없다는 것의 값
콘텐츠 시스템을 두면 로그인과 데이터베이스가 생기고, 모든 글의 사본이 하나 더 생기며, 필드를 처음 바꾸는 날 마이그레이션이 따라옵니다.2 git은 누가 무엇을 언제 바꿨는지 이미 기록하고, 텍스트 파일은 diff가 깔끔하고, 풀 리퀘스트가 곧 리뷰입니다. 그래서 지금 읽고 있는 이 페이지는 파일이고, 이 문장의 역사는 커밋입니다.
대가는 저장소 체크아웃입니다. 여기에 글을 쓰려면 체크아웃이 필요하고, git을 쓰지 않는 필자는 쓸 수 없습니다.
알고 선택한 맞교환입니다. 여기에 글을 쓰는 사람은 이미 저장소를 열어 두고 있습니다.
마크다운에서 정적 페이지까지
.md 파일은 mdsvex가 스벨트 컴포넌트로 컴파일합니다. 그래서 문단 옆에 차트가 설 수 있고, 파일이 바로 옆에 있는 컴포넌트를 불러올 수 있습니다. 그다음 SvelteKit이 빌드 시점에 모든 페이지를 HTML로 렌더링합니다. 요청 시점에는 서버가 없고 CDN 위의 파일만 있습니다.
그 빌드에서 독자가 알아채는 일이 두 가지 있습니다. 코드는 Shiki가 색을 입혀서 평범한 HTML로 도착하고, 브라우저에서는 하이라이터가 돌지 않습니다.3이 페이지의 클라이언트 스크립트는 여백 주석의 위치를 잡고 목차를 따라가는 일만 합니다. 차트가 있는 글은 그때만 ECharts를 추가로 불러옵니다. 그리고 마크다운 각주는 오른쪽 여백의 주석이 됩니다. 작은 플러그인이 [^1]을 번호가 붙은 참조로 바꾸고, 주석을 그 문장 옆으로 옮깁니다. 자바스크립트 없이도 HTML은 완전합니다. 스크립트는 참조가 어디에 놓였는지 재기만 합니다.
무엇을 공개할지 정하는 규칙은 전부 읽어도 짧습니다.
export function isPublished(entry: Entry): boolean {
return !entry.meta.draft;
}
/** Locales of one article whose file exists and is not a draft. */
export function publishedLocales(entries: Entry[], slug: string): Locale[] {
const set = new Set(
entries.filter((e) => e.slug === slug && isPublished(e)).map((e) => e.locale)
);
return LOCALES.filter((l) => set.has(l));
}배포가 이루어지는 방식
네 단계이고, 그중 양식을 채우는 단계는 없습니다.
- 1 커밋
- 2 푸시
- 3 빌드
- 4 공개
commit.mjs는 지정한 파일만 커밋합니다.
커밋. node scripts/repo/agent/commit.mjs -m "post(<slug>): title" apps/blog/content/<slug>는 지정한 파일만 스테이징합니다. 열몇 개의 에이전트가 이 체크아웃을 공유하기 때문에, 전부를 담는 커밋은 남이 쓰다 만 작업까지 끌고 갑니다.
푸시. git push origin sieun/dev. 푸시는 fast-forward여야 합니다. 거부되면 누군가 먼저 푸시한 것이니 fetch 후 다시 푸시합니다. 아무도 손으로 머지하지 않습니다.
빌드. Vercel은 먼저 ignored-build 단계를 실행해서, 이번 푸시에 apps/blog가 바뀌지 않았으면 빌드를 건너뜁니다. 바뀌었으면 블로그 전체를 약 40초 만에 다시 빌드합니다.
공개. 새 빌드가 성공하면 이전 빌드를 대체하고, URL은 그대로입니다.
깨질 때
깨진 글은 빌드를 실패시키고, 실패한 빌드는 배포되지 않습니다. 이전 버전이 그대로 떠 있고 로그가 어느 파일인지 알려 줍니다. 그게 안전망입니다. 최악의 경우는 새 글이 안 올라가는 것이지, 사이트가 내려가는 것이 아닙니다.
공개한 글을 되돌리는 것은 git revert 후 푸시하거나, Vercel 대시보드에서 클릭 한 번입니다. 읽기 화면 자체나 여백 주석, 타입 스케일을 바꾸면 같은 배포에서 모든 글이 다시 빌드됩니다. 템플릿은 한 벌이고 글은 그 템플릿을 지나가는 데이터이기 때문입니다.
세 가지 언어, 각자의 속도로
en.md, ko.md, ja.md는 컴포넌트와 데이터와 다이어그램을 공유하고, 공개 시점은 각자 정합니다. 위의 언어 전환기는 이 글이 실제로 가진 언어만 보여 주고, hreflang 링크도 같은 목록을 따릅니다. 그래서 검색 엔진이 아직 없는 번역을 가리키는 일은 생기지 않습니다.
한국어판을 쓰는 일은 영어 파일을 복사해서 본문을 옮기는 일입니다. 차트는 그대로 차트입니다.