
로컬에서 python job.py로 돌리면 멀쩡하던 스크립트가, 작업 스케줄러에 등록하고 나면 API 키를 못 찾겠다며 죽어버리는 경우가 있습니다. 결론부터 말씀드리면 이건 코드 문제가 아니라 작업 스케줄러가 어떤 계정으로 프로세스를 띄우느냐의 문제입니다. 작업을 SYSTEM 계정으로 등록하면 로그인 사용자가 설정한 환경변수를 파이썬이 아예 읽지 못하는 구조이기 때문입니다.
이 글은 Windows 10/11의 작업 스케줄러(Task Scheduler)와 schtasks 명령, 그리고 파이썬 os.environ을 기준으로 설명합니다. 다른 OS나 WSL 환경에는 해당하지 않습니다.
SYSTEM 계정으로 등록한 작업이 환경변수를 못 찾는 이유가 뭘까요?
작업 스케줄러에서 새 작업을 만들 때 “보안 옵션”에 실행 계정을 지정하는 칸이 있습니다. 여기를 비워두거나 별도 설정 없이 만들면 많은 경우 SYSTEM(정확히는 로컬 시스템 계정)으로 등록됩니다.
SYSTEM은 사람이 로그인해서 쓰는 계정이 아니라 운영체제 자체가 쓰는 서비스 계정입니다. 마이크로소프트 공식 문서에도 로컬 시스템 계정은 사용자 프로필을 갖지 않는다고 명시돼 있습니다. 사용자 프로필이 없다는 말은 곧 제어판 > 시스템 속성 > 환경 변수에서 “사용자 변수” 항목에 등록한 값(API 키, 가상환경 경로 등)에 접근할 방법 자체가 없다는 뜻입니다.
반면 “시스템 변수”에 등록한 값은 SYSTEM 계정도 읽을 수 있습니다. 다만 이 값도 작업 스케줄러 서비스가 처음 시작된 시점의 환경을 기준으로 캐시되기 때문에, 재부팅 없이 시스템 변수를 새로 추가하면 스케줄러에서 실행되는 프로세스에는 곧바로 반영되지 않는 경우가 흔합니다.
사용자 환경변수와 시스템 환경변수는 저장 위치와 적용 시점이 다릅니다
환경 변수는 원래 프로세스가 실행될 때 부모 프로세스로부터 물려받는 키-값 목록입니다. 사용자 변수는 로그인한 사용자 세션에, 시스템 변수는 컴퓨터 전체에 적용되는데, 이 둘이 합쳐지는 시점은 새 프로세스가 시작될 때뿐입니다.

직접 실험해 보면 차이가 뚜렷하게 드러납니다. 다음 스크립트를 두 가지 방식으로 실행해 결과를 비교해 보겠습니다.
import os
api_key = os.environ.get("MY_API_KEY")
print(f"MY_API_KEY = {api_key}")
터미널에서 직접 실행하면 사용자 변수로 등록해 둔 MY_API_KEY 값이 그대로 출력됩니다. 하지만 이 스크립트를 SYSTEM 계정으로 등록한 작업 스케줄러 태스크에서 돌리면 출력은 MY_API_KEY = None이 됩니다. 코드는 한 줄도 바뀌지 않았는데 결과만 달라지는, 전형적인 “내 컴퓨터에선 되는데요” 상황입니다.
환경변수 대신 코드에서 값을 직접 읽는 방법
가장 실용적인 우회는 시스템 환경변수 자체에 기대지 않고, 스크립트가 절대경로로 지정된 설정 파일을 직접 읽게 만드는 방식입니다. python-dotenv 패키지를 쓴다면 경로를 명시적으로 지정해야 합니다.
from dotenv import load_dotenv
import os
load_dotenv(dotenv_path=r"C:\scripts\myjob\.env")
api_key = os.environ.get("MY_API_KEY")
print(f"MY_API_KEY = {api_key}")
여기서 중요한 조건이 하나 있습니다. load_dotenv()를 인자 없이 호출하면 현재 작업 디렉터리를 기준으로 .env를 찾는데, 작업 스케줄러는 “시작 위치(Start in)” 필드를 비워두면 작업 디렉터리가 C:\Windows\System32가 되는 경우가 많습니다. 그래서 경로를 반드시 절대경로로 못박아야 스케줄러 환경에서도 동일하게 동작합니다.
레지스트리나 JSON 설정 파일로 관리하는 것도 같은 원리입니다. 핵심은 “OS 환경변수 조회에 의존하지 않는” 경로를 하나 만들어 두는 것입니다.

작업 등록 화면에서 이 옵션부터 확인해 보세요
환경변수를 그대로 쓰고 싶다면 실행 계정을 SYSTEM에서 로그인 사용자 계정으로 바꾸는 방법도 있습니다. 다만 계정에 따라 동작 조건이 달라지므로 표로 정리했습니다.
| 실행 방식 | 사용자 환경변수 접근 | 로그아웃 상태에서 실행 | 비밀번호 저장 필요 |
|---|---|---|---|
| SYSTEM 계정 | 불가 | 가능 | 불필요 |
| 로그인 사용자 + “로그온할 때만” | 가능 | 불가 | 불필요(세션 필요) |
| 로그인 사용자 + “로그온 여부와 관계없이 실행” | 가능 | 가능 | 필요 |
schtasks 명령으로 등록한다면 아래처럼 계정과 옵션을 지정합니다.
schtasks /create /tn "MyPythonJob" ^
/tr "C:\Python311\python.exe C:\scripts\myjob\job.py" ^
/sc daily /st 09:00 ^
/ru "MYPC\myuser" /rp "비밀번호" /rl HIGHEST
GUI에서는 작업 속성의 “일반” 탭에서 “사용자가 로그온했는지 여부와 관계없이 실행” 라디오 버튼을 선택하면 같은 효과를 냅니다. 이 옵션을 켜면 사용자 환경변수도 정상적으로 상속되지만, 계정 비밀번호를 바꾸는 순간 작업이 조용히 실패하기 시작한다는 점은 미리 알아두어야 합니다.
그냥 로그인 계정으로 등록하면 되지 않나요? — 트레이드오프까지 따져보기
로그인 계정으로 바꾸면 환경변수 문제는 해결되지만 다른 조건이 따라붙습니다. 서버용 미니PC처럼 계정 비밀번호 만료 정책이 있는 환경이라면, 비밀번호가 바뀔 때마다 작업 스케줄러에 저장된 자격 증명도 다시 입력해 줘야 합니다. 재부팅 후 아무도 로그인하지 않는 서버에서는 “로그온 여부와 관계없이 실행” 옵션이 꺼져 있으면 작업 자체가 소리 없이 안 돌아가기도 합니다.
반대로 SYSTEM 계정은 비밀번호 만료나 세션 유무와 무관하게 항상 동작한다는 장점이 있습니다. 그래서 환경변수에 의존하는 코드를 바꾸는 쪽이, 계정을 바꾸는 쪽보다 장기적으로는 유지보수가 덜 필요한 경우가 많습니다. 지금 등록된 작업이 SYSTEM으로 돌아가고 있다면, 계정을 바꾸기 전에 먼저 os.environ.get()을 쓰는 부분을 절대경로 설정 파일 읽기로 바꿀 수 있는지부터 점검해 보시기 바랍니다.
대화 히스토리 관리, 슬라이딩 윈도우와 요약 압축의 손익 계산법
