출처: http://www.jiniya.net/lecture/techbox/unicode1.html

MBCS는 한 문자를 표현하는데 여러 바이트를 사용하는 것을 말한다. 일반적인 영문자의 경우 모두 한 바이트로 표시되지만, 한글과 같은 경우에는 두 바이트를 사용한다. 또한 두 바이트를 사용하더라도 인코딩 규칙에 따라서 서로 다른 두 바이트가 생성된다. 따라서 인코딩 규칙이 맞지 않는 경우에는 제대로 된 문서를 읽을 수 없는 불편함이 있다. 이러한 불편함을 해결하기 위해서 나온 것이 유니코드다. 유니코드에 대해선 이미 많은 문서에서 소개가 되었기 때문에 별도로 설명하진 않겠다. 혹시 좀 더 자세한 내용이 궁금하다면 http://www.jinsuk.pe.kr/Unicode/Unicode_intro-kr.html를 방문해 보도록 하자.

Windows NT에 기반한 운영체제의 경우 커널 코드가 유니코드를 통해서 작성 되었다. 문자열을 입력 받는 API의 경우 유니코드 버전이 기본적으로 사용된다. 만약 MBCS 버전의 API를 호출한다면 내부적으로 MBCS문자열을 유니코드로 변경한 후에 다시 유니코드 버전의 API를 호출하게 된다. 따라서 MBCS 버전의 프로그램을 작성하는 것은 NT 기반의 운영체제에서는 비효율 적이다. 이러한 점 때문에 최근에는 대부분의 업체에서 NT 기반의 프로그램은 유니코드 버전을 출시하고 있다. 유니코드와 MBCS 빌드에 상관없이 유연하게 컴파일 되는 프로그램을 만들기 위해서 지켜야할 여섯 가지 기본 원칙에 대해서 알아보자.

첫째, 문자열 타입은 T형을 쓴다. T형의 경우 유니코드, MBCS 빌드에 상관없이 적절한 타입으로 변환되기 때문에 빌드가 변경된다고 해서 따로 타입을 수정해 주는 불편함이 없다. char를 사용할 장소에 TCHAR를, char *를 사용해야 한다면 LPTSTR을, const char *을 사용해야 한다면 LPCTSTR을 사용하도록 한다.

둘째, 문자열 리터럴은 항상 TEXT 매크로로 묶어 둔다. 문자열 리터럴은 프로그램 내에서 사용되는 문자열 상수를 의미한다. 그런데 이러한 문자열 상수의 경우 유니코드 빌드에선 앞에 L을 붙여서 wchar_t형이 되어야 한다. 즉, 보통의 경우엔 "%s\n"과 같이 사용하면 되지만, 유니코드 빌드에선 L"%s\n"로 변경해 주어야 한다. 이러한 작업을 일일이 빌드가 바뀔 때 마다 해주는 것은 매우 번거롭고 오류가 많이 나는 일이다. 이러한 작업을 도와주는 것이 TEXT 매크로다. 위에 언급된 문자열 상수의 경우에도 TEXT("%s\n")과 같이 사용하면 MBCS 빌드에선 "%s\n"이, 유니코드 빌드에선 L"%s\n"이 된다. 매크로가 알아서 처리해 준다. 따라서 문자열 리터럴은 항상 TEXT 매크로로 묶어주도록 하자.

셋째, CRT 문자열 함수는 T타입 함수를 사용한다. T형과 마찬가지로 이러한 함수들은 빌드에 따라 적절한 인자를 입력으로 받는다. 예를 들자면 문자열을 복사하는 strcpy에 대응하는 함수로는 _tcscpy가 있다. _tcscpy는 유니코드 빌드에선 인자를 wchar_t로 받고, MBCS빌드에선 인자를 char로 받는다. 따라서 _tcscpy를 사용하고 인자를 T형을 사용한다면 빌드가 변경된다고 해서 직접 수정해야 하는 부분이 없다. 아래 표에는 일반적으로 자주 사용하는 문자열 함수들과 거기에 대한 T타입 함수를 표시해 두었다. 표를 살펴보면 알겠지만 변환 규칙은 무척 간단하다. str로 시작하는 함수는 _tcs로 치환 하면 되고, 그렇지 않은 함수들은 앞에 _t를 붙이면 된다. 물론 그렇지 않은 함수들도 일부 있다. 그러한 함수들은 MSDN을 이용하면 손쉽게 T타입 함수명을 알 수 있다.

