블로그 발행을 자동화해보자 - 티스토리·네이버용 개인 자동화 플랫폼 만들기 (1) 전체 구조 설계
블로그를 꾸준히 쓰려고 마음먹을 때마다 걸리는 게 있습니다. 글감은 있는데, 막상 앉아서 초안을 쓰고 다듬고 티스토리·네이버 두 군데에 각각 로그인해서 붙여넣는 과정이 생각보다 품이 많이 듭니다. "오늘은 좀 바빠서" 하다 보면 어느새 한 달이 비어 있고요.
그래서 이번에 아예 이 과정 자체를 자동화하는 개인용 도구를 만들어봤습니다. 이름하여 blog-auto-writer. 날짜별로 주제만 등록해두면, 정해진 시간에 AI가 제 스타일대로 초안을 쓰고, 브라우저 자동화로 티스토리·네이버에 발행까지 끝내주는 구조입니다. 이 글은 그 시리즈의 첫 편으로, 전체 구조를 어떻게 설계했는지 정리합니다.
왜 만들었나
블로그 자동화라고 하면 흔히 "발행 API로 글을 올리는" 그림을 떠올리기 쉽습니다. 그런데 확인해보니 티스토리 Open API와 네이버 블로그 글쓰기 API가 모두 서비스 종료된 상태였습니다. 즉, 공식적으로 프로그램이 글을 올릴 방법이 없다는 뜻이죠.
이 지점에서 선택지는 두 가지였습니다.
- API가 열려 있는 다른 플랫폼(예: 미디엄, Ghost 등)으로 갈아탄다
- 사람이 브라우저로 하는 행동을 그대로 자동화한다
저는 이미 쓰고 있는 블로그를 옮기고 싶지 않았고, 마침 최근 세션에서 로그인 세션 유지 + 브라우저 조작이 얼마나 안정적으로 되는지 궁금하기도 했어서 2번, 브라우저 자동화로 방향을 잡았습니다.
전체 흐름
설계는 크게 세 덩어리로 나눴습니다.
topics/topics.yaml (주제 등록)
│
▼ 매일 지정 시간 (스케줄러) 또는 수동 실행
Claude API 초안 생성 ──▶ drafts/*.md
│
▼ Playwright (저장된 로그인 세션 사용)
티스토리 / 네이버 블로그 발행 ──▶ published/*.md 로 이동
핵심 아이디어는 각 단계가 파일 하나로 이어진다는 점입니다. 주제는 YAML 파일에, 초안은 마크다운 파일에, 발행 완료 여부는 같은 YAML의 status 필드에 기록됩니다. 데이터베이스도, 별도 서버도 필요 없이 로컬 파일 몇 개로 전체 파이프라인이 굴러갑니다.
왜 이렇게 나눴나 - 관심사 분리
처음엔 "주제 등록 → 초안 생성 → 발행"을 한 함수에 몰아넣을까 고민했는데, 결국 셋을 분리했습니다. 이유는 단순합니다.
- 초안 생성과 발행의 실패 지점이 다릅니다. AI 응답이 이상하게 나오는 것과, 로그인 세션이 만료되는 것은 완전히 다른 문제입니다. 하나로 묶으면 어디서 실패했는지 파악하기 어렵습니다.
- 검수가 필요합니다. AI가 쓴 글을 그대로 올리고 싶지 않을 때가 있습니다. 초안 생성과 발행을 분리해두면, 그 사이에 사람이 끼어들어 내용을 고칠 여지가 생깁니다.
- 재시도가 쉬워야 합니다. 발행만 실패했다면 초안을 다시 만들 필요 없이 발행만 재시도하면 됩니다.
그래서 최종적으로 아래처럼 디렉터리와 역할을 나눴습니다.
config/style.md 글쓰기 스타일 가이드 (AI 프롬프트에 포함됨)
config/accounts.json 계정·스케줄 설정
topics/topics.yaml 날짜별 주제 큐
drafts/ 생성된 초안 (발행 전 검토 가능)
published/ 발행 완료된 글 아카이브
profiles/ 브라우저 로그인 세션 (쿠키)
src/
draft.ts AI 초안 생성
publishers/tistory.ts 티스토리 발행 자동화
publishers/naver.ts 네이버 발행 자동화
scheduler.ts 매일 자동 발행 크론
server.ts 로컬 웹 대시보드
주제 큐 설계 - YAML 하나로 상태 관리
주제 관리는 아주 단순한 YAML 파일 하나로 처리했습니다.
topics:
- date: "2026-07-07"
platform: both # tistory | naver | both
topic: "Playwright로 브라우저 자동화 시작하기"
keywords: [Playwright, 자동화]
status: pending # pending → drafted → published
여기서 재밌는 설계 포인트가 하나 있습니다. platform: both로 등록하면, 파이프라인 내부에서는 이걸 tistory 항목과 naver 항목 두 개로 펼쳐서 처리합니다. 이렇게 하면 "티스토리는 발행됐는데 네이버는 실패"한 상황을 각 플랫폼별로 독립적인 status처럼 다룰 수 있습니다. (실제로는 같은 엔트리를 공유하지만, 처리 로직에서는 플랫폼별로 갈라지는 구조입니다.)
status는 사람이 직접 건드릴 필요가 없습니다. 파이프라인이 각 단계를 마칠 때마다 자동으로 pending → drafted → published로 갱신해줍니다. 만약 발행 중 에러가 나면 failed로 남아서, 다음에 뭐가 문제였는지 바로 알 수 있습니다.
계정 설정도 파일 하나로
티스토리 블로그 이름, 네이버 블로그 아이디, 자동 발행 시각 같은 건 config/accounts.json에 몰아넣었습니다.
{
"tistory": { "blogName": "my-blog", "enabled": true },
"naver": { "blogId": "my_naver_id", "enabled": false },
"schedule": { "publishTime": "09:00", "timezone": "Asia/Seoul" }
}
enabled 플래그를 넣은 이유는, 네이버 연동을 아직 안 끝냈는데 티스토리만 먼저 테스트하고 싶은 상황이 실제로 있었기 때문입니다. 플랫폼 하나를 끄고 켜는 걸 코드 수정 없이 설정 파일로 할 수 있게 해두니, 단계적으로 기능을 검증하기 훨씬 편했습니다.
다음 편에서 다룰 내용
이번 편에서는 "왜 이 구조로 설계했는가"를 정리했습니다. 다음 편부터는 실제로 손을 댄 순서대로:
- 2편: Claude API로 내 스타일에 맞는 초안을 자동 생성하는 방법 (프롬프트 캐싱까지)
- 3편: Playwright로 로그인 세션을 유지하고 티스토리에 실제로 발행하기까지 겪은 시행착오
- 4편: CLI만으로는 아쉬워서 만든 로컬 웹 대시보드
를 다뤄보겠습니다. 특히 3편은 "이론상 되어야 하는데 실제로는 세 번 실패했다"는 이야기라 가장 재밌을 것 같습니다.
backtodev
40대 PM, 다시 개발자로 돌아갑니다. 실패하고 배우며 성장하는 기록.