VS Code 터미널 및 디버그 콘솔 한글 깨짐
등록일: 2026-04-18 내용: Spring Boot Multi-module + Gradle + Windows 환경에서 한글이 깨지는 문제를 완전히 해결하기까지의 과정과 최종 해결책 정리.
증상
| 실행 방법 | 결과 |
|---|---|
| VSCode 내장 터미널 (일반 출력) | ✅ 정상 |
PowerShell에서 gradlew.bat :모듈:bootRun | ❌ 한글 깨짐 |
Git Bash에서 ./gradlew :모듈:bootRun | ❌ 한글 깨짐 |
| Spring Boot Dashboard 디버그 모드 | ❌ 한글 깨짐 |
문제의 원인 구조
Windows는 기본 콘솔 코드 페이지가 CP949(MS949) 임. Java 프로세스가 UTF-8 문자열을 출력할 때, 이를 받는 쪽(터미널 또는 Debug Adapter)이 CP949로 해석하면 깨짐.
실행 경로가 3가지로 갈리기 때문에 원인도 각기 달랐음:
[1] gradlew.bat (PowerShell/cmd)
└─ cmd.exe가 CP949 코드 페이지로 JVM 실행
→ 터미널도 CP949 → UTF-8 출력 깨짐
[2] ./gradlew (Git Bash/MSYS2)
└─ Unix 쉘 스크립트 실행 → gradlew.bat 미사용
→ Windows ConPTY 코드 페이지는 여전히 CP949
→ JVM 출력 깨짐
[3] Spring Boot Dashboard 디버그 모드
└─ Java Debug Adapter가 JVM을 직접 실행 (Gradle 미사용)
→ gradlew 관련 설정 전혀 적용 안 됨
→ file.encoding = MS949 (Windows 기본값)
→ Debug Adapter가 MS949로 stdout 디코딩 → 깨짐
해결 시도 과정
시도 1 — gradlew.bat에 chcp 65001 추가
@rem Set UTF-8 code page to prevent Korean/CJK character corruption in Java output
chcp 65001 > nul
결과: PowerShell/cmd 환경 ✅ 해결, Git Bash ❌ 여전히 깨짐
이유: Git Bash는 gradlew.bat를 직접 실행하지 않고 Unix gradlew 쉘 스크립트를 사용함. gradlew.bat의 chcp는 Git Bash 세션에 영향 없음.
시도 2 — Unix gradlew 스크립트에 인코딩 설정
DEFAULT_JVM_OPTS에 인코딩 플래그 추가:
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m" "-Dfile.encoding=UTF-8" "-Dstdout.encoding=UTF-8" "-Dstderr.encoding=UTF-8"'
MSYS 감지 블록 이후 Windows 코드 페이지 전환 추가:
if "$msys" ; then
cmd.exe //c chcp 65001 > /dev/null 2>&1 || true
fi
결과: Git Bash ✅ 해결
이유: Git Bash(MSYS2)는 Windows 위에서 동작하므로 출력은 결국 Windows ConPTY를 통함. JVM 인코딩 플래그만으로는 부족하고, ConPTY의 코드 페이지 자체를 UTF-8로 바꿔야 함.
gradlew --stop후 재시도해야 데몬이 새 JVM 옵션을 반영함.
시도 3 — 전 모듈 공통 적용
gradle.properties — Gradle 데몬 JVM 인코딩:
org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8
root build.gradle.kts의 subprojects 블록 (기존에 이미 설정됨):
tasks.withType<JavaExec> {
jvmArgs("-Dfile.encoding=UTF-8", "-Dstdout.encoding=UTF-8", "-Dstderr.encoding=UTF-8")
}
각 모듈 application.yml — Logback ConsoleAppender charset:
logging:
charset:
console: UTF-8
file: UTF-8
결과: gradlew bootRun 경로 모든 모듈 ✅ 해결
시도 4 — Spring Boot Dashboard 디버그 모드: launch.json vmArgs
"vmArgs": "-Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8"
결과: ❌ 여전히 깨짐
이유: Spring Boot Dashboard 확장이 launch.json의 vmArgs를 사용하지 않고 자체 launch configuration을 동적으로 생성하는 것으로 추정됨. vmArgs가 JVM에 전달되지 않은 것.
시도 5 — java.debug.settings.console: "integratedTerminal"
"java.debug.settings.console": "integratedTerminal"
결과: ❌ 여전히 깨짐 (출력이 Debug Console → 통합 터미널로 변경되었으나 한글 여전히 깨짐)
이유: integratedTerminal은 디버그 세션마다 새 터미널을 생성함. 새 터미널은 Windows 기본 코드 페이지(CP949)로 시작. gradlew.bat의 chcp 65001은 기존 세션에만 적용되므로 이 새 터미널에는 효과 없음.
핵심 차이:
integratedTerminal: JVM stdout → 터미널 PTY → CP949로 렌더링 → ❌internalConsole: JVM stdout → Debug Adapter (Node.js/UTF-8) → VSCode Debug Console → ✅
시도 6 — DotenvUtil.load()에 System.setOut/setErr 추가
public static void load() {
System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
System.setErr(new PrintStream(System.err, true, StandardCharsets.UTF_8));
// ... 기존 .env 로딩 로직
}
의도: Spring 기동 전에 System.out을 UTF-8 PrintStream으로 교체 → Logback이 초기화될 때 이미 UTF-8 스트림을 래핑하게 됨.
결과: 단독으로는 부족. file.encoding이 여전히 MS949이므로 Debug Adapter가 MS949로 디코딩.
최종 해결 — 3계층 동시 적용
계층 1: launch.json — env.JAVA_TOOL_OPTIONS
"env": {
"JAVA_TOOL_OPTIONS": "-Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8 -Dstderr.encoding=UTF-8"
}
vmArgs와 달리 JAVA_TOOL_OPTIONS는 OS 환경변수로 주입됨 → JVM 시작 전 C 라이브러리 레벨에서 읽힘 → file.encoding=UTF-8이 JVM 시작 시점부터 적용됨 → Debug Adapter가 JDWP로 JVM의 file.encoding을 읽으면 UTF-8을 반환 → stdout을 UTF-8로 디코딩.
계층 2: settings.json — internalConsole로 복원
"java.debug.settings.console": "internalConsole"
Debug Console은 VSCode의 UTF-8 네이티브 출력 영역. integratedTerminal처럼 CP949 터미널을 새로 열지 않음.
계층 3: DotenvUtil.load() — Logback을 위한 UTF-8 스트림 보장
System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
System.setErr(new PrintStream(System.err, true, StandardCharsets.UTF_8));
DotenvUtil.load()는 모든 모듈의 main() 최상단에서 호출됨 → Spring/Logback 초기화보다 먼저 실행 → Logback ConsoleAppender가 UTF-8 스트림을 래핑하게 됨 → 벨트+멜빵 역할.
결과: ✅ Spring Boot Dashboard 디버그 모드 한글 정상 출력
최종 설정 파일 요약
| 파일 | 변경 내용 | 영향 범위 |
|---|---|---|
gradlew.bat | chcp 65001 > nul 추가 | PowerShell/cmd bootRun |
gradlew | DEFAULT_JVM_OPTS 인코딩 플래그 + MSYS chcp 65001 | Git Bash bootRun |
gradle.properties | org.gradle.jvmargs 인코딩 플래그 | Gradle 데몬 JVM |
root build.gradle.kts | subprojects.tasks.withType<JavaExec> jvmArgs | 모든 모듈 bootRun JVM |
각 모듈 application.yml | logging.charset.console/file: UTF-8 | Logback ConsoleAppender |
.vscode/launch.json | 각 구성에 env.JAVA_TOOL_OPTIONS 추가 | VSCode 디버그 JVM |
.vscode/settings.json | java.debug.settings.console: "internalConsole" | VSCode 디버그 출력 위치 |
DotenvUtil.java | System.setOut/setErr(UTF-8) 추가 | 모든 모듈 (공통) |
핵심 교훈
-
실행 경로가 3가지:
gradlew.bat,./gradlew, Debug Adapter. 각각 독립적인 프로세스이므로 한 곳의 설정이 다른 곳에 전파되지 않음. -
vmArgsvsJAVA_TOOL_OPTIONS:vmArgs는 Gradle/IDE 런처가 JVM에 전달해야 적용됨.JAVA_TOOL_OPTIONS는 OS 환경변수로 JVM이 직접 읽음 → 런처가 누구든 무관하게 적용됨. -
integratedTerminalvsinternalConsole: 디버그 세션에서integratedTerminal은 매번 CP949 터미널을 새로 생성함.internalConsole(Debug Console)은 VSCode 자체 UI이므로 코드 페이지 문제 없음. -
Spring Boot Dashboard는
launch.jsonvmArgs를 무시할 수 있음: 확장이 자체 config를 동적 생성하면vmArgs가 전달되지 않음. 반면env는 process 레벨이라 더 낮은 레이어에서 적용됨. -
DotenvUtil.load()의 위치가 핵심: Spring/Logback 초기화 전에System.out을 교체해야 Logback ConsoleAppender가 UTF-8 스트림을 래핑함.SpringApplication.run()이후에 교체하면 이미 Logback이 원래 스트림을 래핑한 뒤라 의미 없음.