Back to automation
automation 9.6 min read 450 lines

tistory-publisher

Tistory 블로그 자동 발행 — 쿠키 기반 내부 API를 사용하여 글 작성/발행

Tistory 자동 발행 스킬

개요

Tistory 공식 Open API가 종료된 후, 브라우저 내부 API를 사용하여 Python으로 블로그 글을 자동 발행합니다.

사전 요구사항

  • Tistory 블로그 계정
  • 쿠키 파일: $TISTORY_COOKIE_PATH (예: secrets/tistory.env)
  • Python requests 라이브러리

쿠키 획득 방법

  • Chrome에서 https://www.tistory.com 로그인
  • 브라우저에서 글쓰기 후 [발행] 클릭
  • F12 → Network 탭 → "로그 유지" 체크
  • post.json 요청 우클릭 → Copy → Copy as cURL
  • 쿠키 값 추출 후 $TISTORY_COOKIE_PATH에 저장

쿠키 필드

| 쿠키 | 환경변수명 | 설명 |
|------|-----------|------|
| REACTION_GUEST | TISTORY_REACTION_GUEST | 게스트 토큰 |
| \_\_T\_ | TISTORY\_\_\_T\_ | 로그인 플래그 |
| \_\_T\_SECURE | TISTORY\_\_\_T\_SECURE | 보안 플래그 |
| TSSESSION | TISTORY_TSSESSION | 세션 ID |
| \_T\_ANO | TISTORY\_\_T\_ANO | 인증 토큰 (주기적 갱신 필요) |

⚠️ \_T\_ANO 쿠키는 시간이 지나면 만료됩니다. 발행 실패 시 다시 쿠키를 갱신하세요.

API 명세

글 발행

POST https://{blog}.tistory.com/manage/post.json
Content-Type: application/json
Cookie: REACTION_GUEST=...; __T_=1; __T_SECURE=1; TSSESSION=...; _T_ANO=...

요청 본문 (JSON)

{
"id": "0",
"title": "글 제목",
"content": "

HTML 본문

",
"slogan": "",
"visibility": 0,
"category": 0,
"tag": "태그1,태그2",
"published": 1,
"password": "8자리문자",
"uselessMarginForEntry": 1,
"cclCommercial": 0,
"cclDerive": 0,
"type": "post",
"attachments": [],
"recaptchaValue": "",
"draftSequence": null
}

응답

성공:

{"entryUrl": "https://blog.tistory.com/123"}

실패:

{"data": null, "message": "에러 메시지"}

파라미터 설명

| 파라미터 | 설명 | 값 |
|----------|------|-----|
| id | "0"=신규, 기존 ID=수정 | string |
| visibility | 공개 범위 | 20=공개, 0=비공개 (2026-04-08 검증) |
| category | 카테고리 ID | 0=카테고리 없음 |
| published | 발행 여부 | 1=발행, 0=임시저장 |
| type | 글 타입 | "post" 또는 "page" |
| password | 랜덤 8자리 | md5 해시 일부 |
| uselessMarginForEntry | 여백 설정 | 1 (고정) |

이미지 업로드

POST https://{blog}.tistory.com/manage/post/attach.json
Content-Type: multipart/form-data
Cookie: (same as post endpoint)

요청: file 필드에 이미지 파일 전송

응답:

{"name":"image.jpg", "url":"https://blog.kakaocdn.net/dna/...", "key":"...", "filename":"img.jpg", "size":12345}

이미지 삽입 (GitHub 호스팅)

Tistory CDN 업로드(attach.json)는 불안정하므로 사용하지 마라. GitHub 저장소를 이미지 호스팅으로 사용한다.

이미지 저장소: sigco3111/blog-images (public)
로컬 클론: /tmp/blog-images-repo/

이미지 추가 방법:

cd /tmp/blog-images-repo && git pull
cp /path/to/image.jpg /tmp/blog-images-repo/{이름}.jpg
cd /tmp/blog-images-repo && git add {이름}.jpg && git commit -m "Add {이름}" && git push origin main

GitHub raw URL 형식:

https://raw.githubusercontent.com/sigco3111/blog-images/main/{filename}

HTML 삽입:



캡션 텍스트


기타 API

| 엔드포인트 | 설명 |
|-----------|------|
| GET /manage/posts.json?category=-3&page=1 | 글 목록 |
| GET /manage/category.json | 카테고리 목록 |
| GET /manage/post/{id}.json | 글 상세 (수정 시) |
| PUT /manage/post/{id}.json | 글 수정 |

스크립트

위치

scripts/tistory_publisher.py

사용법

# 세션 체크
python3 tistory_publisher.py --check

카테고리 목록


python3 tistory_publisher.py --categories

HTML로 발행 (공개)


python3 tistory_publisher.py \
--title "제목" \
--content "

HTML 내용

