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 // 44글자가 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 빼세요
까먹지 않게 공유하려 올립니다.