목록으로
유틸리티

최신 UTF-8 표준과 한글 깨짐 해결 5가지 핵심 정리

최신 UTF-8 환경은 10년 전과 완전히 다릅니다. 인코딩 선택지가 여럿이던 시절은 사실상 끝났고, 지금은 웹과 운영체제, 데이터베이스 대부분이 UTF-8 하나를 전제로 동작합니다. 전 세계 웹사이트의 98% 이상이 UTF-8을 사용하고 있으며 HTML 표준, JSON 규격, 리눅스 기본 로케일 모두 마찬가지입니다. 그런데도 한글 깨짐, 이모지 저장 실패, 파일 첫 글자에 붙는 정체불명의 기호 같은 문제는 여전히 반복됩니다. 표준 자체보다 표준을 다루는 설정이 어긋나 있기 때문입니다.

최신 UTF-8 표준의 현재 위치

UTF-8이라는 인코딩 방식 자체는 2003년 RFC 3629로 확정된 이후 바뀌지 않았습니다. 이때 최대 길이가 4바이트로 고정됐고, 표현 범위는 U+0000부터 U+10FFFF까지로 정해졌습니다. 초기에 논의되던 5바이트, 6바이트 확장안은 공식적으로 폐기됐습니다.

따라서 "최신 UTF-8"이라는 말은 인코딩 규격이 아니라 그 위에 올라가는 유니코드 문자 집합의 버전을 뜻하는 경우가 대부분입니다. 유니코드 컨소시엄은 매년 9월경 새 버전을 공개하며, 문자와 이모지가 계속 추가되고 있습니다.

구분내용비고
인코딩 규격RFC 36292003년 이후 변경 없음
바이트 길이1~4바이트 가변ASCII는 1바이트 호환
표현 범위U+0000 ~ U+10FFFF약 111만 개 코드포인트
문자 집합유니코드 17.0 (2025년 9월)누적 약 15만 9천 자
참고: 유니코드 버전이 올라가도 기존 문자의 코드포인트는 절대 바뀌지 않습니다. 이를 안정성 보장 정책이라고 하며, 덕분에 오래된 문서도 최신 환경에서 그대로 읽힙니다.

UTF-8 인코딩 구조를 3단계로 이해하기

UTF-8이 널리 쓰이는 이유는 구조가 단순하면서도 자기 동기화가 가능하기 때문입니다. 바이트 하나만 봐도 그것이 문자의 시작인지 이어지는 부분인지 즉시 판별할 수 있습니다.

  1. 첫 바이트가 길이를 알려줍니다. 0으로 시작하면 1바이트, 110으로 시작하면 2바이트, 1110이면 3바이트, 11110이면 4바이트 문자입니다.
  2. 이어지는 바이트는 항상 10으로 시작합니다. 그래서 중간부터 읽어도 문자 경계를 다시 찾을 수 있습니다.
  3. 남은 비트를 이어 붙이면 코드포인트가 됩니다. 별도의 변환표가 필요 없는 계산식 구조입니다.
바이트 수코드포인트 범위대표 문자
1바이트U+0000 ~ U+007F영문, 숫자, 기호
2바이트U+0080 ~ U+07FF라틴 확장, 그리스어, 키릴 문자
3바이트U+0800 ~ U+FFFF한글, 한자, 일본어
4바이트U+10000 ~ U+10FFFF이모지, 고대 문자, 확장 한자

여기서 실무자가 꼭 기억할 지점은 한글 한 글자가 3바이트, 이모지 하나가 4바이트를 차지한다는 사실입니다. 글자 수 제한을 바이트 기준으로 잡아 둔 시스템에서 입력 오류가 나는 이유가 대부분 여기에 있습니다.

주의: 같은 문자를 필요 이상으로 긴 바이트열로 표현하는 오버롱 인코딩은 표준에서 금지됩니다. 과거 이 허점을 이용한 필터 우회 공격이 있었기 때문에, 검증 없이 바이트열을 그대로 신뢰하는 코드는 위험합니다.

BOM과 utf8mb4, 가장 흔한 두 가지 함정

UTF-8 관련 장애의 상당수는 두 가지 설정에서 발생합니다.