" \
--tags "태그1,태그2" \
--visibility public

Markdown 파일로 발행 (비공개)


python3 tistory_publisher.py \
--file post.md \
--visibility private

특정 카테고리에 발행


python3 tistory_publisher.py \
--title "제목" \
--content "

내용

" \
--category 12345

Markdown 파일 형식

# 글 제목 (H1이 제목으로 사용됨)

본문 내용입니다.

소제목

  • 리스트 항목 1
  • 리스트 항목 2

tags: 태그1,태그2

자동 발행 파이프라인

크론 잡에서 자동으로 실행되는 7단계 파이프라인:

  • 주제 선정blog_pipeline.pyNotion 긱뉴스 주제를 1순위로 주제 결정 (트렌드 히스토리는 fallback). 할당량 제한 없음 (무제한 발행, 2026-04-21 변경)
  • 뉴스 검색 — 주제 관련 최신 기사 3-5개 수집
  • 글 작성 — 가이드라인에 따라 1500자+ 블로그 글 작성
  • SEO 최적화blog_seo_optimizer.py로 제목/태그/메타데이터 최적화
  • 품질 관리blog_quality_gate.py로 품질 평가
  • Tistory 발행 — 품질 통과 시 tistory_publisher.py로 발행

주제 소스 (2026-04-20 변경)


  • 1순위: 긱뉴스 (Notion DB) — 매일 13:00 RSS 수집하여 8개 주제를 Notion에 저장
  • 2순위: 트렌드 히스토리 — HN/GitHub/RSS 트렌드 (fallback)
  • Notion DB ID: 34276f2e-9097-811e-ab69-f8759ecf0be7 (출처=긱뉴스, 상태=활성)

관련 스크립트


  • scripts/tistory_duplicate_checker.py — 중복 발행 체커 (★ 최우선 실행)
  • scripts/blog_pipeline.py — 파이프라인 오케스트레이터
  • scripts/trend_collector.py — 트렌드 수집
  • scripts/blog_seo_optimizer.py — SEO 최적화
  • scripts/blog_quality_gate.py — 품질 관리

카테고리별 Tistory 매핑 (할당량 무제한)


| 카테고리 | Tistory ID | 블로그 카테고리명 |
|----------|------------|---|
| AI/ML | 1219746 | AI 뉴스 |
| iOS/Swift | 1219747 | 개발 팁 |
| 개발도구 | 1219748 | 자동화&툴 리뷰 |
| 투자/경제 | 1219750 | 투자&경제 |
| 아이디어 | 1219752 | 아이디어 |
| 기타 | 1219751 | 기타 |

시간대별 선호 카테고리


  • 아침(7-10시): AI/ML, 투자/경제 (뉴스 성격)
  • 저녁(19시-새벽2시): AI/ML, iOS/Swift, 개발도구 (기술 심층)

중복 방지 시스템 (5단계)

파이프라인은 5단계로 중복 발행을 방지합니다:

  • 전용 중복 체커 (tistory_duplicate_checker.py) — ★ 최우선
- 크론 잡 발행 전 반드시 실행하여 중복 검증
- SEO 히스토리 50건 + published_topics 해시 + Tistory API 실제 글 확인
- 키워드 유사도 40%+ 또는 고유명사 2개 이상 공유 시 중복 판정
- --title "제목"is_duplicate: true/false JSON 반환 (exit code 1=중복)
- --recent 20 → 최근 발행글 목록 출력

  • 파이프라인 주제 dedup (blog_pipeline.py)
- published_topics.json에 해시 기록 → 같은 주제 재선정 불가
- check_tistory_duplicate()로 SEO 히스토리 전체(7일 lookback)와 비교
- 최근 48시간 발행 주제와 키워드 유사도 50% 초과 시 skip
- 모든 카테고리 할당량 소진 시 전체 skip

  • SEO 중복 체크 (blog_seo_optimizer.py) — 한국어 완벽 지원
- is_duplicate() 4단계 체크:
- L1: 해시 매치 (전체 히스토리, 기간 무관)
- L2: 의미 단어 오버랩 50%+ (한국어 포함, SEO 불용어 제외)
- L3: 핵심 키워드 2개 이상 공유
- L4: 특정 엔티티 매치 (Cloudflare, Wrangler 등 고유명사)
- _parse_post_timestamp()로 timestamp/date 필드 모두 지원
- timestamp 없는 게시물은 보수적으로 중복 대상에 포함

  • 크론 프롬프트 가드
- "반드시 1편 발행" 지시 없음 — 중복이면 발행 포기가 최우선
- 발행 전 반드시 tistory_duplicate_checker.py로 중복 확인
- 파이프라인 skip 시 다른 카테고리 최대 2회 재시도 후, 그래도 없으면 발행 포기
- 절대 임의 주제를 수동 선정하지 않음 — 중복 발행의 유일한 원인

  • 수동 선정 금지
