엑셀 VBA 한글 깨짐·물음표·?? 문제 완벽 해결 가이드

이 글의 목적은 엑셀 VBA에서 한글이 인식되지 않거나 깨지는 다양한 원인을 체계적으로 분류하고, 현장에서 바로 적용 가능한 재현 절차와 검증된 해결 방법을 제공하는 것이다.

1. 현상 분류와 빠른 진단 절차

한글 인식 문제는 입력·저장·표시·호환 네 영역에서 발생한다. 아래 표로 증상과 원인 범주를 먼저 매칭한 후 해당 장의 처방을 적용하면 된다.

증상 가능 원인 즉시 점검 레버
메시지박스·시트에 "???" 표시 폰트 미지원, ANSI 저장, 시스템 로캘 캡션/셀 폰트를 맑은 고딕으로 지정, 코드 저장 인코딩 확인, 시스템 로캘 점검
외부 파일 읽기 시 깨짐(UTF-8) Open #… ANSI I/O, FSO 기본 ANSI ADODB.Stream Charset="utf-8" 사용 또는 FSO TristateTrue
CSV 불러오면 물음표·깨짐 65001 미지정, 지역설정 미적용 Workbooks.OpenText Origin:=65001, Local:=True 사용
사용자 폼 텍스트 상자 입력 시 깨짐 IME 모드, 폰트 MSForms.TextBox.IMEMode, Font=맑은 고딕
파일 저장 후 재개방 시 깨짐 ANSI 저장 xlCSVUTF8 또는 ADODB.Stream로 UTF-8 저장
외부 COM/DB 연동 시 깨짐 연결 문자열/필드 Charset 누락 ADODB.Connection/Stream Charset 명시
주의 : 동일 파일이라도 “읽기 경로”와 “쓰기 경로”의 인코딩이 불일치하면 왕복 시 데이터가 돌이킬 수 없이 손상될 수 있다. 반드시 읽기·쓰기 모두에서 같은 문자셋을 강제해야 한다.

2. 기본 개념: VBA와 인코딩의 상호작용

