문서 작업 가이드
재인코딩 없이 CSV·텍스트 파일의 BOM 제거하기
파일 원본 바이트를 검사해 확인된 UTF-8·UTF-16·UTF-32 서명 하나만 제거하고 나머지 바이트 스트림을 보존한 뒤, 복구 파일을 대상 parser의 인코딩 계약에 맞춰 검증합니다.
작성 및 검수 SimpleWebUtils게시일: 검토일:
이 작업 흐름을 검증한 방법
“데이터베이스 import 전 UTF-8 BOM CSV 복구” 문서 입력을 UTF-8·UTF-16·UTF-32 파일의 BOM 제거에 그대로 넣었습니다. 수행 순서는 “텍스트를 열어 다시 저장하지 말고 파일 바이트 검사하기”, “없거나 불완전한 서명을 보수적으로 처리하기”였고 브라우저 결과를 예시 출력과 대조했습니다. 실패 경계는 “원본 파일이 있는데 디코딩 텍스트 모드 사용하기”, “UTF-32 LE 확인 전에 FF FE만 제거하기” 두 항목으로 나눠 검토했습니다.
EF BB BF만 제거하자 파일은 34바이트에서 31바이트가 되고 id가 첫 header byte가 됐으며, 나머지 CSV를 디코딩 후 재작성하지 않았습니다.
문제 상황
바이트 순서 표시(BOM)는 Unicode 텍스트 스트림의 시작에 놓이는 바이트 배열입니다. UTF-8은 EF BB BF를 선택적인 인코딩 서명으로 사용하고, UTF-16과 UTF-32 서명은 여러 바이트로 된 코드 단위가 big-endian인지 little-endian인지도 나타낼 수 있습니다. 일부 CSV importer, 명령 interpreter, 설정 parser와 데이터 pipeline은 이 바이트를 첫 필드의 일부로 노출하거나 필요한 첫 token을 찾지 못해 거부합니다. 화면에는 정상적인 id가 보여도 importer는 숨은 표시가 붙은 다른 헤더와 비교할 수 있고, script는 0번 바이트의 #!를 요구할 수 있습니다. 복구는 바이트 경계에서 이루어져야 합니다. UTF-16·UTF-32 파일을 디코딩한 뒤 UTF-8로 저장하는 것은 BOM 제거가 아니라 전체 변환입니다. 별도 인코딩 레이블이 없는 UTF-16·UTF-32에서 BOM을 지우면 유일한 바이트 순서 신호까지 사라질 수도 있습니다. 원본을 보존하고 대상 계약을 확인한 뒤 0번 바이트의 완전한 지원 서명만 제거하며, editor 미리보기가 아니라 다운로드 결과물을 검증해야 합니다.
공식 출처와 표준
다음 공식 문서는 이 작업 흐름에서 다루는 형식과 보안 경계의 기준입니다. 도구별 실행 검증은 위에 별도로 기록합니다.
- Encoding Standard
WHATWG
이럴 때 사용하세요
- 화면에는 id나 name으로 보이지만 CSV importer가 첫 열을 알 수 없는 헤더라고 보고합니다.
- UTF-8 script, JSON, XML 또는 설정 파일이 0번 바이트의 ASCII token을 요구하는 parser에서 실패합니다.
- 수신 인터페이스가 BOM 없는 UTF-8 또는 서명 없는 UTF-16LE·UTF-16BE·UTF-32LE·UTF-32BE를 명시적으로 요구합니다.
- 텍스트 editor의 인코딩 추측이 아니라 복구 전후의 정확한 선두 바이트 증거가 필요합니다.
- 원본 파일 바이트가 없고 복사한 문자열이 디코딩된 U+FEFF로 시작하며, UTF-8 텍스트 결과를 받아들일 수 있습니다.
단계
- 1단계
원본을 보존하고 대상 인코딩 계약 기록하기
변경 전에 원본 파일을 복제합니다. 수신 프로그램이 BOM 없는 UTF-8, LE·BE가 명시된 스트림, BOM이 있는 Unicode 파일 중 무엇을 요구하는지 기록합니다. 한 번의 import 실패만으로 요구사항을 추정하지 않습니다.
- 2단계
텍스트를 열어 다시 저장하지 말고 파일 바이트 검사하기
원본 파일을 파일 바이트 모드에 넣습니다. ArrayBuffer로 읽으면 editor가 먼저 디코딩하거나 정규화하지 않은 상태에서 EF BB BF, FE FF, FF FE, 00 00 FE FF, FF FE 00 00을 구분할 수 있습니다.
- 3단계
수동으로 검토 실행하기
파일을 선택한 뒤 직접 검사를 시작합니다. 선택만으로 데이터가 제거돼서는 안 됩니다. 판별 인코딩 레이블, 서명 길이, 입력 크기, 전후 선두 바이트 행을 확인합니다.
- 4단계
없거나 불완전한 서명을 보수적으로 처리하기
서명이 없다고 UTF-8로 단정하지 않습니다. EF BB처럼 일부만 있으면 모든 바이트를 보존하고, 빠진 바이트를 추측하는 대신 전송 손상이나 비텍스트 파일 가능성을 조사합니다.
- 5단계
UTF-16·UTF-32 순서를 모른 채 제거하지 않기
UTF-16·UTF-32라면 charset 레이블, schema, protocol 또는 import 설정이 LE·BE를 이미 정하는지 확인합니다. 그렇지 않으면 BOM을 유지하거나 별도의 인코딩 도구로 의도적인 변환과 왕복 검증을 수행합니다.
- 6단계
다운로드 후 정확한 바이트 변화 검증하기
정상적인 UTF-8 복구 결과는 정확히 3바이트, UTF-16은 2바이트, UTF-32는 4바이트 작아야 합니다. 제거 후 첫 바이트는 원본 서명 바로 다음 바이트와 같아야 하고 이후 순서도 모두 유지돼야 합니다.
- 7단계
실제 parser에서 재시도하고 인접 문제 확인하기
작은 복사본을 import해 첫 헤더, 첫 데이터 행과 열 매핑을 확인합니다. Script가 계속 실패하면 CRLF/LF를 검사하고, 글자가 깨지면 바이트를 반복 삭제하지 말고 문자셋을 별도로 조사합니다.
예시
데이터베이스 import 전 UTF-8 BOM CSV 복구
입력
파일: customers.csv
입력 크기: 34바이트
선두 바이트: EF BB BF 69 64 2C 6E 61 6D 65
화면 헤더: id,name출력
판별: UTF-8 BOM (EF BB BF)
출력 크기: 31바이트
선두 바이트: 69 64 2C 6E 61 6D 65
검증: importer가 첫 필드를 id에 매핑흔한 실수
원본 파일이 있는데 디코딩 텍스트 모드 사용하기
텍스트 상자에는 원본 인코딩 바이트가 남아 있지 않습니다. 바이트 보존이 중요하면 파일 경로를 사용하고, 디코딩 텍스트 출력은 UTF-8임을 전제로 합니다.
UTF-32 LE 확인 전에 FF FE만 제거하기
FF FE 00 00은 4바이트 UTF-32 LE 서명입니다. FF FE에서 멈추는 판별기는 표시의 절반만 제거해 의도한 결과를 망칩니다.
선두가 아닌 U+FEFF 삭제하기
본문의 U+FEFF는 0번 바이트의 BOM이 아니며 실제 내용이나 과거 단어 결합 데이터일 수 있습니다. 코드 포인트 검토를 위해 보존합니다.
BOM 제거가 깨진 글자를 고친다고 가정하기
문자 깨짐은 보통 불필요한 선두 서명보다 잘못된 decoder가 원인입니다. 문자셋을 별도로 판별하거나 확인합니다.
import 성공 전에 원본 덮어쓰기
대상이 복구 사본을 받아들이고 필드 값, 행 수, 구분자, 줄바꿈을 검증할 때까지 손대지 않은 원본 export를 보관합니다.
자주 묻는 질문
왜 BOM은 첫 CSV 열만 바꿀 수 있나요?
서명이 첫 헤더 바이트 앞에 있기 때문입니다. 이를 소비하지 않는 parser는 디코딩된 표시를 첫 필드 이름에 포함할 수 있지만 이후 필드는 정상으로 보입니다.
UTF-8 BOM은 잘못된 형식인가요?
아닙니다. UTF-8에서 바이트 순서를 정하는 용도는 없지만 선택적 서명으로 사용할 수 있습니다. 존재 여부는 파일 형식이나 수신 protocol에 달려 있습니다.
UTF-16과 UTF-32에는 왜 경고가 나오나요?
BOM이 LE·BE 직렬화 순서를 나타낼 수 있기 때문입니다. 다른 레이블이 없는 스트림에서 제거하면 decoder에 필요한 정보가 사라질 수 있습니다.
BOM이 없는 파일도 UTF-16이나 UTF-32일 수 있나요?
가능합니다. Charset 레이블, protocol, schema 또는 프로그램 설정이 BOM 없이 바이트 순서를 정의할 수 있습니다. 서명 부재는 문자셋 판정이 아닙니다.
파일이 BOM의 일부 바이트로만 시작하면 어떻게 되나요?
도구는 잘린 서명 가능성을 알리고 아무것도 제거하지 않습니다. 실제로 바이트가 유실됐는지 알아야 복구할 수 있으며 추측은 정상 데이터를 훼손할 수 있습니다.
선택한 바이트를 변환 서버로 보내나요?
일반 도구 흐름에서 브라우저가 선택한 바이트 배열을 로컬로 읽고 다시 만듭니다. 집계 분석에는 파일 이름, 파일 바이트, 붙여넣은 내용과 결과 내용이 포함되지 않습니다.