- 파이프라인이 모든 카테고리에서 skip하면 → 당회 발행 포기 (정상 동작)
- 에이전트가 임의로 주제를 골라 발행하는 것을 엄격히 금지

중복 발생 시 대처


# 오늘 할당량 리셋 (주의: 실제 할당량보다 적게 리셋하면 중복 위험)
python3 scripts/blog_pipeline.py --reset-daily

발행 이력 확인


python3 -c "import json; data=json.load(open('$HOME/.hermes/memory/blog_seo_history.json')); [print(p['title']) for p in data['posts'][-10:]]"

published_topics.json 초기화 (모든 dedup 리셋 — 긴급 시에만 사용)


rm memory/published_topics.json

시간 신선도 검증 (★ CRITICAL)

과거 자료를 최신 자료로 오인하여 발행하는 것을 반드시 방지해야 합니다.

❌ 절대 하지 말 것


  • [2026 최신] 같은 연도 하드코딩 사용 → 현재 연도가 바뀌면 오정보가 됨
  • "Gemini 2.0 완벽가이드 2026년" 같이 구버전 제목에 현재 연도를 붙이는 것
  • 2년 이상 된 기술을 "최신"으로 포장하는 것

✅ 올바른 접근


  • 제목에는 버전/연도 없이 기술명만 사용: "Gemini 완벽 가이드"
  • 본문에서는 반드시 최신 버전을 기준으로 작성
  • 내용에 버전/연도를 명시할 때는 정확한 출시 시점 기재

검증 체크리스트


  • 주제 버전 확인 — 이 기술/모델의 최신 버전이 무엇인지 검색 후 작성
  • 내용 연도 검증 — 본문에 언급된 연도/버전이 현재 유효한지 확인
  • 제목-내용 일치 — "최신/완벽가이드"라고 쓴 제목에 구버전 내용이 없는지 확인

파이프라인 통합


  • blog_pipeline.py: check_topic_timeliness() — 주제 선택 시 구버전 차단
  • blog_quality_gate.py: 연도/버전 검증 + 최신 주장 vs 구버전 내용 모순 감지 (페널티 -10~-15점)
  • blog_seo_optimizer.py: 하드코딩 연도 프리픽스 사용 금지

품질 관리 시스템

발행 전 글 품질을 5차원으로 평가하여, 기준 미달 시 자동 스킵 또는 재작성을 권장합니다.

평가 항목

| 항목 | 가중치 | 평가 내용 |
|------|--------|----------|
| 내용 품질 | 30% | 본문 길이, 구조, 단락 수, 도입/결론 |
| 가독성 | 15% | 문장 길이, 포맷팅, 시각적 구조 |
| 독창성 | 20% | 반복/스팸 감지, 어휘 다양성, 기존 글과 유사도 |
| SEO | 20% | 제목 최적화, 태그, 링크, 메타 설명 |
| 시간 신선도 | 15% | 연도/버전 정확성, 최신 주장과 내용 일치 (CRITICAL) |

등급 기준

| 등급 | 점수 | 결정 |
|------|------|------|
| S | 85+ | 🌟 최상급 — 즉시 발행 |
| A | 70-84 | ✅ 양호 — 발행 |
| B | 55-69 | 📝 보통 — 발행 가능 |
| C | 40-54 | ⚠️ 재작성 권장 |
| D | <40 | 🔁 새 주제로 재시도 (최대 2회) |

사용법

# 글 품질 평가
python3 scripts/blog_quality_gate.py \
--title "글 제목" \
--content "본문 내용" \
--tags "AI,LLM,GPT" \
--category "AI/ML"

파일로 평가


python3 scripts/blog_quality_gate.py --file post.md

품질 통계 확인


python3 scripts/blog_quality_gate.py --stats

현재 설정 확인


python3 scripts/blog_quality_gate.py --config

크론 잡에서의 통합

파이프라인에서 글 작성 완료 후, 발행 전에 반드시 품질 평가를 수행:

# 6단계: 품질 평가
QUALITY_RESULT=$(python3 scripts/blog_quality_gate.py --title "$TITLE" --content "$CONTENT" --tags "$TAGS" --category "$CATEGORY")
GRADE=$(echo "$QUALITY_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['grade'])")
DECISION=$(echo "$QUALITY_RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin)['decision'])")

if [ "$DECISION" = "retry_with_new_topic" ]; then
echo "품질 미달 ($GRADE 등급) — 새 주제로 재시도"
# 파이프라인 2단계로 돌아가서 새 주제 선택
python3 scripts/blog_pipeline.py
# 새 주제로 3~6단계 재실행 (최대 2회)
exit 0
elif [ "$DECISION" = "rewrite" ]; then
echo "품질 부족 ($GRADE 등급) — 재작성 권장"
# 발행을 진행하지 않고 다음 사이클로
exit 0
fi