VBA 내부 문자열은 유니코드(UTF-16)이다. 문제는 입출력 경로 에서 발생한다. 전통적 VBA 파일 I/O( Open # )와 FileSystemObject 기본값은 시스템 ANSI 코드페이지를 따른다. UTF-8, EUC-KR 등 외부 인코딩을 다루려면 ADODB.Stream 또는 FileSystemObject 의 유니코드 옵션을 사용해야 한다. 시트·폼·컨트롤은 글꼴이 한글 글리프를 포함해야 하며, 메시지박스·윈도우 컨트롤은 OS 폰트 렌더링에 의존한다.

3. 가장 흔한 원인별 처방

3.1 메시지박스·Label·셀에서 한글이 ??? 로 보이는 경우

  1. 표시 폰트를 한글 지원 글꼴로 변경한다. 엑셀 셀·도형·폼 컨트롤에 맑은 고딕 또는 Malgun Gothic 을 지정한다.
  2. VBA 코드 저장 인코딩은 VBE가 관리한다. 모듈에 직접 한글 리터럴을 넣었다면 파일 자체는 유니코드로 보존되므로 보통 문제는 입출력 경로에서 발생한다.
  3. 비교·검색이 실패할 때는 Option Compare Binary 가 영향을 줄 수 있다. 한글 대소문자 개념은 없으나 정렬·비교가 의도와 다르면 Option Compare Text 로 논리를 단순화한다.
  
' 셀 폰트 일괄 지정 Sub SetKoreanFont() Cells.Font.Name = "맑은 고딕" End Sub 
  
주의 : VBE 편집기 글꼴은 런타임 표시와 무관하다. 편집기에서 깨져 보인다고 해서 실행 결과도 깨지는 것은 아니다.

3.2 UTF-8 텍스트 파일 읽기 시 깨짐

Open # 또는 FileSystemObject.OpenTextFile 기본값은 ANSI이다. UTF-8 파일은 ADODB.Stream 으로 Charset을 명시해 읽는다.

  
' ADODB.Stream으로 UTF-8 텍스트 읽기 Sub ReadUtf8Text() Dim stm As Object, txt As String Set stm = CreateObject("ADODB.Stream") With stm .Type = 2 ' adTypeText .Mode = 3 ' adModeReadWrite .Charset = "utf-8" .Open .LoadFromFile "C:\data\korean_utf8.txt" txt = .ReadText(-1) ' adReadAll .Close End With Debug.Print txt ' 한글이 정상 출력됨 End Sub 
  

FileSystemObject를 유지하고 싶다면 TristateTrue 로 유니코드 읽기를 강제한다(UTF-16LE 대상).

  
' FSO로 유니코드(UTF-16LE) 텍스트 읽기 Sub ReadUnicodeText() Dim fso As Object, ts As Object, s As String Set fso = CreateObject("Scripting.FileSystemObject") Set ts = fso.OpenTextFile("C:\data\korean_utf16.txt", 1, False, -1) ' ForReading, Create:=False, Format:=TristateTrue(-1) s = ts.ReadAll ts.Close Debug.Print s End Sub 
  
주의 : FSO의 TristateTrue 는 UTF-16을 의미한다. UTF-8 파일에는 적합하지 않다. UTF-8은 ADODB.Stream 을 사용한다.

3.3 UTF-8 CSV·TXT를 엑셀로 불러올 때 깨짐

매크로에서 Workbooks.OpenText 를 사용할 때 Origin:=65001 (UTF-8) 또는 지역설정 반영을 명시한다.

  
' UTF-8 CSV를 올바르게 불러오기 Sub OpenCsvUtf8() Workbooks.OpenText Filename:="C:\data\korean.csv", _ Origin:=65001, DataType:=xlDelimited, Comma:=True, Local:=True End Sub 
  

쿼리테이블 기반이라면 TextFilePlatform = 65001 을 지정한다.

  
' QueryTable로 UTF-8 CSV 로드 Sub LoadCsvWithQueryTable() Dim qt As QueryTable Set qt = ActiveSheet.QueryTables.Add( _ Connection:="TEXT;C:\data\korean.csv", _ Destination:=Range("A1")) With qt .TextFilePlatform = 65001 .TextFileCommaDelimiter = True .Refresh BackgroundQuery:=False End With End Sub 
  

3.4 파일 저장 후 다시 열면 한글이 깨지는 경우

CSV 저장 시 기본 ANSI가 적용될 수 있다. UTF-8로 저장하는 코드를 사용한다.

  
' 통합문서를 UTF-8 CSV로 저장 Sub SaveAsUtf8Csv() ActiveWorkbook.SaveAs Filename:="C:\data\korean.csv", _ FileFormat:=xlCSVUTF8, CreateBackup:=False End Sub 
  

任의 텍스트를 UTF-8로 저장하려면 ADODB.Stream 을 이용한다.

  
' 임의 문자열을 UTF-8로 파일 저장 Sub WriteUtf8Text() Dim stm As Object Set stm = CreateObject("ADODB.Stream") With stm .Type = 2 ' adTypeText .Charset = "utf-8" .Open .WriteText "한글 데이터, UTF-8 저장 테스트", 1 ' adWriteLine(선택) .SaveToFile "C:\data\out_utf8.txt", 2 ' adSaveCreateOverWrite .Close End With End Sub 
  

3.5 사용자 폼·컨트롤 입력 시 깨짐

MSForms.TextBox ComboBox 에는 IMEMode Font 가 있다. 한글 입력 정확도를 위해 IME를 활성화하고 한글 글꼴을 지정한다.

  
' UserForm 초기화 시 폰트와 IME 지정 Private Sub UserForm_Initialize() With Me.TextBox1 .Font.Name = "맑은 고딕" .IMEMode = fmIMEModeOn End With End Sub 
  
주의 : ActiveX 컨트롤의 렌더링은 OS 폰트 서브시스템에 의존한다. 기업 환경에서 영문 글꼴만 배포된 이미지로 PC를 구축했다면 관리자에게 한글 글꼴 배포 정책을 요청해야 한다.

3.6 클립보드·외부 COM(ADODB, CDO 등) 연계 시 깨짐

문자셋을 명시하지 않으면 외부 컴포넌트가 ANSI로 강제할 수 있다. ADODB.Stream · ADODB.Connection 에서 Charset 을 설정한다.

  
' ADODB로 텍스트 필드 UTF-8 강제 Sub AdoWithUtf8() Dim stm As Object Set stm = CreateObject("ADODB.Stream") With stm .Type = 2 .Charset = "utf-8" .Open .WriteText "한글 필드" .Position = 0 Debug.Print .ReadText(-1) .Close End With End Sub 
  

클립보드는 MSForms.DataObject 가 유니코드를 지원한다.

  
' 클립보드로 한글 복사/붙여넣기 Sub ClipboardKorean() Dim dobj As Object Set dobj = CreateObject("Forms.DataObject") dobj.SetText "엑셀 VBA 한글 복사", 1 ' 1 = CF_TEXTFORMAT (유니코드) dobj.PutInClipboard End Sub 
  

3.7 API 선언·바이트 배열 경로에서의 손실

Windows API를 호출할 때 문자열 인자를 ByVal s As String 으로 전달하면 기본적으로 유니코드가 전달된다. 바이트 배열( Byte() )로 변환하는 과정에서 코드페이지 손실이 발생할 수 있다. 변환은 StrConv 를 사용한다.

  
' 문자열-바이트 상호 변환 예시 Sub ConvertBytes() Dim s As String: s = "한글 테스트" Dim b() As Byte b = s ' UTF-16LE 바이트 배열 ' 특정 ANSI로 변환이 필요하면: Dim bAnsi() As Byte bAnsi = StrConv(s, vbFromUnicode,
0) ' 시스템 코드페이지로 ANSI 변환 ' 다시 유니코드로: Dim s2 As String s2 = StrConv(bAnsi, vbUnicode,
0) Debug.Print s2 End Sub 
  
