Huny's Dev Blog
huny.dev

Python 스트리밍 UTF-8 한글 깨짐 해결하기: 증분 디코더와 종료 검사

청크 경계에서 잘린 한글·이모지를 Python 증분 디코더로 복원한다. 모든 분할 위치를 검증하고 final=True, 빈 청크, 잘못된 바이트 처리 범위를 정리한다.

Hun Jang
Hun Jang Nov 11, 2026

UTF-8 텍스트를 네트워크나 파일에서 나눠 읽을 때 한글이 깨지거나 UnicodeDecodeError가 발생하면, 청크마다 bytes.decode()를 호출하는 부분부터 확인한다. 문자 하나의 바이트가 두 청크에 나뉠 수 있으므로, 스트림마다 증분 디코더 하나를 유지하고 정상 종료 시 final=True로 남은 바이트를 검사한다. Python 예제로 한글과 이모지의 모든 2분할 경계를 검증하고, 불완전한 입력을 검출하는 방법을 살펴본다.

분리된 청록색 데이터 조각이 주황색 버퍼를 지나 연속된 리본으로 이어지는 일러스트

청크 경계가 문자 경계와 다른 이유

한글 한의 UTF-8 표현은 ed 95 9c다. 첫 청크에 ed만 들어오면 문자열로 완성할 수 없다. 다음 청크가 95 9c로 시작해도 각 청크를 독립적으로 디코딩하면 앞 청크의 시작 바이트를 알 수 없다. 이모지처럼 4바이트인 문자가 있어도 같은 문제가 발생한다.

errors="ignore"는 잘못된 구간을 버리고, errors="replace"는 대체 문자를 넣는다. 정상 문자의 바이트가 청크 사이에 나뉘었을 뿐인 경우에도 이런 처리를 적용하면 원본 텍스트를 잃을 수 있다. 증분 디코더는 아직 완성되지 않은 바이트를 내부에 보관해 다음 입력과 함께 처리한다. Python codecs 문서 · 오류 처리 방식

스트림마다 디코더 하나 유지하기

아래 코드를 incremental_utf8.py로 저장하고 python3 incremental_utf8.py를 실행한다. 표준 라이브러리만 사용한다. decode_utf8_chunks는 유한한 바이트 청크 반복자를 받아, 완성된 텍스트 조각을 차례로 내보내는 제너레이터다.

반복 중에는 final=False를 사용하고, 입력 반복자가 정상적으로 끝났을 때 빈 바이트와 final=True를 전달한다. 이 마지막 호출이 있어야 마지막에 남은 불완전한 문자도 검출한다. strict에서는 잘못된 바이트나 끝까지 완성되지 않은 문자에 대해 UnicodeDecodeError가 발생한다. Python IncrementalDecoder.decode 문서

분할 위치와 종료 상태 확인하기

2026년 11월 11일 Linux 환경에서 Python 3.12.14로 실행한 결과다. 한글🙂ABC는 UTF-8로 13바이트다. 시작과 끝을 포함한 14개 위치에서 두 조각으로 나눠 원문과 같은지 검사했다. 추가로 매 바이트를 별도 청크로 전달하고, 대기 중인 바이트가 있을 때 빈 청크를 끼워 넣는 사례도 확인했다.

예제의 빈 청크는 입력 반복자가 내보낸 일시적인 빈 데이터다. 디코더는 final=False인 빈 입력만으로 종료를 판단하지 않는다. 실제 소켓의 recv()가 빈 바이트를 반환하면 연결 종료를 의미하므로, 수신 루프는 그 시점에 반복을 끝내고 종료 검사를 수행해야 한다. Python 소켓 HOWTO

TTS·JSON·SSE 파이프라인에 연결할 때

증분 디코딩의 출력 조각도 문장이나 JSON 객체 하나와 일치한다고 가정할 수 없다. 텍스트를 복원한 다음, 사용하는 프로토콜의 줄·이벤트·메시지 경계를 별도로 처리한다. TTS 입력이라면 그 후 문장 단위 버퍼링 정책을 적용한다. 서로 다른 응답이나 연결의 바이트를 하나의 디코더에 섞지 않는다.

이 예제는 메모리에서 만든 청크로 디코딩을 검증했다. 실제 TTS API나 HTTP 클라이언트의 수신 동작은 검증하지 않았다. 클라이언트가 이미 디코딩된 문자열을 제공하면 그 인터페이스의 버퍼링·오류 처리 규칙을 먼저 확인한다.

제너레이터를 끝까지 소비해야 종료 검사가 실행된다. 중간 취소나 전송 오류가 발생한 경우는 정상 종료와 구분해 처리한다. 또한 마지막 오류가 발견되기 전에 텍스트 일부가 이미 소비자에게 전달됐을 수 있다. 문서 전체의 유효성이 필요한 저장 작업은 전체 검증을 마친 뒤 결과를 확정한다.

English version · Fix Split UTF-8 Characters in Python Streams with an Incremental DecoderFix Split UTF-8 Characters in Python Streams with an Incremental Decoder

추천 글