MFC_C++ 멀티바이트 vs 유니코드
2026. 4. 3. 11:52

 

 

 

 

 

 

 

 

 

 

 

 

 

 

🌟 멀티바이트 vs 유니코드, 도대체 뭐가 다른가?

더보기

C++ / MFC 개발을 하다 보면 char*, wchar_t*, CString, _T() 같은 게 뒤섞여서 빌드 에러가 빵빵 터진다.

오늘은 그 원인인 멀티바이트 vs 유니코드에 대해 정리해본다.


1. 컴퓨터는 문자를 어떻게 저장할까?

컴퓨터는 모든 걸 숫자 (0과 1)로 저장한다.

문자도 마찬가지다. 'A'라는 글자를 저장하려면 "'A'는 숫자 '65'야" 라고 약속을 정해야 한다.

이 약속을 인코딩(Encoding) 이라고 한다.


2. 멀티바이트 (Multi-Byte) 란?

  • 문자 하나를 1바이트 또는 2바이트로 표현하는 방식
  • 영어는 1바이트, 한글/한자 같은 문자는 2바이트 사용
  • 대표적인 인코딩: EUC-KR, CP949

C++에서는?

char ch = 'A';         // 1바이트
char* str = "안녕";    // 한 글자가 2바이트 → 총 4바이트

 

문제점

  • "이 문자가 1바이트짜리인지 2바이트짜리인지" 직접 판단해야 해서 복잡하다.
  • 나라마다 인코딩 방식이 달라서 호환이 안 되는 경우가 많다.

3. 유니코드 (Unicode) 란?

  • 전 세계 모든 문자를 하나의 표준으로 표현하는 방식
  • 영어든 한글이든 일본어든 이모지든 전부 커버
  • C++에서는 문자 하나를 2바이트 (wchat_t)로 표현

C++에서는?

wchar_t ch = L'A';          // 2바이트
wchar_t* str = L"안녕";     // 한 글자도 2바이트 → 총 4바이트 (동일)

 

장점

  • 모든 문자를 동일한 크기로 처리 → 단순하고 안전
  • 국제화 (다국어 지원)에 유리

4. TCHAR와 _T()가 뭔데?

Visual Studio에서는 프로젝트 설정에 따라 멀티바이트 / 유니코드 둘 중 하나를 선택할 수 이다.

그런데 코드를 두 환경 모두에서 동작하게 만들려면?

→ Microsoft가 만든 TCHAR와 _T() 매크로를 쓴다

// 프로젝트 설정이 멀티바이트라면:
//   TCHAR  →  char
//   _T("") →  "문자열"

// 프로젝트 설정이 유니코드라면:
//   TCHAR  →  wchar_t
//   _T("") →  L"문자열"

TCHAR str[] = _T("안녕하세요");  // 설정에 따라 자동으로 변환됨

 

쉽게 말하면 _T()는 "설정에 따라 알아서 맞춰줘" 라는 뜻


5. 그래서 빌드 에러가 왜 나는 거야?

유니코드 프로젝트에서 char*를 그냥 쓰면 에러가 난다.

// 유니코드 프로젝트에서 에러 발생
void SomeFunction(char* szValue);
SomeFunction("Hello");   // const wchar_t* → char* 변환 불가!

// 해결 방법 1: 함수 인자를 wchar_t*로 바꾸기
void SomeFunction(wchar_t* szValue);

// 해결 방법 2: CT2A로 변환해서 넘기기
SomeFunction(CT2A(_T("Hello")));  // wchar_t* → char* 로 변환

6. CT2A / CA2T 변환 매크로

MFC에서는 두 타입을 서로 변환하는 매크로를 제공한다.

 

매크로 변환 방향 의미
CT2A TCHAR* → char* T(유니코드) to ANSI(멀티바이트)
CA2T char* → TCHAR* ANSI(멀티바이트) to T(유니코드)
CString strValue = _T("Mono8");

// CString(유니코드)을 char*로 변환해서 함수에 넘길 때
SetEnumeration(CT2A(strValue), "PixelFormat");
외우는 팁: "T to A" / "A to T" → 화살표 방향으로 기억하자!

7.  정리

구분 멀티바이트 유니코드
문자 크기 1~2 바이트 2 바이트 고정
타입 char wchar_t
문자열 리터럴 "문자열" L"문자열"
MFC 매크로 - _T("문자열")
VS 프로젝트 설정 Mlti-Byte Character Set Unicode Character Set

 

 

 

 

🌟 멀티바이트 → 유니코드 전환할 때 실제로 겪는 에러들

더보기

프로젝트 설정을 Multi-Byte → Unicode 로 바꾸는 순간, 빌드 에러가 한 번에 쏟아진다.

실제로 겪은 에러 3가지와 해결 방법을 정리해봤다.


에러 1. char* vs wchar_t* 타입 불일치

유니코드로 바꾸면 TCHAR가 wchar_t가 되는데,

기존 코드에서 char*를 그대로 쓰고 있으면 타입이 안 맞아서 에러가 난다.

// 에러 발생
// error C2664: 'char *' 형식의 인수를 'const wchar_t *'(으)로 변환할 수 없습니다
char* strName = "Basler";
CString result = strName;   // char* → CString(wchar_t) 불일치!

 

해결방법

// 방법 1: wchar_t* 로 바꾸기
wchar_t* strName = L"Basler";

// 방법 2: _T() 매크로 사용 (권장)
TCHAR* strName = _T("Basler");

