기술을 하나씩 비교해서 고른 게 아니다. 1편에서 적은 요구사항 중 검색에 걸릴 것, 내 어드민에서 쓰고 고치고 발행할 것, 화면을 마음대로 바꿀 수 있을 것 세 가지가 후보를 거의 다 걸러냈다. 비교표는 그 뒤를 확인하는 용도였다. 이 편은 무엇이 무엇을 정했는지 순서대로 적는다.
1. 요구사항이 후보를 걸렀다
1.1 검색에 걸려야 한다
광고 수익이 목적인 이상 검색 유입이 없으면 아무 의미가 없다. 그래서 처음부터 SEO와 속도를 최우선에 뒀다.
이 조건 하나로 방향이 정해졌다. 읽는 사람에게 필요한 건 완성된 HTML이지 브라우저에서 조립되는 화면이 아니다. 미리 만들어 둔 정적 파일로 가기로 했다. 검색 엔진이 읽기 쉽고, 빠르고, 서버가 필요 없다.
여기에 한국어와 영어를 같이 낼 계획이었으니 다국어 라우팅도 조건에 들어갔다.

1.2 어드민에서 쓰고 고치고 발행해야 한다

1편에서 남의 플랫폼을 접은 이유가 이것이다. 글을 쓰는 화면, 고치는 화면, 발행을 승인하는 화면이 내 쪽에 있어야 했다.
그러려면 정적 사이트만으로는 안 된다. 글을 담을 데이터베이스와, 그걸 다루는 관리 화면이 따로 필요하다. 블로그와 어드민을 분리된 두 덩어리로 만들기로 한 게 이 지점이다.
1.3 화면을 내 마음대로 바꿀 수 있어야 한다
이건 경험에서 나온 조건이다. 전에 깃 기반 CMS(Decap)를 쓰다가 화면을 못 바꿔서 막힌 적이 있다. 대시보드를 하나 붙이고 싶었는데 방법이 없었다.

이번엔 분석 지표 대시보드, AI 검수 결과 패널처럼 기성 CMS에 없는 화면이 처음부터 계획에 있었다. "이 도구가 그걸 지원하나?"를 매번 확인해야 하는 구조는 후보에서 뺐다.
2. 조각을 하나씩 채웠다
2.1 블로그 — Astro
정적 사이트 생성기 후보가 다섯 개였다. Astro, Next.js, Hugo, Docusaurus, Eleventy.
Docusaurus는 문서 사이트 틀이 강해서 원하는 디자인을 내기 어려웠다(1.3 위반). Eleventy는 가볍지만 다국어도 목차도 직접 만들어야 했다. Hugo는 빌드가 제일 빠르지만 인터랙티브한 요소를 붙이기가 번거로웠다.
남은 건 Astro와 Next.js였고, 둘 다 조건은 만족했다. 여기서는 배포가 갈랐다 — 아래 2.3에서 이어진다.
2.2 어드민 — Payload
1.3이 사실상 답을 정했다.
Payload는 어드민 화면 자체가 내 코드다. 화면을 추가하는 게 "이 CMS가 그걸 지원하나?"가 아니라 그냥 페이지를 하나 더 만드는 일이 된다. 2편에서 편집기를 통째로 새로 만들 수 있었던 것도 이 구조 덕분이다.
Strapi나 Directus도 셀프호스팅이 되지만 커스텀 화면을 붙이려면 플러그인 작업이 필요했다. 깃 기반 CMS는 애초에 1.2를 만족하지 못한다 — 분석 대시보드나 상태 관리 워크플로를 올릴 자리가 없다.
2.3 배포 — Cloudflare
배포처에는 조건이 세 개 붙었다.
- 블로그는 맥북과 무관하게 24시간 떠 있어야 한다 — 어드민은 집 맥북에서 돌리기로 했으니, 맥북이 꺼져도 독자는 아무 영향이 없어야 한다
- 비용은 0원에 가까워야 한다
- 밖에서 어드민에 들어갈 수 있어야 한다 — 포트를 열거나 공인 IP를 받지 않고
Cloudflare 는 이 셋을 한 곳에서 해결한다. 정적 사이트 호스팅과 CDN이 무료고, 바깥에서 집 안 서버로 들어오는 인증된 통로(터널)와 그 앞을 막는 인증(Access)도 무료로 준다. 도메인도 마침 거기서 샀으니 연결할 것도 없었다.
그리고 여기서 Astro와 Next.js가 갈렸다. Next.js를 클라우드플레어에 올리려면 어댑터를 하나 끼워야 하고 그러고도 일부 기능은 온전히 돌지 않는다. Astro는 결과물이 그냥 정적 파일이라 끼울 게 없다. 마찰이 없는 쪽을 골랐다.
3. 데이터는 어디에 두나
3.1 글은 SQLite 한 파일에
클라우드 DB를 붙일 이유를 못 찾았다. 쓰는 사람이 한 명이고 서버도 하나라 여러 곳에서 동시에 쓰는 상황이 없다. 글이 수천 개 쌓여도 텍스트라 수백 메가 수준이다. 파일 하나라 백업도 그 파일만 복사하면 된다.
3.2 이미지는 옮기기로 해놓고 아직 안 옮겼다
이미지는 계속 쌓이면 디스크를 먹으니 클라우드 저장소(R2)에 두기로 정했다. 그런데 아직 안 옮겼다. 지금은 빌드할 때 이미지 폴더를 결과물에 복사해서 같이 올린다. 그렇게 해도 잘 돌아가고, 비용이 0원이고, 옮기려면 결제 수단을 등록해야 한다.
나중에 옮기기 쉽게는 해뒀다. 이미지 주소를 바꾸는 코드가 한 군데에 있어서, 그 한 곳만 고치면 이미 쓴 글은 건드릴 필요가 없다.
4. 전체 그림
flowchart LR
subgraph mac["집 맥북 (24시간)"]
admin["어드민<br/>Payload + Next.js"]
db[("SQLite")]
ai["AI 작업<br/>구독 사용"]
admin --- db
ai --- admin
end
subgraph cf["Cloudflare"]
pages["Pages<br/>블로그 정적 파일"]
tunnel["Tunnel + Access"]
end
admin -->|발행하면 빌드해서 올림| pages
reader(("독자")) --> pages
me(("폰에서 나")) --> tunnel --> admin
핵심은 블로그와 어드민이 끊어져 있다는 점이다. 맥북이 꺼져 있어도 블로그는 그대로 서비스된다. 맥북은 글을 쓰고 배포를 밀어 넣을 때만 필요하다.
밖에서 어드민에 들어갈 때만 터널을 지난다. 서버를 클라우드로 옮기는 게 아니라 인증된 통로만 뚫어주는 방식이다. 이건 6편에서 자세히 쓴다.
5. 이 구조의 약점
맥북이 꺼져 있으면 글을 쓸 수 없다. 독자에게는 아무 영향이 없지만 나는 아무것도 못 한다.
설계 문서에도 이 위험을 적어뒀다. "맥북이 꺼지면 그날 예약된 발행이 실행되지 않음."
지금 보면 이 문장은 틀린 걱정이었다. 그때는 매일 새벽 초안이 자동으로 준비되는 구조를 전제하고 있었는데, 그 구조 자체가 나중에 없어졌기 때문이다.
왜 없앴는지는 다음 편에서 쓴다.