7단계: 발행 진행


python3 scripts/tistory_publisher.py --title "$TITLE" --content "$HTML_CONTENT" --tags "$TAGS"

품질 기준 설정

# 설정 확인
python3 scripts/blog_quality_gate.py --config

설정 파일 직접 수정: memory/blog_quality_config.json


주요 설정값:


min_score: 60 # 최소 통과 점수


auto_skip_grade: "D" # 자동 스킵 등급


rewrite_grade: "C" # 재작성 권장 등급


min_content_length: 1500 # 최소 본문 길이


이미지 삽입 규칙 (GitHub 호스팅)

Tistory CDN 업로드(attach.json)는 불안정하므로 사용하지 마라. GitHub 저장소를 이미지 호스팅으로 사용한다.

이미지 저장소: sigco3111/blog-images (public)
로컬 클론: /tmp/blog-images-repo/

파일명 규칙 (중복 방지 ★)


파일명은 YYYYMMDD-{영문키워드}-{3자리해시} 형식을 사용한다. 해시는 제목이나 키워드에서 생성하여 중복을 원천 차단한다.
예시: 20260422-worldmonitor-a7f.jpg
예시: 20260422-flux-imagegen-b3c.png

이미지 추가 방법


cd /tmp/blog-images-repo && git pull
cp /path/to/image.jpg /tmp/blog-images-repo/20260422-{키워드}-{해시}.jpg
cd /tmp/blog-images-repo && git add . && git commit -m "Add 20260422-{키워드}-{해시}" && git push origin main

GitHub raw URL 형식


https://raw.githubusercontent.com/sigco3111/blog-images/main/{filename}

HTML 삽입




캡션 텍스트


크론 잡 필수 설정 (★ CRITICAL)

크론 잡이 정상 발행하려면 다음 두 가지가 반드시 필요합니다:

  • 스킬 명시적 연결: 크론 잡 설정에 skills: ["tistory-publisher"]가 포함되어야 합니다. skills: []이면 프롬프트에 스킬 로드 지시가 있어도 크론 환경에서 인식되지 않습니다.
  • terminal 도구 강제 사용: 크론 프롬프트에 "무조건 execute_code 또는 terminal 도구를 사용하여 스크립트를 실행하라"는 명시적 지시가 필요합니다. 일부 모델은 도구가 있다는 것을 인지하면서도 시도하지 않고 [SILENT]로 종료하는 문제가 있습니다 (2026-04-18에 glm-5-turbo에서 확인).

크론 잡 정상 실행 시간


  • 정기 실행: 매일 22:00 KST
  • 수동 실행: hermes cron run

트러블슈팅

| 문제 | 원인 | 해결 |
|------|------|------|
| HTTP 500 | 쿠키 만료 | 브라우저에서 새 쿠키 복사 |
| 세션 만료 메시지 | TSSESSION 만료 | 재로그인 후 쿠키 갱신 |
| Content-Type 오류 | 반드시 application/json | form-urlencoded 사용 금지 |
| 글 발행 안 됨 | needCaptcha: true | 일정 시간 후 재시도 |
| 파이프라인 skip | 주제 소진 (할당량 없음) | 긱뉴스/트렌드 데이터 확인, --reset-daily로 카운터 리셋 |
| SEO 등급 C 이하 | 본문 길이 부족 | 1500자 이상으로 수정 |
| 중복 발행 | published_topics.json 누락 | 파일 존재 여부 확인 후 record_published_topic() 호출 |
| 크론 실행만 되고 발행 안 됨 | skills: [] 또는 프롬프트에 terminal 강제 지시 없음 | 크론 잡에 tistory-publisher 스킬 연결 + 프롬프트 수정 |
| 모델이 도구 시도 안 하고 SILENT 종료 | 모델이 스스로 "터미널 안 된다"고 오판 | 프롬프트에 "무조건 execute_code 사용" 명시적 지시 추가 |

블로그 정보

  • 블로그: sigco.tistory.com (ICBM의 Dev 블로그)
  • Blog ID: 5587853

Related Skills / 관련 스킬

agent-benchmark-tracker

AI 에이전트/모델 벤치마크 결과를 추적하여 Notion에 기록 — SWE-bench, HumanEval, GAIA, WebArena, LiveCodeBench 등

agentnews-monitor

AgentNews 실시간 모니터링 — 매시간 AI 에이전트 뉴스 피드를 확인하고 관심사 매칭 뉴스를 threshold 기반으로 알림 (하루 2~3건 제한)

ai-model-tracker

AI 모델 릴리즈/업데이트를 매일 Notion에 기록 — 새 모델 출시, 벤치마크, 가격 변동 추적

auto-researcher

심층 자동 조사 — 주제를 받아 여러 소스에서 수집 후 종합 리포트 작성