Skip to main content

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.batchcp 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.batchcp는 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.ktssubprojects 블록 (기존에 이미 설정됨):

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.jsonvmArgs를 사용하지 않고 자체 launch configuration을 동적으로 생성하는 것으로 추정됨. vmArgs가 JVM에 전달되지 않은 것.


시도 5 — java.debug.settings.console: "integratedTerminal"

"java.debug.settings.console": "integratedTerminal"

결과: ❌ 여전히 깨짐 (출력이 Debug Console → 통합 터미널로 변경되었으나 한글 여전히 깨짐)

이유: integratedTerminal은 디버그 세션마다 새 터미널을 생성함. 새 터미널은 Windows 기본 코드 페이지(CP949)로 시작. gradlew.batchcp 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.jsonenv.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.jsoninternalConsole로 복원

"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.batchcp 65001 > nul 추가PowerShell/cmd bootRun
gradlewDEFAULT_JVM_OPTS 인코딩 플래그 + MSYS chcp 65001Git Bash bootRun
gradle.propertiesorg.gradle.jvmargs 인코딩 플래그Gradle 데몬 JVM
root build.gradle.ktssubprojects.tasks.withType<JavaExec> jvmArgs모든 모듈 bootRun JVM
각 모듈 application.ymllogging.charset.console/file: UTF-8Logback ConsoleAppender
.vscode/launch.json각 구성에 env.JAVA_TOOL_OPTIONS 추가VSCode 디버그 JVM
.vscode/settings.jsonjava.debug.settings.console: "internalConsole"VSCode 디버그 출력 위치
DotenvUtil.javaSystem.setOut/setErr(UTF-8) 추가모든 모듈 (공통)

핵심 교훈

  1. 실행 경로가 3가지: gradlew.bat, ./gradlew, Debug Adapter. 각각 독립적인 프로세스이므로 한 곳의 설정이 다른 곳에 전파되지 않음.

  2. vmArgs vs JAVA_TOOL_OPTIONS: vmArgs는 Gradle/IDE 런처가 JVM에 전달해야 적용됨. JAVA_TOOL_OPTIONS는 OS 환경변수로 JVM이 직접 읽음 → 런처가 누구든 무관하게 적용됨.

  3. integratedTerminal vs internalConsole: 디버그 세션에서 integratedTerminal은 매번 CP949 터미널을 새로 생성함. internalConsole(Debug Console)은 VSCode 자체 UI이므로 코드 페이지 문제 없음.

  4. Spring Boot Dashboard는 launch.json vmArgs를 무시할 수 있음: 확장이 자체 config를 동적 생성하면 vmArgs가 전달되지 않음. 반면 env는 process 레벨이라 더 낮은 레이어에서 적용됨.

  5. DotenvUtil.load()의 위치가 핵심: Spring/Logback 초기화 전에 System.out을 교체해야 Logback ConsoleAppender가 UTF-8 스트림을 래핑함. SpringApplication.run() 이후에 교체하면 이미 Logback이 원래 스트림을 래핑한 뒤라 의미 없음.