일반 함수명 T타입 함수명 기능
strcpy _tcscpy 문자열 복사
strncpy _tcsncpy 특정 길이 문자열 복사
strcat _tcscat 문자열 추가
strstr _tcsstr 문자열 검색
strchr _tcschr 문자 검색
strrchr _tcsrchr 역순 문자 검색
strcmp _tcscmp 문자열 비교
stricmp _tcsicmp 대소문자 구분 없이 문자열 비교
mkdir _tmkdir 디렉터리 생성
fopen _tfopen 파일 열기

넷째, 길이와 크기를 구분한다. 문자열에는 길이와 크기가 있다. MBCS 빌드에서 기본적인 문자 단위로 사용되는 char형의 경우 그 크기가 1바이트 이기 때문에 크기와 길이가 동등한 의미를 가진다. 이러한 이유로 기존의 MBCS 빌드에서 사용하던 많은 코드가 크기와 길이를 정확하게 구분하지 않고 혼용해서 사용하고 있다. 하지만 유니코드 환경으로 넘어오면 크기와 길이는 항상 달라진다. 따라서 함수의 인자가 길이와 크기 중 어떠한 것을 요구하는지 판단해서 정확하게 사용하도록 해야 한다.

다섯째, std::string을 사용하는 경우다. std::string의 경우 ANSI 문자열을 표현하도록 선언된 클래스다. 유니코드 문자열을 표현하는데 사용할 수 있는 타입으로 std::wstring이 있다. 이 둘을 빌드 타입이 변경될 때 마다 바꿔줘야 하는 것은 번거로운 일이다. 이쯤되면 std::tstring이 없는지 궁금해 질 것이다. 하지만 std::tstring은 없다. 없으면 우리가 만들어야 한다. std::string의 경우 std::basic_string<char>로 선언된 것이고, std::wstring의 경우 std::basic_string<wchar_t>로 선언된 것이다. 따라서 우리는 아래와 같이 std::tstring을 정의할 수 있다.

  1. namespace std { typedef basic_string<TCHAR> tstring; } 

마지막으로 일부 함수나 메시지 인자의 경우 반드시 char나, wchar_t로 취급해야 하는 경우가 있다. 이러한 경우 부득이하게 빌드에 따라서 문자열을 변환해서 사용해야 하며 빌드가 변경되면 반드시 코드를 수정해야 한다. 따라서 처음 작성시에 안전하게 두 가지 빌드로된 버전을 모두 작성해 놓거나 아니면 다른 빌드에서 컴파일 할 경우 에러가 나도록 처리해 두면 편리하다. 아래와 같은 전처리기를 사용하면 쉽게 작성할 수 있다. 유니코드 빌드에선 _UNICODE가 선언된다는 점을 기억해 두자.

  1. #ifdef _UNICODE  
  2. // 유니코드 빌드에서 수행할 내용  
  3. #else  
  4. // MBCS 빌드에서 수행할 내용  
  5. #endif 

만약 현재 프로그램이 MBCS 기반이고 유니코드 부분을 지금은 신경 쓰지 않아도 된다면 #error 전처리기를 사용하면 된다. 이렇게 하면 유니코드로 빌드 할 경우 컴파일 오류가 발생하기 때문에 어떤 부분이 수정되어야 하는지 바로 찾을 수 있기 때문에 편리하다.

  1. #ifdef _UNICODE  
  2. // 유니코드 부분은 작성 필요  
  3. #error 이 프로그램은 유니코드 빌드로 컴파일되지 않습니다.  
  4. #else  
  5. // MBCS 빌드에서 수행할 내용  
  6. #endif 
AND