주의 : vbFromUnicode 는 시스템 ANSI 코드페이지를 사용한다. 국제 배포 매크로라면 코드페이지 의존 로직을 피하고 처음부터 끝까지 UTF-8로 통일해야 한다.

4. 시스템 레벨 점검: 비유니코드 프로그램용 언어, UTF-8 베타 옵션

제어판 > 국가 또는 지역 > 관리 탭 의 “비유니코드 프로그램용 언어”가 한글이 아니면 레거시 ANSI 경로에서 물음표가 발생할 수 있다. Windows 10 1903+에는 “전 세계 언어 지원을 위해 Unicode UTF-8 사용” 옵션이 있다. 이는 일부 레거시 앱에 영향을 줄 수 있으므로, 기업 환경에서는 전면 적용 전 파일·매크로 회귀 테스트가 필요하다.

주의 : OS 레벨 변경은 전사적 영향이 크다. 프로젝트 단위 매크로에서는 ADODB.Stream 강제, OpenText의 Origin 지정처럼 애플리케이션 레벨 해결을 우선한다.

5. 데이터 경로별 표준 솔루션 레시피

경로 권장 설정 샘플 코드/옵션
TXT/JSON 읽기 ADODB.Stream + UTF-8 .Charset="utf-8" , .LoadFromFile
TXT/JSON 쓰기 ADODB.Stream + UTF-8 .Charset="utf-8" , .SaveToFile
CSV 불러오기 OpenText Origin 65001 Origin:=65001, Local:=True
CSV 저장 xlCSVUTF8 FileFormat:=xlCSVUTF8
폼 입력 IME On + 한글 폰트 IMEMode=fmIMEModeOn , Font="맑은 고딕"
클립보드 MSForms.DataObject SetText "…" , 1

6. 진단 체크리스트

  1. 문제가 발생하는 “정확한 경로”를 특정한다(입력/저장/표시/연동).
  2. 문제 데이터의 실제 인코딩을 도구로 확인한다(UTF-8 with BOM, UTF-8, UTF-16LE 등).
  3. 매크로의 해당 경로에서 문자셋을 명시했는지 확인한다.
  4. 표시 컨트롤의 폰트가 한글 글리프를 포함하는지 점검한다.
  5. 왕복 테스트를 수행해 손실 여부를 확인한다(읽기→쓰기→재열기).

7. 실무 샘플: JSON UTF-8 파이프라인

  
' UTF-8 JSON 읽어 파싱 후 시트에 적재 Sub LoadJsonUtf8() Dim stm As Object, json As String Set stm = CreateObject("ADODB.Stream") With stm .Type = 2: .Charset = "utf-8": .Open .LoadFromFile "C:\data\korean.json" json = .ReadText(-1) .Close End With ' JSON 파서에 전달(외부 파서 전제) ' ParseJson json Range("A1").Value = json Range("A1").Font.Name = "맑은 고딕" End Sub 
  