첫째는 BOM입니다. 파일 맨 앞에 붙는 EF BB BF 세 바이트로, 윈도우 메모장이나 일부 엑셀 저장 옵션이 자동으로 넣습니다. UTF-8은 바이트 순서가 고정돼 있어 BOM이 필요 없는데도 붙기 때문에, PHP 파일에서는 헤더 전송 오류가 나고 CSV 첫 열 이름이 깨지며 JSON 파서는 파싱에 실패합니다. 해결책은 편집기에서 "UTF-8(BOM 없음)"으로 저장하는 것입니다.

둘째는 MySQL과 MariaDB의 utf8 문제입니다. 이들 데이터베이스에서 utf8은 최대 3바이트만 저장하는 비표준 구현이라 이모지나 일부 확장 한자를 넣으면 오류가 납니다. 반드시 utf8mb4와 utf8mb4_unicode_ci 계열 콜레이션을 사용해야 진짜 UTF-8이 됩니다. 최신 MySQL 8 이상에서는 기본값이 utf8mb4로 바뀌었지만, 오래된 서버에서 마이그레이션한 테이블은 여전히 예전 설정을 물고 있는 경우가 많습니다.

인코딩 문제는 데이터가 깨진 뒤에 고치는 것보다, 저장되기 전 단 한 곳의 설정을 맞추는 편이 언제나 저렴합니다.

한글이 깨질 때 순서대로 확인할 것

문자가 깨졌다면 감으로 고치기보다 데이터가 흐르는 경로를 따라 위에서부터 점검하는 편이 빠릅니다.

  • HTML 헤더: head 최상단에 charset 선언이 있는지 확인합니다. 선언 위치가 늦으면 브라우저가 이미 다른 인코딩으로 해석을 시작합니다.
  • HTTP 응답 헤더: Content-Type에 charset=utf-8이 포함돼야 합니다. 헤더 값이 HTML 선언보다 우선합니다.
  • DB 연결 문자셋: 테이블이 utf8mb4여도 커넥션이 latin1이면 저장 시점에 깨집니다.
  • 파일 자체의 인코딩: 소스 파일이 EUC-KR로 저장돼 있지 않은지 확인합니다.
  • 외부 연동 데이터: 레거시 시스템에서 받은 CSV나 고정 길이 텍스트는 여전히 EUC-KR인 경우가 많습니다.

참고로 비밀번호나 인증 토큰처럼 시스템 간을 오가는 문자열은 인코딩 사고를 피하기 위해 ASCII 범위로 제한하는 편이 안전합니다. 다국어 문자가 섞이면 전송 과정에서 정규화 방식 차이로 값이 달라질 수 있기 때문입니다. 안전한 무작위 문자열이 필요할 때는 비밀번호 생성기를 활용해 문자 범위를 통제하면 이런 위험을 줄일 수 있습니다.

팁: 깨진 글자 모양으로 원인을 역추적할 수 있습니다. "ìë"처럼 라틴 문자가 나오면 UTF-8 데이터를 latin1으로 읽은 경우이고, "?"로 바뀌었다면 저장 시점에 이미 정보가 손실된 상태라 복구가 어렵습니다.

지금 적용할 수 있는 실무 기준

새로 시작하는 프로젝트라면 선택의 여지가 거의 없습니다. 저장은 UTF-8(BOM 없음), 데이터베이스는 utf8mb4, 응답 헤더에는 charset 명시, 이 세 가지를 기본값으로 두면 대부분의 문제가 사라집니다.

여기에 한 가지를 더한다면 문자열 길이 계산 기준을 통일하는 일입니다. 바이트 길이, 코드포인트 개수, 사용자가 인식하는 글자 수는 서로 다릅니다. 국기 이모지 하나는 코드포인트 2개로 이뤄져 있고, 조합형 이모지는 더 길어집니다. 입력 제한이나 미리보기 자르기 기능을 만들 때는 코드포인트가 아니라 사용자가 보는 단위를 기준으로 처리해야 문자가 중간에서 잘리는 사고를 막을 수 있습니다.

인코딩은 잘 동작할 때는 존재감이 없다가 한 번 어긋나면 데이터 복구가 불가능해지는 영역입니다. 구조를 이해하고 설정 지점을 한 번 정리해 두면, 이후에는 다시 들여다볼 일이 거의 없습니다.

자동차 수리가 필요하신가요?

대전 사고차 수리 전문 - 남대전자동차공업사

무료 견적받기