무슨 일이 있었나
크론 0단계에서 대시보드 자가복구용 PowerShell 명령을 -File 인수와 함께 실행했다. 대시보드가 응답하지 않을 때 start-dashboard.ps1을 자동으로 다시 띄우는, 평소엔 조용히 넘어가야 할 절차였다. 그런데 스크립트가 시작도 못 하고 끊겼다.
수집된 오류 로그는 인코딩이 두 번 어긋나(cp949 바이트가 utf-8로 그대로 기록됨) 한글 부분이 복원되지 않는다. 확인할 수 있는 것은 ASCII로 살아남은 조각뿐이다.
출처: 헤르메스 오류 로그(errors.log) 원문. 한글 부분은 인코딩이 깨져 복원되지 않아
<한글 깨짐> 으로 표시했고, ASCII 로 살아남은 부분만 그대로 옮겼다.
2026-09-08 22:15:51 WARNING [cron_...] agent.tool_executor:
Tool terminal returned error (3.46s): {"output":
"-File <한글 깨짐> 'scriptsstart-dashboard.ps1'<한글 깨짐>.
<한글 깨짐> '.ps1' <한글 깨짐> -File <한글 깨짐>.
Windows PowerShell
Copyright (C) Microsoft Corp...
이 로그에서 확실히 읽을 수 있는 것은 ASCII로 남은 세 조각뿐이다 — -File, 파일 이름 자리에 찍힌 'scriptsstart-dashboard.ps1', 확장자 .ps1. 한글 부분은 인코딩이 깨져 그럴듯하게 복원하지 않는다.
그런데 원래 명령에 넘긴 값은 scripts\start-dashboard.ps1이었다. 즉 -File 뒤의 인수 자체는 파워셸에 도착했다 — 다만 역슬래시(\)가 사라져 scripts와 start-dashboard.ps1이 한 낱말로 붙은 채로 도착한 것이다. 그런 이름의 파일은 없으니 파워셸은 -File이 받을 경로를 찾지 못했다고 답했다.
로그를 어떻게 읽었나
가장 먼저 한 일은 로그에서 무엇을 근거로 삼을 수 있는지 가르는 것이었다. cp949 바이트가 utf-8로 두 번 어긋난 로그는 한글 부분을 아무리 들여다봐도 복원되지 않는다. 반쯤 짐작으로 채우는 대신, ASCII로 남은 부분만 사실로 다루기로 했다 — -File, 'scriptsstart-dashboard.ps1', .ps1 세 조각이다.
이 세 조각만으로도 방향은 잡혔다. 오류 문구 안에 파일 이름이 그대로 찍혀 있다는 것은 -File이 인수를 아예 못 받은 게 아니라 받긴 받았는데 그 값이 이미 훼손된 채 도착했다는 뜻이다. 원래 넘긴 값 scripts\start-dashboard.ps1과 로그에 찍힌 scriptsstart-dashboard.ps1을 나란히 놓고 보면 차이는 역슬래시 한 글자뿐이었다.
여기서 의심할 지점은 두 군데로 좁혔다. 하나는 경로 구분자(역슬래시 \)가 중간 계층에서 이스케이프 문자로 오인돼 사라졌을 가능성, 다른 하나는 상대경로라서 크론이 실행되는 작업 디렉터리와 실제 스크립트 위치가 어긋났을 가능성이다. 표로 정리하면 이렇다.
| 구간 | 받는 것 | 내보내는 것 | 문제 가능 지점 |
|---|---|---|---|
| 크론 | 등록된 명령 문자열 | 셸 실행 요청 | 역슬래시가 이스케이프로 해석돼 사라질 수 있음 |
| 셸 | 크론이 넘긴 명령 | 파워셸 프로세스 실행 | 상대경로 → 작업 디렉터리 불일치 |
| 파워셸 | -File 뒤 인수 | 스크립트 실행 | 인수는 도착했으나 역슬래시가 빠진 채 |
절대경로 + 슬래시(/) + 전체를 따옴표로 감싸는 조합으로 바꿔서 재실행했더니 바로 통과했다. 이 결과로 역슬래시가 도구 계층을 지나며 사라지는 지점이 원인이라는 쪽으로 좁혔다.
원인
역슬래시가 든 상대경로 인수가 크론 → 셸 → 파워셸로 이어지는 도구 계층을 지나며 역슬래시가 사라졌고, 그 결과 scripts와 start-dashboard.ps1이 한 낱말로 붙은 값이 -File에 도착한 것으로 보인다. 인수 자체가 사라진 것이 아니라, 인수 값 안의 구분자가 사라져 존재하지 않는 파일명이 만들어진 것이 이 사고의 핵심이다. 다만 정확히 어느 계층에서 어떤 규칙으로 역슬래시가 빠졌는지 — 크론의 인용부호 처리인지, 중간 셸의 이스케이프 규칙인지 — 는 이 로그만으로는 단정할 수 없다. 절대경로·슬래시·따옴표로 바꾸자 문제가 재현되지 않았다는 것까지만 확인됐다.
조치
절대경로를 슬래시(/)로 쓰고 전체를 따옴표로 감싸 전달하도록 고쳐 재실행했고, 정상 동작을 확인했다.
powershell -NoProfile -ExecutionPolicy Bypass -File "C:/Users/dpffl/Desktop/ellisinfo/scripts/start-dashboard.ps1" -NoChrome
PowerShell 공식 문서(about_PowerShell_exe)의 -File 파라미터 설명에도 이 스위치가 받는 값은 "파일 경로와 그 인수들"이며 -File이 명령의 마지막 파라미터여야 한다고 명시돼 있다. 값이 셸을 거치며 문자열로 전달되는 방식이라, 상위 계층에서 구분자 처리가 어긋나면 이 스위치가 받는 값 자체가 달라질 수 있는 구조다. 이 사건은 그 경계에서 걸린 경우로 보인다.
참고로 이 자가복구 절차는 티스토리 Open API 종료 후 자동 발행 구축기에서 다룬 크롬 자동화 워커와 마찬가지로, 대시보드가 죽으면 발행 파이프라인 전체가 멈추기 때문에 만들어 둔 것이다. 예약 발행이 대시보드 쪽 상태와 어긋나는 문제를 다룬 워드프레스 REST 예약 발행이 9시간 밀리는 이유도 같은 맥락에서 "이 파이프라인이 조용히 멈추면 알아채기 어렵다"는 문제의식으로 이어진다.
같은 일을 막으려면
지금 확인한 것은 딱 하나 — 절대경로 + 슬래시 + 따옴표 조합이면 통과한다는 것뿐이다. 아직 하지 않은 것들이 있다.
- 정확히 어느 계층에서 역슬래시가 빠지는지, 크론 등록 문자열 자체를 단계별로 로깅해 확인하는 작업은 아직 하지 않았다.
- 이 자가복구 명령 외에 크론에 등록된 다른 PowerShell 호출도 같은 방식(역슬래시·상대경로)으로 돼 있는지 전수 점검은 아직 하지 않았다.
- 인수 값이 훼손된 채로 조용히 실패하는 대신, 자가복구 스크립트 실행 실패 자체를 알림으로 받는 장치는 없다.
이번엔 원래 명령을 고쳐 재실행하는 것으로 끝났지만, 크론에 등록된 다른 경로 인수들도 같은 패턴일 가능성은 남아 있다.