윈도우에서 Claude Code가 조용히 망가지는 15가지 함정

Windows 11에서 Windows PowerShell 5.1로 Claude Code를 비대면으로 실행할 때, 나는 오류는 전혀 없는데 결과만 틀어지는 실패를 계속 마주쳤다. 나는 그중 하나하나를 내 머신(Windows PowerShell 5.1.26100, Python 3.13.7, Claude Code 2.1.304, 2026년 10월)에서 다시 트리거해 보았다.
내 머신은 일본어 Windows다. 코드 페이지에 의존하는 함정들에 대해서는 영어 기본값(ANSI 1252, OEM 437)으로도 다시 검사를 돌렸다. 그러자 #9만 더 이상 깨지지 않았다. 나머지는 같은 방식으로 깨지거나, 다르게 깨지거나(아래에 적음), 또는 언어 설정과 무관했다.
#1–#14 중 내 컴퓨터에서 무엇이 발생하는지 보려면, 무료 스크립트( MIT 라이선스, Claude Code 플러그인이기도 함)를 실행하면 된다: https://github.com/cart231-ffly-b/claude-code-windows-traps
전체 지도
| # | 무엇이 깨지는가 | 어디서 발생하는가 | 관찰한 결과 |
|---|---|---|---|
| 1 | exit 2가 1로 바뀜 | powershell -Command로 스크립트를 호출할 때 | -File=2 -Command=1 |
| 2 | 인수가 조용히 사라짐 | -Path a b가 있는 -File 사용 시 | count=1, 종료 코드 0 |
| 3 | 배열이 하나의 문자열이 됨 | -Path a,b가 있는 -File 사용 시 | first=a,b |
| 4 | 비 ASCII가 ??로 들어옴 | 텍스트를 네이티브 프로그램에 파이프로 전달할 때 | 바이트 3f3f |
| 5 | 캡처된 출력이 문자 깨짐이 됨 | 기본 콘솔 인코딩으로 UTF-8 출력을 읽을 때 | café—가 caf├⌐ΓÇö(cp437)로 읽힘 |
| 6 | Python은 리디렉션할 때만 크래시남 | ✓를 출력하는 python x.py > out.txt | UnicodeEncodeError, 종료 코드 1 |
| 7 | PYTHONIOENCODING이 영향 없음 | python -I | -I에서는 종료 코드 1, 없으면 0 |
| 8 | 긴 대시는 구문 오류가 됨 | BOM 없이 저장된 비 ASCII .ps1 | "string is missing the terminator" |
| 9 | .cmd가 주석을 실행함(CJK Windows에서만) | LF로 저장된 .cmd | cp932=broken cp65001=ok; 437/1252에서는 정상 |
| 10 | 한 줄짜리가 구문 오류가 됨 | PowerShell에서 powershell -Command "foreach ($i ...)" 실행 | 종료 코드 1 |
| 11 | -Include가 무시됨 | Get-ChildItem -LiteralPath ... -Include *.ps1 | .md도 함께 반환됨 |
| 12 | 글자가 줄바꿈으로 사라짐 | "..." 안의 Markdown 백틱 | `n이 줄바꿈이 됨 | |
| 13 | 커지는 파일이 0바이트로 보임 | AppData 아래 로그를 나열할 때 | 목록에는 0, 실제는 5,000,000 |
| 14 | claude -p에 프롬프트가 전달되지 않음 | --allowedTools Read "prompt" | Input must be provided ... |
| 15 | 느린 안전성 훅이 쓰기를 통과시킴 | 시간 초과 뒤에 응답하는 PreToolUse 훅 | 파일이 작성됨, 3번 중 3번 |
대부분을 막아 주는 세 가지 습관
- 화면에 안 보이는 것을 측정하라. 종료 코드, 바이트 수, 실제 파일 크기.
- ASCII 지시문 + UTF-8 파일. 비ASCII 텍스트는 파이프, 인수,
"..."안에 두지 말라. Claude에게 이렇게 말하라:Read the file C:\work\prompt.md and do exactly what it says. - 실제로 돌아갈 방식대로 한 번 실행해 보라 — 스케줄러를 통해,
-File로 — 신뢰하기 전에 반드시 한 번.
각각의 함정
1. -Command가 종료 코드 2를 1로 바꾼다. exit 2만 하는 스크립트가 -File에서는 2를 반환했지만 -Command에서는 1을 반환했다. 0과 1은 그대로 통과하므로, 2 이상을 쓰기 전까지는 아무 이상 없어 보인다 — 작업 스케줄러의 "Last Run Result"도 1로 표시된다. -File을 쓰거나, ; exit $LASTEXITCODE를 덧붙여라.
2–3. -File은 인수를 평범한 문자열로 전달한다. -Path a b는 b를 조용히 버렸다(종료 코드 0). [CmdletBinding()]을 추가하면 같은 호출이 "A positional parameter cannot be found"로 멈춘다 — 멈추는 쪽이 더 안전하다. -Path a,b는 한 문자열 a,b로 남는다. -split ','로 직접 나눠라.
4. 파이프로 전달된 비ASCII 텍스트가 ??로 도착한다. Windows PowerShell 5.1은 네이티브 프로그램으로 보낸 텍스트를 $OutputEncoding으로 인코딩하는데, 기본값은 us-ascii다. Python으로 café—를 파이프로 보내면 caf??로 도착했다. claude -p로 파이프한 프롬프트도 같은 방식으로 손상된다. 긴 프롬프트는 UTF-8 파일에 넣고 ASCII 지시문만 보내라.
5. 캡처된 출력이 모지바케로 바뀐다. 반대 방향도 깨진다: 기본 콘솔 인코딩으로 읽은 UTF-8 출력이 전혀 상관없는 문자로 바뀌었다 — cp437(미국 기본값)에서는 café—가 caf├⌐ΓÇö로 돌아왔다. 읽는 쪽에서 [Console]::OutputEncoding을 UTF-8로 설정하고, 보내는 쪽도 함께 맞춰라.
6. Python은 리디렉션할 때만 충돌한다. 화면에 출력할 때는 Windows Python이 무엇이든 출력한다. 하지만 파일로 리디렉션하면 ANSI 코드 페이지로 인코딩하고, 그 안에 없는 문자를 만나면 충돌한다. cp1252에서는 —와 é는 괜찮았지만, ✓(U+2713)는 종료 코드 1과 함께 충돌했다 — 그리고 AI가 작성한 스크립트는 ✓와 →를 출력하는 걸 좋아한다. 이것이 스크립트가 "손으로 돌릴 때는 되는데 스케줄러 아래에서는 실패하는" 이유 중 하나다. PYTHONIOENCODING=utf-8가 이를 고친다. (2026년 10월 9일에 출시된 Python 3.15는 기본값이 UTF-8이므로, 3.15 이상에서는 이런 현상이 없을 것으로 예상된다. 나는 3.13에서 테스트했고, 3.15의 기본값인 -X utf8에서는 같은 스크립트가 충돌하지 않았다.)
7. python -I는 PYTHONIOENCODING을 무시한다. -I(격리 모드)에서는 충돌이 그대로였다(-I에서는 종료 코드 1, 없으면 0). -I를 빼거나, 스크립트 안에서 sys.stdout.reconfigure(encoding="utf-8")를 호출하라. 이것 역시 Python 3.15 이상에서는 예상되지 않는다.
8. BOM이 없는 비ASCII .ps1. PowerShell 5.1은 BOM 없는 파일을 ANSI 코드 페이지로 읽는다. 영어 Windows에서는 그게 모지바케보다 더 나쁘다: —의 UTF-8 바이트는 0x94로 끝나는데, cp1252는 이를 ”로 읽는다 — 그리고 PowerShell은 중괄호 따옴표를 실제 따옴표로 취급한다. Write-Output "a—b"를 BOM 없이 저장하면 cp1252로 읽혀 파싱에 실패한다("The string is missing the terminator"). 이런 파일은 BOM이 있는 UTF-8로 저장하거나, .ps1 파일을 ASCII 전용으로 유지하라.
9. (일본어, 중국어, 한국어 Windows에서만) LF 줄바꿈이 있는 .cmd가 주석을 실행한다. AI 파일 도구는 보통 LF로 쓴다. 비ASCII rem 주석이 들어 있는 .cmd를 LF로 저장하면 cmd.exe가 주석 텍스트를 실행하려고 했다. 같은 파일은 코드 페이지 932에서는 깨졌고 65001(UTF-8)에서는 끝까지 실행됐다. 437과 1252에서는 악센트가 있는 라틴 문자 주석이 들어 있는 파일이 정상적으로 실행됐다. 사용자가 CJK Windows를 포함한다면, 어쨌든 .cmd 파일은 CRLF로 저장하라.
10. 한 줄짜리 명령이 구문 오류가 된다. PowerShell에서 powershell -Command "foreach ($i in 1..2) { $i }"를 실행하면 호출한 셸이 먼저 $i를 확장해버립니다(빈 값으로). 자식 프로세스는 foreach ( in 1..2)를 받게 되고 종료 코드 1로 끝납니다. 로직을 .ps1 파일에 넣고 -File로 실행하세요.
11. -Include는 -LiteralPath와 함께 쓰면 무시된다. 한 폴더에 .ps1 하나와 .md 하나가 있을 때 Get-ChildItem -LiteralPath <dir> -Recurse -File -Include *.ps1를 실행하면 둘 다 반환됐습니다. 제가 만든 상태 점검 스크립트도 이 문제를 만나 Markdown과 JSON 파일을 손상된 배치 파일로 잘못 표시했는데, 제 테스트 픽스처에는 스크립트만 들어 있어서 테스트로는 드러나지 않았습니다. Where-Object로 확장자를 필터링하세요.
12. "..." 안의 백틱. 큰따옴표로 묶인 PowerShell 문자열 안에서 백틱은 이스케이프 문자입니다. `night-shift` 같은 Markdown은 `n이 줄바꿈으로 바뀌고 n이 사라집니다. 백틱이 포함된 텍스트에는 작은따옴표 here-string을 사용하세요.
13. 쓰여지고 있는 파일은 0바이트로 보인다. 백그라운드 프로세스가 쓰는 로그 파일이 Get-ChildItem에서는 0바이트로 표시됐지만, 열어보니 5,000,000바이트였습니다. 제 환경에서는 이것이 AppData 아래에서는 항상 발생했고 홈 폴더 아래에서는 발생하지 않았습니다. 이유는 아직 확인하지 못했습니다. 비어 있다고 결론 내리기 전에 파일을 열어보세요.
14. claude -p는 프롬프트를 삼킨다. claude -p --allowedTools Read "say hi"를 실행하면 Error: Input must be provided either through stdin or as a prompt argument when using --print 오류가 났습니다. --allowedTools와 --add-dir는 여러 값을 받을 수 있어서 프롬프트를 먹어버립니다. 프롬프트는 stdin으로 보내세요.
15. 늦게 응답하는 안전 훅은 쓰기를 통과시킨다. 저는 항상 거부하는 PreToolUse 훅을 만들었는데, 3초 타임아웃이 걸린 상태에서 먼저 15초를 기다리게 했습니다. 그러자 Claude Code가 파일을 그냥 써버렸습니다. 오류도 경고도 없었고, 3번 모두 그랬습니다. 타임아웃을 60초로 늘리면 차단됐습니다. 공식 훅 문서도 같은 내용을 말합니다. 시간 초과된 PreToolUse 훅은 호출을 막지 않으므로, 멈춘 훅을 게이트로 믿으면 안 됩니다. 해결책은 한 줄입니다. 훅에 "onFailure": "block"을 추가하세요(Claude Code 2.1.295 이상). 그러면 같은 늦은 훅이 쓰기를 막았고, 스크립트가 없는 훅도 마찬가지로 막았습니다.
저는 이 수정 사항들을 cc-win-kit으로 패키징했습니다. Claude Code용 쓰기 가드(onFailure: "block" 포함), 중지 조건이 있는 비대면 실행기, 이런 파일과 fail-open 훅 설정을 머신에서 찾아내는 doctor, 그리고 실제 claude -p로 훅 타임아웃 구멍을 보여주는 스크립트가 들어 있습니다. 가격은 \$19입니다: https://payhip.com/b/bRnDJ (함정 재현 스크립트 자체는 위의 무료 버전입니다)
공개: 이 글은 AI의 도움을 받아 작성했습니다. Claude Code가 제 Windows 머신에서 재현 스크립트를 실행하고 초안을 작성했으며, 위에 인용한 결과는 그 실행에서 나온 것입니다.
출처
- 원문: https://dev.to/kamijo_aoto/claude-code-on-windows-breaks-without-errors-15-traps-i-reproduced-d6 (2026-10-24 · 上条碧人)