멀티바이트 & 유니코드

Jaemyeong Lee·2024년 7월 18일

게임 서버1

목록 보기
103/220

먼저 용어를 분리하자

문자열 깨짐의 원인은 대부분 용어 혼동에서 시작됩니다.

용어의미
문자 집합(Character Set)어떤 문자를 다루는가 (예: ASCII, Unicode)
코드 포인트(Code Point)문자에 부여된 번호 (예: U+AC00)
인코딩(Encoding)그 번호를 바이트로 저장하는 방식 (UTF-8, UTF-16 등)

핵심:

  • Unicode는 "인코딩"이 아니라 문자 체계(번호 체계)입니다.
  • UTF-8/UTF-16은 Unicode 코드 포인트를 저장하는 인코딩 방식입니다.

ASCII

  • 7비트(0~127) 문자 집합.
  • 영어/기호 중심이며 현대 다국어 서비스에는 부족합니다.
  • ASCII는 UTF-8의 앞부분과 호환됩니다(0~127은 동일 바이트 값).

MBCS / 코드 페이지 기반 방식의 한계

  • 과거 윈도우 로컬 코드 페이지(예: CP949)는 환경 의존성이 큽니다.
  • 같은 바이트라도 코드 페이지가 다르면 다른 문자로 해석됩니다.
  • 따라서 파일/네트워크/DB를 오갈 때 깨짐(mojibake)이 자주 발생합니다.

예시 문제:

  • 서버는 UTF-8, 클라이언트는 CP949로 해석 -> 한글 깨짐
  • 개발자 PC마다 기본 코드 페이지가 달라 재현이 어려운 버그 발생

Unicode + UTF-8/UTF-16 비교

항목UTF-8UTF-16
단위1바이트 코드 유닛2바이트 코드 유닛
영어(ASCII)1B2B
한글(대부분 BMP)3B2B
이모지/일부 확장 문자4B4B(서로게이트 페어)
장점웹/파일/네트워크 표준, ASCII 호환BMP 문자 처리 효율, 일부 플랫폼 API 친화

중요 포인트:

  • UTF-16도 항상 2바이트 고정이 아닙니다(이모지 등은 4바이트).
  • UTF-8은 가변 길이이므로 size()가 곧 "글자 수"가 아닐 수 있습니다.

C++ 문자열 타입 감각

타입의미
std::string바이트 문자열(인코딩은 별도 약속 필요)
std::wstringwide 문자열 (wchar_t 크기는 플랫폼 의존)
std::u16stringUTF-16 코드 유닛 시퀀스
std::u32stringUTF-32 코드 유닛 시퀀스(코드 포인트 접근 쉬움)

주의:

  • Windows에서 wchar_t는 보통 2바이트(UTF-16 계열), Linux에서는 보통 4바이트입니다.
  • 즉, wstring을 네트워크/파일 포맷으로 바로 쓰면 이식성 문제가 생길 수 있습니다.
std::u8string utf8 = u8"AAA 루키스입니다"; // C++20: char8_t 기반 UTF-8
std::wstring wide = L"AAA 루키스입니다"; // 플랫폼별 wchar_t 기반

실무 권장 패턴

  • 외부 경계(파일, DB, 네트워크, 로그)는 UTF-8로 통일하는 전략이 가장 흔합니다.
  • 플랫폼 API 경계에서만 필요한 인코딩으로 변환합니다.
  • 문자열 길이 정책은 "바이트 수 제한인지, 사용자 글자 수 제한인지"를 명확히 구분해야 합니다.

게임/툴 체감 팁:

  • 엔진/OS API와 맞추기 위해 내부에서 UTF-16을 쓰더라도,
    외부 저장 포맷은 UTF-8로 두면 상호운용이 쉽습니다.

깨짐 디버깅 체크리스트

문자열이 깨지면 아래 순서로 확인하면 빠릅니다.

  1. 소스 파일 인코딩(UTF-8 BOM/without BOM) 일관성
  2. 컴파일러/IDE 문자셋 설정(MSVC라면 /utf-8 여부)
  3. 파일 입출력 시 사용한 인코딩과 해석 인코딩 일치 여부
  4. 네트워크 프로토콜에서 문자열 인코딩 명시 여부
  5. 콘솔/로그 뷰어가 해당 인코딩을 올바르게 렌더링하는지

짧은 원칙:

  • "저장 인코딩"과 "표시 인코딩"은 다를 수 있다.
  • 깨짐은 거의 항상 양쪽의 인코딩 합의 실패 문제다.

profile
李家네_공부방

0개의 댓글