8. 실무 샘플: 다국어 안전 CSV 내보내기

  
' 현재 시트 데이터를 UTF-8 CSV로 안전하게 저장 Sub ExportSheetToUtf8Csv() Dim tmp As String: tmp = Environ$("TEMP") & "\_tmp_export.xlsx" ActiveWorkbook.SaveCopyAs tmp Dim wb As Workbook Set wb = Workbooks.Open(tmp) wb.SaveAs Filename:="C:\data\export_utf8.csv", FileFormat:=xlCSVUTF8 wb.Close False Kill tmp End Sub 
  

9. 엣지 케이스와 방어 코드

  • 사용자 PC의 코드페이지가 달라도 동작하도록 모든 외부 파일 I/O는 UTF-8로 고정한다.
  • 비정상 바이트가 포함된 파일을 방어적으로 정규화한다.
  
' UTF-8 파일을 읽되, 디코딩 실패 바이트를 안전 문자로 대체 Function ReadUtf8Safe(path As String) As String Dim stm As Object Set stm = CreateObject("ADODB.Stream") With stm .Type = 2: .Charset = "utf-8": .Open On Error Resume Next .LoadFromFile path ReadUtf8Safe = .ReadText(-1) If Err.Number <> 0 Then ReadUtf8Safe = Replace(ReadUtf8Safe, ChrW(&HFFFD), "?") .Close On Error GoTo 0 End With End Function 
  

10. 품질 보증: 재현 스크립트와 회귀 테스트

  1. 샘플 데이터 세트(UTF-8/UTF-16/ANSI)를 준비한다.
  2. 각 경로별 매크로를 실행하여 결과를 캡처한다.
  3. 왕복 보존 여부를 비교한다. 문자열 길이·해시가 일치해야 한다.
  
' 간단 무결성 체크(길이·SHA1 등은 외부 구현 가정) Sub RoundTripCheck() Dim s As String, t As String s = "테스트 문자열 한글" ' UTF-8 저장 → 읽기 WriteUtf8Text t = ReadUtf8Safe("C:\data\out_utf8.txt") Debug.Print Len(s), Len(t), (s = t) End Sub 
  

11. 자주 하는 실수

  • 파일 읽기는 UTF-8로 했지만 저장은 기본 ANSI로 하는 혼용.
  • CSV 불러오기에 Origin 을 생략하여 자동 판별에 의존.
  • 폼 컨트롤의 폰트를 기본값(영문 폰트)으로 방치.
  • 레거시 API 예제에서 바이트 배열을 무비판적으로 복사.

12. 표준 코드 스니펫 모음

  
' [1] 셀·시트 전역 한글 폰트 Sub UseKoreanFontAll() Cells.Font.Name = "맑은 고딕" End Sub
' [2] UTF-8 파일 일괄 로드 → 시트 기록
Sub LoadAllUtf8InFolder()
Dim fso As Object, f As Object, s As String
Set fso = CreateObject("Scripting.FileSystemObject")
For Each f In fso.GetFolder("C:\data\in").Files
s = ReadUtf8Safe(f.Path)
Cells(Rows.Count,
1).End(xlUp).Offset(1).Value = s
Next
End Sub

' [3] OpenText로 UTF-8 CSV 정확 로드
Sub OpenCsv65001(p As String)
Workbooks.OpenText Filename:=p, Origin:=65001, DataType:=xlDelimited, Comma:=True, Local:=True
End Sub

  

FAQ

VBA에서 유니코드 문자열인데 왜 깨지나?

VBA 내부는 UTF-16이 맞다. 다만 파일·클립보드·DB 등 입출력 경로 기본값이 ANSI인 경우가 많아 손실이 발생한다. 경로마다 문자셋을 명시해야 한다.

BOM 없는 UTF-8도 처리 가능한가?

가능하다. ADODB.Stream Charset="utf-8" 을 지정하면 BOM 유무와 무관하게 읽는다. 다만 일부 자동판별 로직은 BOM이 없으면 오탐한다.

CSV를 수동 더블클릭으로 열면 왜 깨지나?

더블클릭은 인코딩 정보를 전달하지 않는다. 매크로나 데이터 가져오기를 통해 Origin 을 65001로 지정해야 한다.

시스템 로캘을 바꾸면 해결되나?

일부 ANSI 경로 문제는 완화되지만 부작용 가능성이 있다. 가급적 애플리케이션 레벨에서 UTF-8을 강제하는 것이 안전하다.

Power Query를 써도 되나?

가능하다. Power Query는 원본 인코딩을 지정할 수 있어 다국어 데이터 파이프라인에 유리하다. 단, 배포 대상 PC에 동일 버전이 있어야 한다.