Task Scheduler로 블로그 자동 발행 완성 | Python 스케줄러 실전
Windows Task Scheduler와 Python으로 블로그 자동 발행 스케줄러를 만든 실전 기록. 삽질 포함 설정 전 과정과 실제 동작 확인까지 솔직하게 공개합니다.
블로그 자동 발행 스케줄러
Slack 트리거, Threads 배포, Notion 기록까지 연결해 놓고 정작 자동 실행이 없었다. Windows Task Scheduler로 Python 스케줄러를 연결해 진짜 ‘무인 자동화’를 완성한 실전 기록.
AI 자동화 부업
📚 이 글은 “개발자가 AI 자동화로 부업하는 실전 기록” 시리즈입니다.
- 📌 1편: AI 자동화로 개발자 부업하는 법 — 실패 3번 하고 나서야 알게 된 것들
- 📌 2편: WordPress + Claude API Day 1 세팅 기록
- 📌 3편: Slack 봇으로 블로그 자동 발행 트리거 만들기 — Day 2
- 📌 4편: Threads API 연동으로 블로그 발행과 SNS 자동 배포 연결하기 — Day 3
- 📌 5편: Notion API로 블로그 발행 기록 자동화하기 — Day 4
- 📌 6편: Windows Task Scheduler로 블로그 자동 발행 스케줄러 만들기 (현재 글)
- 📌 7편: 블로그 SEO 최적화 실전 — Yoast SEO + 키워드 전략부터 검색 상위 노출까지 (예정)
- 📌 8편: 블로그 자동화 2주 운영 결과 공개 — 트래픽, 비용, 배운 것들 (예정)
- 📌 9편: 쿠팡파트너스 가입부터 자동화까지 — 개발자가 수익화 채널 추가하는 실전기 (예정)
- 📌 10편: 구글 애드센스 신청 도전기 — 10개 글 채우고 드디어 신청했다 (예정)
← 5편: Notion API로 블로그 발행 기록 자동화하기 — Day 4
5편까지 마치고 나서 잠깐 뿌듯했다. Slack에서 명령 하나 보내면 Claude가 글을 쓰고, WordPress에 올라가고, Threads에 배포되고, Notion에 기록까지 남는다. 파이프라인이 꽤 그럴듯해 보였다.
근데 다음 날 아침에 블로그를 열어봤더니 글이 하나도 안 올라와 있었다. 당연했다. 내가 Slack 명령을 직접 입력해야만 돌아가는 구조였으니까. 자동화라고 부르기엔 민망했다. 매일 아침 “야, 글 써” 하고 명령 보내는 게 자동화인가? 그건 그냥 내가 편하게 쓰는 CLI 도구에 불과했다.
그래서 Day 5는 처음부터 한 가지만 목표로 잡았다. 내가 아무것도 안 해도 매일 정해진 시간에 글이 올라가는 것. 그 답이 Windows Task Scheduler였다.
왜 Task Scheduler였나 — cron, APScheduler, 클라우드 다 검토했다
솔직히 처음에는 Task Scheduler를 쓸 생각이 없었다. 개발자 커뮤니티에서 Task Scheduler 얘기를 꺼내면 “그게 뭐야, 그냥 cron 써” 같은 반응이 오는 경우가 많아서 나도 좀 무시하는 편이었다. 그래서 선택지를 여러 개 놓고 진지하게 비교해봤다.
| 방법 | 장점 | 단점 | 나한테 맞나? |
|---|---|---|---|
| Linux cron | 가볍고 신뢰성 높음 | Windows 환경에서 WSL 필요, 경로 꼬임 | ❌ 메인 머신이 Windows라 복잡해짐 |
| APScheduler | Python 코드 안에서 관리 | 프로세스가 항상 살아있어야 함, 재시작 시 소멸 | ❌ PC 재부팅마다 수동 실행해줘야 함 |
| GitHub Actions | 무료, 클라우드 실행 | Claude API 키 노출 위험, 무료 제한 있음 | ⚠️ 보안 설정 복잡, 오버스펙 |
| AWS Lambda + EventBridge | 서버리스, 안정적 | 추가 비용, 설정 러닝커브 | ❌ 지금 단계에서 비용 대비 불필요 |
| Windows Task Scheduler | OS 기본 탑재, 재부팅 후에도 유지, GUI 설정 가능 | PC가 켜져 있어야 함 | ✅ 메인 머신 Windows, 항상 켜두는 편 |
결론적으로 나한테는 Task Scheduler가 가장 현실적이었다. 재부팅해도 유지되고, OS가 직접 관리하니까 프로세스 죽을 걱정도 없다. 무엇보다 추가 비용이 0원이다. 부업 초반에 고정비를 최소화하고 싶었던 나한테는 그게 제일 중요했다.
Task Scheduler는 촌스럽지 않다. 재부팅 후에도 살아있고, 무료고, Windows 환경에서는 가장 현실적인 선택이다.
Python 스케줄러 스크립트 준비 — 기존 파이프라인을 하나로 묶기
Task Scheduler가 실행할 Python 스크립트부터 정리해야 했다. 3편~5편에 걸쳐 만들어 둔 기능들(Claude 글 생성 → WordPress 발행 → Threads 배포 → Notion 기록)이 각각 분리된 파일로 흩어져 있었는데, Task Scheduler는 하나의 진입점 파일을 실행하는 구조라 먼저 이걸 publish_pipeline.py 하나로 합쳐야 했다.
그냥 각 모듈을 import해서 순서대로 호출하는 방식으로 정리했다. 중요한 건 에러가 나도 전체 프로세스가 멈추지 않도록 각 단계를 try-except로 감싸는 것이었다. Threads API가 간헐적으로 503을 뱉는 경험을 이미 해봤기 때문에, Threads 하나 실패했다고 WordPress 발행까지 롤백되면 안 됐다.
⚙️ 사전 준비: 3~5편에서 만든 모듈들(claude_writer.py, wp_publisher.py, threads_poster.py, notion_logger.py)이 같은 디렉토리에 있어야 합니다. .env 파일에 각 API 키가 모두 세팅되어 있어야 하며, python-dotenv, anthropic, requests 라이브러리가 설치되어 있어야 합니다.
# publish_pipeline.py
# Task Scheduler가 실행할 메인 진입점 스크립트
import sys
import logging
from datetime import datetime
from dotenv import load_dotenv
# 각 편에서 만들어 둔 모듈 import
from claude_writer import generate_post
from wp_publisher import publish_to_wordpress
from threads_poster import post_to_threads
from notion_logger import log_to_notion
load_dotenv()
# 로그 파일 설정 — Task Scheduler는 콘솔을 못 보니까 파일로 남겨야 함
logging.basicConfig(
filename="pipeline.log",
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
encoding="utf-8"
)
def run_pipeline():
logging.info("=== 자동 발행 파이프라인 시작 ===")
result = {
"post_title": None,
"wp_url": None,
"threads_ok": False,
"notion_ok": False,
}
# 1단계: Claude로 글 생성
try:
post_data = generate_post()
result["post_title"] = post_data.get("title", "제목 없음")
logging.info(f"글 생성 완료: {result['post_title']}")
except Exception as e:
logging.error(f"Claude 글 생성 실패: {e}")
sys.exit(1) # 글 생성 실패는 치명적 — 여기서 중단
# 2단계: WordPress 발행
try:
wp_url = publish_to_wordpress(post_data)
result["wp_url"] = wp_url
logging.info(f"WordPress 발행 완료: {wp_url}")
except Exception as e:
logging.error(f"WordPress 발행 실패: {e}")
sys.exit(1) # 발행 실패도 치명적
# 3단계: Threads 배포 (실패해도 계속 진행)
try:
post_to_threads(result["post_title"], result["wp_url"])
result["threads_ok"] = True
logging.info("Threads 배포 완료")
except Exception as e:
logging.warning(f"Threads 배포 실패 (무시하고 계속): {e}")
# 4단계: Notion 기록 (실패해도 계속 진행)
try:
log_to_notion(result)
result["notion_ok"] = True
logging.info("Notion 기록 완료")
except Exception as e:
logging.warning(f"Notion 기록 실패 (무시하고 계속): {e}")
logging.info(f"=== 파이프라인 완료 | Threads:{result['threads_ok']} | Notion:{result['notion_ok']} ===")
if __name__ == "__main__":
run_pipeline()
여기서 내가 제일 신경 쓴 부분은 로깅이었다. Task Scheduler가 실행할 때는 콘솔 창이 안 보인다. 뭔가 잘못돼도 그냥 조용히 실패한다. 처음에 그걸 몰라서 “왜 글이 안 올라오지?” 하고 1시간 동안 코드만 뚫어봤는데, 알고 보니 Task Scheduler가 실행조차 안 되고 있었다. 그때부터 파일 로그는 필수라고 생각하게 됐다.
Task Scheduler 환경에서는 콘솔 출력이 보이지 않는다. 반드시 파일 로그를 남겨야 디버깅이 가능하다.
Task Scheduler 설정 — 진짜 삽질 포인트만 추려서
Task Scheduler 자체 설정은 GUI라 어렵지 않다. 시작 메뉴에서 “작업 스케줄러” 검색하면 나온다. “기본 작업 만들기”보다는 “작업 만들기”(고급 옵션)를 써야 세부 설정이 가능하다.
내가 실제로 설정한 값들을 정리하면 이렇다.
- ✅ 일반 탭: “사용자가 로그온할 때만 실행” → “사용자가 로그온되어 있는지 여부에 관계없이 실행”으로 변경 (이걸 안 하면 화면 잠금 상태일 때 실행 안 됨)
- ✅ 트리거 탭: 매일 오전 8시 00분, 반복 없음
- ✅ 동작 탭: 프로그램/스크립트에
python.exe전체 경로 입력 (반드시 가상환경 내부 python 경로로) - ✅ 동작 탭 인수 추가:
publish_pipeline.py파일명만 입력 - ✅ 동작 탭 시작 위치: 스크립트가 있는 폴더 경로 입력 (이걸 빠뜨리면 .env를 못 찾아서 API 키 인식 실패)
- ✅ 조건 탭: “AC 전원에 연결된 경우에만 작업 시작” 체크 해제 (노트북 배터리 상태에서도 실행되게)
- ✅ 설정 탭: “작업이 실패하면 다시 시작” — 1분 간격으로 최대 3회 재시도 설정
python.exe 경로를 반드시 가상환경 내 경로로 입력해야 합니다. 예: C:\Users\devYul\projects\blog_auto\venv\Scripts\python.exe. 시스템 Python을 쓰면 패키지를 못 찾아서 ModuleNotFoundError가 납니다.내가 처음에 가장 많이 걸렸던 문제는 세 가지였다.
**첫 번째**는 “시작 위치” 미입력. .env 파일은 스크립트 파일과 같은 폴더에 있었는데, Task Scheduler가 시작 위치 없이 실행하면 작업 디렉토리가 C:\Windows\system32가 되어버린다. 거기서 .env를 찾으니 당연히 못 찾는다.
**두 번째**는 가상환경 Python 경로 문제. 시스템 Python으로 실행했더니 anthropic, slack_bolt 같은 패키지들이 설치가 안 되어 있어서 즉시 에러. 가상환경 경로로 바꾸니 해결됐다.
**세 번째**는 “로그온 여부에 관계없이 실행” 설정. 이걸 설정하면 관리자 비밀번호를 입력하라고 한다. 처음엔 귀찮아서 “로그온할 때만”으로 뒀는데, 화면이 잠겨있는 새벽에 Task Scheduler가 실행을 건너뛰는 걸 보고 바꿨다.
Task Scheduler에서 가장 중요한 세 가지: 가상환경 Python 경로, 시작 위치(작업 디렉토리), 로그온 무관 실행 설정.
실제 동작 확인 — 테스트 실행부터 첫 자동 발행까지
설정을 마치고 나서 바로 “지금 실행”을 눌러봤다. 작업 스케줄러 우측 패널에 있는 “실행” 버튼이다. 처음엔 조용히 아무것도 안 일어났다. 또 뭔가 잘못됐나 싶었다.
30초 기다렸다가 pipeline.log 파일을 열어보니까 로그가 찍혀 있었다. “글 생성 완료”, “WordPress 발행 완료”가 쭉 올라오고, 마지막에 “파이프라인 완료” 로그까지. WordPress 대시보드에도 새 글이 발행 상태로 올라와 있었다.
그때 느낌이 기억난다. 진짜로 내가 아무것도 안 했는데 글이 올라갔다. Slack 명령도 없이, 터미널도 안 열었는데. 좀 어이없을 정도로 허무하게 됐다는 생각이 들면서 동시에 굉장히 뿌듯했다.
다음 날 아침 8시에 실제로 자동 실행이 되는지 확인하기 위해 알람을 맞춰놨다. 8시 3분쯤 WordPress를 확인했더니 새 글이 올라와 있었다. Notion 대시보드에도 발행 기록이 추가되어 있었고. 진짜 무인 자동화가 된 것이다.
⚙️ 사전 준비: 아래 배치 파일은 Task Scheduler 설정 전, 먼저 수동으로 실행해서 파이프라인이 정상 작동하는지 확인하는 용도입니다. Python 가상환경 경로와 스크립트 경로를 본인 환경에 맞게 수정하세요.
@echo off
:: run_pipeline.bat — 테스트용 수동 실행 배치 파일
:: Task Scheduler에 등록하기 전에 이걸 먼저 더블클릭해서 정상 작동 확인
cd /d C:\Users\devYul\projects\blog_auto
call venv\Scripts\activate
python publish_pipeline.py
:: 실행 후 로그 확인
echo.
echo === 실행 완료. pipeline.log 확인하세요 ===
type pipeline.log | findstr /C:"파이프라인"
pause
Task Scheduler에 직접 Python 경로를 넣기 전에 이 배치 파일로 먼저 테스트하는 걸 강하게 권장한다. 배치 파일이 정상 작동하면 그 경로 그대로 Task Scheduler에 옮겨 적으면 되니까 훨씬 덜 헷갈린다.
Task Scheduler 등록 전, 배치 파일로 먼저 수동 테스트. 경로 확인이 핵심이다.
한 가지 한계와 현실적인 보완책
당연히 완벽하지 않다. Task Scheduler의 가장 큰 제약은 PC가 켜져 있어야 한다는 것이다. 나는 개발 머신을 대부분 켜두는 편이라 실제로 문제가 된 적은 없지만, 여행을 가거나 장기 외출을 하면 그날은 글이 안 올라간다.
이 부분을 해결하는 가장 현실적인 방법은 두 가지다.
- ✅ 옵션 A: PC 전원 설정에서 “절전 모드 없음”으로 설정 + WOL(Wake on LAN) 설정으로 원격 부팅 가능하게
- ✅ 옵션 B: 장기 외출 시에만 GitHub Actions cron으로 임시 대체 (평소엔 Task Scheduler, 외출 시엔 Actions)
솔직히 나는 지금 옵션 A로 쓰고 있다. 절전 모드를 끄고 WOL 설정만 해뒀다. 아직 장기 외출이 없었고, 이걸 더 고도화하는 데 시간을 쓰기보다는 콘텐츠 퀄리티를 높이는 데 집중하는 게 맞다고 판단했다. “완벽한 시스템보다 일단 돌아가는 것 먼저”는 이 시리즈 내내 내가 스스로에게 하는 말이다.
0x1은 스크립트 에러, 0x41301은 현재 실행 중, 0x0은 정상 완료입니다.Task Scheduler의 PC 의존성은 실제 운영에서 큰 문제가 아니다. 완벽한 해결보다 현실적인 보완이 먼저다.
Day 5를 마치며 — 자동화가 ‘진짜’가 된 날
5편까지는 사실 “자동화 도구 모음”이었다. 각 기능이 있고, 내가 트리거를 당기면 동작하는 구조. 그게 6편에서 드디어 진짜 자동화가 됐다. 내가 아무것도 안 해도 매일 아침 글이 올라온다.
비용 면에서도 이번 편에서 추가된 고정비는 0원이다. Task Scheduler는 Windows에 기본 탑재된
Pingback: 쿠팡파트너스 가입부터 자동화까지 — 개발자가 수익화 채널 추가하는 실전기 - devYul
Pingback: 네이버 서치어드바이저 등록 후기 — 구글만으로는 부족하다 | devYul - devYul