// 방법 3: CA2T 로 변환 (char*를 그대로 써야 하는 경우)
char* strName = "Basler";
CString result = CA2T(strName);   // char* → wchar_t* 변환

에러 2. _T() 매크로 관련 에러

_T()를 붙이면 유니코드 환경에서 L"문자열"로 바뀐다.

그런데 char*를 인자로 받는 함수에 _T()를 붙인 문자열을 넘기면 에러가 난다.

// 에러 발생
// error: 'const wchar_t[6]' → 'char *' 변환 불가
void SetEnumeration(char* szValue, char* szNodeName);

SetEnumeration(_T("Mono8"), _T("PixelFormat"));
// 유니코드에서 _T("Mono8") = L"Mono8" (wchar_t*)
// 근데 함수는 char* 를 원함 → 타입 불일치!

 

해결방법

// _T() 를 제거하고 CT2A 로 감싸기
SetEnumeration(CT2A(_T("Mono8")), ("PixelFormat"));
//             ↑ wchar_t* → char* 로 변환

// 또는 처음부터 _T() 없이 char 리터럴로 넘기기
SetEnumeration("Mono8", "PixelFormat");  // 함수 자체가 char*를 받는 경우
핵심 판단 기준:
함수 인자가 char* 인가, wchar_t* 인가를 먼저 확인하자.
char* 를 먼저 받는 함수하면 → _T() 빼거나 CT2A() 로 감싸기
wchar_t* 를 받는 함수라면 → _T() 그대로 쓰기

에러 3. CT2A / CA2T 변환 에러

변환 방형을 반대로 쓰거나, 변환 없이 그냥 대입하면 에러

// 방향 반대로 쓴 경우
char* szResult = CA2T("Hello");   // A→T 인데 결과를 char*에 넣으려 함 → 에러

// 변환 없이 그냥 대입한 경우
CString strValue = _T("Mono8");
char* szValue = strValue;         // CString(wchar_t*) → char* 직접 대입 불가!

 

해결 방법

// CT2A : CString(wchar_t*) → char* 로 변환할 때
CString strValue = _T("Mono8");
CT2A szValue(strValue);           // 이렇게 변수처럼 선언해서 쓰는 것도 가능
SetEnumeration(szValue, "PixelFormat");

// 또는 인라인으로 바로 넘기기
SetEnumeration(CT2A(strValue), "PixelFormat");

// CA2T : char* → CString(wchar_t*) 로 변환할 때
char* szName = "Basler";
CString strName = CA2T(szName);

 

변환 방향 한눈에 정리

[ 멀티바이트 ]         [ 유니코드 ]
   char*       →  CA2T  →  wchar_t* (TCHAR*)
   char*       ←  CT2A  ←  wchar_t* (TCHAR*)

에러 4. 실제 겪은 케이스 → tfopen vs fopen_s

CString strFile을 파일 경로로 받아서 파일을 여는 함수였는데, 

멀티바이트 → 유니코드로 전환하면서 에러가 났다.

// 멀티바이트 코드 (기존)
pFile = _tfopen(strFile, _T("rb"));
// 멀티바이트 환경에서는 _tfopen → fopen 으로 동작해서 문제없었음
// 유니코드로 바꿨더니 에러 발생
fopen_s(&pFile, strFile, "rb");
// fopen_s 는 char* 만 받는데
// 유니코드 환경에서 CString = wchar_t* 이라 타입 불일치!

 

왜 이런 오류가 났나?

함수 인자 타입 비고
-tfopen TCHAR* 프로젝트 설정에 따라 자동 대응
fopen_s char* 항상 멀티바이트만 받음
_wfopen_s wchar_t* 항상 유니코드만 받음

해결 방법

// 방법 1: CT2A 로 CString → char* 변환해서 넘기기
fopen_s(&pFile, CT2A(strFile), "rb");

// 방법 2: 유니코드 전용 함수 _wfopen_s 사용
_wfopen_s(&pFile, strFile, L"rb");

// 방법 3: _tfopen 그대로 유지 (설정에 따라 자동 대응)
pFile = _tfopen(strFile, _T("rb"));
어떤 방법이 가장 좋을까?
외부 라이브러리처럼 char*를 강제로 요구하는 경우 → CT2A 사용
유니코드 환경에서만 쓸 거라면 → _wfopen_s 사용
멀티바이트 / 유니코드 둘 다 지원해야 한다면 → _tfopen 유지가 제일 무난

 

 

 

🌟 전환 작업 체크리스트

더보기
멀티바이트 → 유니코드로 바꿀 때 이 순서로 확인하면 편하다.
  1. 문자열 리터럴 "문자열" → _T("문자열")로 바꾸기
  2. char* 변수 → TCHAR* 또는 wchar_t*로 바꾸기
  3. 외부 라이브러리 함수 (char* 인자) → CT2A() 로 감싸서 넘기기
  4. char*로 받아야 하는 경우 → _T() 제거 or CT2A() 적용
  5. 빌드 후 에러 메시지에서 타입 확인 → 방향에 맞게 CT2A / CA2T 선택 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

'MFC_C++ > Task' 카테고리의 다른 글

MFC_C++ 스레드 (Thread)  (0) 2026.04.20
MFC_C++ CallBack  (0) 2026.03.27
MFC_C++ 윈도우 계산기 리펙토링  (0) 2026.03.19
MFC_C++ 배열  (0) 2026.03.03