수동 실행은 되는데, 심야 자동 실행만 실패하는 Python (Windows・cp932) 확인 목록
요약
Windows 작업 스케줄러에서 Python 자동화 스크립트가 심야에 실패하는 주된 원인은 콘솔의 문자 코드(cp932) 문제 때문입니다. 이 글은 수동 실행과 달리 환경이 달라지는 무인 운영 시 발생할 수 있는 오류를 방지하기 위한 4가지 필수 점검 항목을 제시합니다.
핵심 포인트
- 표준 출력 코드를 `sys.stdout.reconfigure`로 UTF-8 고정
- 파일 입출력 시에는 반드시 `encoding='utf-8'` 명시
- 실제 운영 환경과 동일한 조건으로 전체 경로 지정하여 테스트 필수
- 로그 파일 외에 알림 시스템을 구축하여 실패를 인지할 수 있도록 함
Claude Code로 만든 작은 자동 운영(예약 포스팅, 상품 집계, 야간 로그 기록)을 Windows의 작업 스케줄러로 구동하고 있습니다.
무인으로 작동하던 심야 잡(job)이 UnicodeEncodeError로 멈춘 적이 있습니다. 수동으로 실행했을 때는 매번 정상적으로 작동했던 스크립트입니다.
원인은 콘솔의 문자 코드(cp932)가 로그에 포함된 문자를 출력하지 못했기 때문이었습니다. 수동으로 작동할 때와 스케줄러가 작동할 때, 실행 환경이 같지 않았던 것입니다.
이 글은 그 이후 '무인으로 구동하기 전에 반드시 확인해야 할' 항목들을 정리한 내용입니다. AI 에이전트에게 작성시킨 스크립트는 작성자(=AI) 본인도 로컬 단말기에서만 작동시키는 경우가 많으므로, 특히 유용합니다.
1. 표준 출력의 문자 코드를 코드 레벨에서 지정하기
Windows에서 Python을 구동할 때, 표준 출력의 문자 코드는 실행 방식에 따라 달라질 수 있습니다. 로컬 터미널에서는 표시되었던 문자가 스케줄러 경유나 리다이렉트 시에는 cp932로 쓰려고 하다가 실패하는 경우가 발생합니다(이모지나 cp932에 없는 기호가 전형적입니다).
대책은 다음 두 가지 중 하나(둘 다 가능)를 적용하는 것입니다.
import sys
# 스크립트 시작 부분에서 출력을 UTF-8로 고정합니다 (Python 3.7+)
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
...
또는, 작업 환경에 PYTHONUTF8=1을 설정하여 Python을 UTF-8 모드로 구동합니다.
errors="replace"로 설정해 두면, 만약 표시할 수 없는 문자가 있어도 잡 자체는 멈추지 않습니다. 로그의 한 글자 때문에 본 처리가 멈추는 것이 가장 아깝기 때문에, 여기서는 관대하게 처리했습니다.
2. 파일 읽기/쓰기는 반드시 encoding을 명시하기
open(path)처럼 encoding을 생략하면 Windows에서는 환경의 기본값(대부분 cp932)이 사용됩니다. 로컬과 스케줄러가 같더라도, 다른 PC로 옮기는 순간 깨집니다.
open(path, encoding="utf-8") # 자신이 작성하는 파일
open(".env", encoding="utf-8-sig") # 메모장에서 저장된 BOM 포함에도 대응
.env와 같은 '사람이 메모장으로 편집하는 파일'은 BOM이 포함된 UTF-8로 저장되는 경우가 있으므로 utf-8-sig로 읽고 있습니다. 파일의 맨 앞에 보이는 BOM 때문에 '키를 찾을 수 없음'이 되는 것을 방지할 수 있습니다.
3. '실제 운영 환경과 동일한 조건'으로 한 번 구동해 보기
수동으로 python job.py를 실행해서 작동했다는 것은 테스트가 될 수 없습니다. 차이가 있는 부분은 주로 여기에 있습니다.
- 실행 사용자 (로그온하지 않아도 실행하는 경우, 환경이 달라짐) -
- 작업 폴더 (상대 경로로 읽는 파일이 찾을 수 없음) -
- 환경 변수 (
PYTHONUTF8나 API 키 읽기 방식) - - 사용되는 Python (PATH 상의 다른 버전)
작업 스케줄러의 '작업'에서 Python 실행 파일과 스크립트를 **전체 경로(full path)**로 지정하고, '시작(작업 폴더)'도 명시합니다. 그 위에 작업을 마우스 오른쪽 버튼으로 클릭 → '실행'을 하여 한 번 구동해 보고 로그를 확인합니다. 밤까지 기다릴 필요 없이, 실제 운영 환경과 동일한 경로로 작동시킬 수 있습니다.
4. 실패는 '로그 파일'이 아닌 '사람'에게 전달하기
로그 파일에만 기록하는 것은 아무도 읽지 않으면 실패하지 않은 것과 같습니다. 대책으로 다음 두 가지가 유효합니다(저는 두 번째를 매일 아침 요약 형태로 운영하고 있습니다).
- 잡이 성공하면 '마지막 성공 시각'을 기록하고, 일정 시간(예: 26시간) 업데이트가 없으면 다른 시스템이 알림 (생존 감시) - 매일 아침 전날 결과의 요약을 사람이 읽는 곳에 내보내기
'실패하면 알림' 방식은, 알림 처리 자체에서 실패할 경우 아무것도 오지 않습니다. '성공이 멈추면 알림' 방식이 무인 운영에서는 더 안전합니다.
5. 쓰기 작업을 하는 잡은, 실패 후 재실행에 주의하기
심야에 실패한 잡을 아침에 수동으로 재실행할 때, 실패하기 전에 어디까지 쓰기가 완료되었는지를 확인하지 않으면, 포스팅이나 전송이 중복됩니다.
저는 쓰기 작업을 하기 전에 '시도(attempt)'를 기록하고, 결과가 불분명한 것(타임아웃 등)이 있는 한 다음을 보내지 않는 일종의 기록장 방식을 사용합니다. 이 메커니즘은 이전 글에 작성했습니다.
요약
- 출력 문자 코드는 코드에서 고정 (
reconfigure또는PYTHONUTF8=1) - 파일은 반드시
encoding을 지정 (사람이 편집하는 파일은utf-8-sig) - 운영 환경과 동일한 사용자, 작업 폴더, Python으로 한 번 실행해 보기
- 실패가 아니라 '성공의 중단'을 사람에게 알리기
- 재실행 전에 어디까지 기록되었는지 확인하기
AI 에이전트가 임의로 쓰기 작업을 하도록 하기 전 체크리스트를 25개 항목의 1페이지(일본어・PDF)에 모아 무료 배포하고 있습니다. 이번 내용은 '7. 실행 환경' 항목입니다.
중복 전송을 막는 기록장(Python 표준 라이브러리만 사용, 1파일)은 여기서 공개합니다.
토론

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기