BOM(Byte Order Mark)와 CSV

3 min read
Cover

TL;DR

  • CSV를 코드에서 파싱할 때: BOM 제거 → content.replace(/^\uFEFF/, '')
  • CSV를 Excel에서 열 때: BOM 필요 → "\ufeff" + csvData

가상의 시나리오

기획팀에서 CSV 파일을 받았습니다. Excel에서 UTF-8로 저장했다고 하네요.

const records = parse(csvData, { columns: true });
console.log(records[0].name);  // undefined (???)

분명 name 컬럼이 있는데 왜 안 읽히는 걸까요?

Object.keys(records[0])[0].length  // 5
'name'.length  // 4

4글자가 5글자가 됐습니다. charCodeAt(0)으로 찍어보면 65279, 유니코드 U+FEFF가 나오네요.

Excel이 UTF-8 CSV로 저장할 때 파일 맨 앞에 눈에 안 보이는 3바이트(BOM)를 붙이는데, 이게 첫 번째 컬럼명에 딸려 들어왔습니다. 그래서 name이 아니라 \uFEFFname이 된 거죠.

문제를 해결하고 나서, 이번엔 반대 상황을 만났습니다. JavaScript로 CSV 다운로드 기능을 만들었는데요.

const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8;' });

Excel로 열었더니 한글이 전부 깨져있네요. Excel은 BOM이 없으면 UTF-8인지 모릅니다.

같은 BOM인데, 있으면 파싱이 꼬이고 없으면 한글이 깨지는 상황이더군요.


BOM이란?

BOM(Byte Order Mark): 파일 맨 앞에 붙는 눈에 안 보이는 바이트입니다. 인코딩 방식을 알려주는 표식이죠.

  • 바이트: 0xEF 0xBB 0xBF
  • 유니코드: U+FEFF
  • JavaScript: \uFEFF
상황BOM
Excel에서 열 CSV필요
코드에서 파싱제거
JSON 파싱제거

해결 방법

BOM 제거 (파싱할 때)

// 직접 제거
const clean = content.replace(/^\uFEFF/, '');

// csv-parse 옵션
parse(data, { bom: true, columns: true });

// strip-bom 패키지
import stripBom from 'strip-bom';
const clean = stripBom(content);

BOM 추가 (Excel용 CSV 생성할 때)

// 브라우저
const blob = new Blob(["\ufeff" + csvData], { type: 'text/csv;charset=utf-8;' });

// Node.js
fs.writeFileSync('data.csv', '\ufeff' + csvData, 'utf8');

마치며

Excel이 읽을 파일 → BOM 붙이세요 코드가 읽을 파일 → BOM 빼세요

까먹지 않게 공유하려 올립니다.


참고