CodeKitHub
16진수→텍스트 변환이 망가질 때: 바이트를 깨진 글자로 만드는 인코딩 실수들

16진수→텍스트 변환이 망가질 때: 바이트를 깨진 글자로 만드는 인코딩 실수들

게시일 2026년 7월 24일

16진수를 텍스트로 변환하는 일은 절대 실패할 수 없을 것처럼 느껴집니다. 16진수 두 자리가 한 바이트고, 그 바이트를 문자에 대응시키면 끝이니까요. 하지만 실제로는 끊임없이 실패하고, 그 원인이 변환기 고장인 경우는 거의 없습니다. 16진수는 그저 바이트를 적는 방식일 뿐이고, 바이트가 특정 문자를 의미하려면 먼저 합의된 인코딩이 필요하기 때문입니다. 출력이 깨졌을 때 실제로 무슨 일이 벌어지고 있는지 살펴봅시다.

원인 1: 잘못된 문자 인코딩 가정

단연 가장 흔한 원인입니다. 바이트는 UTF-8로 인코딩됐는데(실제 쓰이는 많은 문자가 2–4바이트를 차지합니다), 변환기는 모든 바이트가 정확히 한 문자인 Latin-1이나 순수 ASCII인 것처럼 바이트 단위로 디코딩하는 경우입니다. 그 결과 악센트 붙은 글자, 둥근 따옴표, 긴 대시, 그 밖의 모든 비영어 문자가 올바른 한 글자 대신 두세 개의 깨진 기호로 바뀝니다.

해결책은 언제나 바이트가 원래 쓰인 것과 같은 인코딩으로 디코딩하는 것입니다. 어떤 인코딩인지 모르겠다면, 현대적인 것(웹 콘텐츠, JSON, 대부분의 API)에는 UTF-8이 올바른 기본값입니다. Latin-1/Windows-1252는 대체로 오래된 Windows에서 만들어진 텍스트 파일이나 레거시 이메일 헤더에서만 나타납니다.

원인 2: 바이트 순서(엔디언) 불일치

이 원인은 텍스트가 아닌 다중 바이트 숫자 값에만 해당하지만, 바이너리/텍스트 혼합 데이터를 다루는 16진수→텍스트 도구에서는 충분히 흔해서 짚고 넘어갈 만합니다. 00 01을 빅 엔디언으로 읽으면 1이고, 같은 두 바이트를 리틀 엔디언으로 읽으면 256입니다. 어떤 16진수 문자열이 숫자 길이 접두사나 텍스트 데이터에 박힌 바이너리 필드를 나타낸다면, 잘못된 바이트 순서로 읽으면 그 숫자만 잘못 읽는 게 아닙니다. 도구가 텍스트 시작점을 잘못된 오프셋으로 잡게 되어 그 뒤의 모든 바이트 위치가 어긋납니다.

원인 3: 니블 묶음 오류

16진수는 항상 짝을 이뤄야 합니다 — 두 자리가 한 바이트입니다. 16진수 문자 개수가 홀수라면(한 자리가 잘못 끼었거나, 복사-붙여넣기에서 한 글자가 빠졌거나, 파싱 전에 제거하지 않은 앞쪽 0x), 이후의 모든 쌍이 니블 하나만큼 밀립니다. 오류 지점 이후의 모든 바이트가 완전히 다른, 무관한 문자로 디코딩됩니다. 출력이 “약간 틀린” 게 아니라 그 지점부터 완전히 뒤죽박죽이 되는데, 이건 사실 유용한 진단 단서입니다. 처음엔 멀쩡하다가 문자열 중간부터 망가지는 출력은 정확히 그 위치의 묶음 오류를 가리키며, 전체적으로 더 균일하게 깨지는 잘못된 인코딩 문제와는 구별됩니다.

원인 4: 보이지 않는 바이트와 인쇄 불가 바이트

모든 바이트가 눈에 보이는 문자에 대응하는 건 아닙니다. 제어 문자(0x00–0x1F), 바이트 순서 표식(UTF-8에서 EF BB BF), 각종 유니코드 서식 문자는 “성공적으로” 디코딩되지만 표시 폰트에 따라 아무것도 안 보이거나, 네모 상자나 물음표로 렌더링됩니다. 변환 자체는 완벽하게 올바른데도 디코딩 실패와 똑같아 보일 수 있는 것이죠. 출력 길이는 맞아 보이는데 글자가 빠진 것처럼 보인다면, 디코딩 로직이 잘못됐다고 단정하기 전에 제어 바이트부터 확인하세요.

어떤 경우인지 빠르게 가려내는 법

  1. 16진수 문자열의 문자 개수가 짝수인지 확인하세요. 아니라면 원인 3이니 입력부터 고치세요.
  2. 먼저 UTF-8로, 그다음 Latin-1로 디코딩해 비교해 보세요. 한쪽만 깨끗한 텍스트가 나온다면 원인 1이었던 겁니다.
  3. 첫 글자부터 균일하게 깨져 있다면 인코딩(원인 1)을 의심하고, 처음엔 멀쩡하다가 중간부터 망가진다면 그 위치의 묶음 오류(원인 3)를 의심하세요.
  4. 길이는 예상과 맞는데 특정 글자가 빠지거나 네모 상자로 보인다면, 인코딩을 다시 확인하기보다 제어/서식 바이트(원인 4)를 확인하세요.

16진수→텍스트 변환 자체는 어떤 언어에서든 코드 한 줄입니다. 실제 디버깅 작업은 거의 언제나 변환 로직이 아니라, 이 네 가지 가정 중 어느 것이 소리 없이 틀려 있었는지 알아내는 데 있습니다.

← 블로그로 돌아가기