RFC 4180 ๊ณต์ ํ์ค ๊ท๊ฒฉ, ์ผํ/๊ตฌ๋ถ์ ์ฒ๋ฆฌ, ๋ฐ์ดํ ์ด์ค์ผ์ดํ ๊ท์น, UTF-8 BOM ์ธ์ฝ๋ฉ(์์
ํ๊ธ ๊นจ์ง ๋ฐฉ์ง), CLI ๊ณ ์ ์ฒ๋ฆฌ ๋๊ตฌ(xsv, csvkit, miller, awk), Python/Pandas ๋ฐ RDBMS SQL(PostgreSQL COPY / MySQL LOAD DATA) ๋์ฉ๋ ๋ฐ์ดํฐ ํ์ดํ๋ผ์ธ ์๋ฒฝ ๊ฐ์ด๋
## 1. CSV ํ์ค ๊ท๊ฒฉ & ํ๊ฒฝ ๊ตฌ์ฑ (RFC 4180 & Setup)
### ๐ป ์์คํ
์ค์น ๋ฐ ์คํ ์๊ตฌ์ฌ์ (System Requirements)
| ํญ๋ชฉ | ์ต์ ์ฌ์ (Minimum) | ๊ถ์ฅ ์ฌ์ (Recommended) | ์ฐธ๊ณ ๋ฐ ๋น๊ณ |
| :--- | :--- | :--- | :--- |
| **์ด์์ฒด์ (OS)** | Windows, macOS, Linux (๋ชจ๋ OS ์ง์) | Ubuntu 22.04 LTS+, macOS, Windows 11 | ํฌ๋ก์ค ํ๋ซํผ ํ
์คํธ ํ์ผ ํฌ๋งท |
| **CPU** | 1 ์ฝ์ด ์ด์ (๋จ์ ํ์ผ ์ฒ๋ฆฌ) | 4 ์ฝ์ด ~ 8 ์ฝ์ด ์ด์ | ์๋ฐฑ๋ง ํ(GB ๋จ์) ์ ๋ ฌ/ํํฐ๋ง/์กฐ์ธ ์ ๋ฉํฐ์ค๋ ๋ฉ ํ์ฉ |
| **๋ฉ๋ชจ๋ฆฌ (RAM)** | 512 MB ์ด์ (๋จ์ ํ์ฑ) | 4 GB ~ 16 GB ์ด์ | ๋์ฉ๋ CSV ์ฒ๋ฆฌ ์ ์คํธ๋ฆฌ๋ฐ(Chunk/Line) ํ์ฑ ํ์ |
| **๋์คํฌ (Storage)** | 100 MB ์ด์์ ์ฌ์ ๊ณต๊ฐ | 10 GB ์ด์์ ๊ณ ์ NVMe SSD | ์์ CSV ๋ฐ ๋ณํ๋ Parquet/DB ์คํ ๋ฆฌ์ง ์ ์ฅ |
| **CLI ์ฒ๋ฆฌ ๋๊ตฌ** | `awk`, `cut`, `sed`, Python 3 | `xsv` (Rust ๊ธฐ๋ฐ ์ด๊ณ ์), `csvkit`, `miller` | 100MB ์ด์ ๋์ฉ๋ ํ์ผ์ Rust ๊ธฐ๋ฐ `xsv` ๊ถ์ฅ |
---
### ๐ ๏ธ ๋์ฉ๋ CSV ์ ๋ฌธ ๊ณ ์ CLI ๋๊ตฌ ์ค์น
```bash
# ๐ฆ [1] xsv ์ค์น (Rust ๊ธฐ๋ฐ ์ด๊ณ ์ ์คํธ๋ฆฌ๋ฐ ์ฒ๋ฆฌ๊ธฐ - 1GB ํ์ผ 1์ด ๋ง์ ๋ถ์)
# macOS
brew install xsv
# Linux / Cargo
cargo install xsv
# ๐ [2] csvkit ์ค์น (Python ๊ธฐ๋ฐ ํ๋ถํ ํต๊ณ/SQL ๋ณํ ์ ํธ๋ฆฌํฐ ๋ชจ์)
pip install csvkit
# โก [3] Miller (mlr) ์ค์น (CSV, TSV, JSON ์คํธ๋ฆฌ๋ฐ ๋ ์ฝ๋ ์กฐ์๊ธฐ)
# Ubuntu / Debian
sudo apt install -y miller
# macOS
brew install miller
```
---
## 2. CSV ํต์ฌ ๋ฉํ ๋ชจ๋ธ & RFC 4180 ํ์ค ๊ท๊ฒฉ (Mental Model)
CSV(Comma-Separated Values)๋ ๋จ์ ํ
์คํธ์ฒ๋ผ ๋ณด์ด์ง๋ง, **RFC 4180**์ด๋ผ๋ ์๊ฒฉํ ๊ตญ์ ํ์ค ์ฌ์์ ๊ฐ์ง๋๋ค. ํ๋ ๋ด๋ถ์ ์ผํ(`,`), ํฐ๋ฐ์ดํ(`"`), ์ค๋ฐ๊ฟ(`\n`)์ด ํฌํจ๋ ๋์ ์ด์ค์ผ์ดํ ์ฒ๋ฆฌ๊ฐ ํต์ฌ์
๋๋ค.
### ๐ CSV ํ์ ์ํ ๋จธ์ ๋ฉํ ๋ชจ๋ธ (ASCII Diagram)
```text
[ ์์ CSV ํ
์คํธ ์คํธ๋ฆผ ์
๋ ฅ ]
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CSV Parser State Machine (์ํ ๋จธ์ ) โ
โ โ
โ [ ์ํ: UNQUOTED (์ผ๋ฐ ํ๋) ] โ
โ โโโ ์ผํ(,) ๋ง๋จ โโโบ ํ์ฌ ํ๋ ์ข
๋ฃ ๋ฐ ๋ค์ ์ปฌ๋ผ์ผ๋ก ์ด๋ โ
โ โโโ ์ค๋ฐ๊ฟ(CRLF) โโโบ ํ์ฌ ๋ ์ฝ๋(ํ) ์ข
๋ฃ ๋ฐ ๋ค์ ํ์ผ๋ก ์ด๋ โ
โ โโโ ํฐ๋ฐ์ดํ(") ์์โโโบ [ ์ํ: IN_QUOTES ] ๋ก ์ ์ด โ
โ โ โ
โ [ ์ํ: IN_QUOTES (๋ฐ์ดํ ๋ด๋ถ) ] โผ โ
โ โโโ ์ผํ(,) โโโบ ์์ ๋ฌธ์์ด ๋ฐ์ดํฐ๋ก ์ทจ๊ธ (๋ถ๋ฆฌํ์ง ์์!) โ
โ โโโ ์ค๋ฐ๊ฟ(CRLF) โโโบ ์์ ๋ฌธ์์ด ๋ฐ์ดํฐ๋ก ์ทจ๊ธ (๋ฉํฐ๋ผ์ธ ํ
์คํธ ์ง์) โ
โ โโโ ์ฐ์๋ ํฐ๋ฐ์ดํ("") โโโบ ํ๋์ ํฐ๋ฐ์ดํ(")๋ก ๋์ฝ๋ฉ (Escape) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
[ 2์ฐจ์ ์ ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ: List<Record> ๋งคํ ์๋ฃ ]
```
### ๐ RFC 4180 7๋ ํ์ค ๊ท์ฝ ์์ฝ
1. **ํ ๊ตฌ๋ถ์**: ๊ธฐ๋ณธ์ ์ผ๋ก `CRLF`(`\r\n`)๋ฅผ ์ฌ์ฉํด์ผ ํจ (Linux์ `\n`๋ ํธํ ํ์์์ ํ์ฉ).
2. **ํค๋ ํ**: ์ ํ ์ฌํญ์ด์ง๋ง ์ฒซ ํ์ ์ปฌ๋ผ๋ช
์ ๋ฐฐ์นํ๋ ๊ฒ์ด ์ผ๋ฐ์ ๊ถ์ฅ.
3. **๊ตฌ๋ถ์**: ๊ฐ ํ๋๋ ์ผํ(`,`)๋ก ๊ตฌ๋ถ๋๋ฉฐ ๋ง์ง๋ง ํ๋ ๋ค์๋ ์ผํ๋ฅผ ๋ถ์ด์ง ์์.
4. **๋ฐ์ดํ ๊ฐ์ธ๊ธฐ**: ํ๋ ๊ฐ ๋ด์ **์ผํ(`,`)**, **ํฐ๋ฐ์ดํ(`"`)**, **์ค๋ฐ๊ฟ(CRLF)**์ด ํ๋๋ผ๋ ๋ค์ด๊ฐ๋ฉด ๋ฐ๋์ ํ๋ ์ ์ฒด๋ฅผ ํฐ๋ฐ์ดํ(`"..."`)๋ก ๊ฐ์ธ์ผ ํจ.
5. **๋ฐ์ดํ ์ด์ค์ผ์ดํ**: ํ๋ ๋ฐ์ดํฐ ์์ ํฐ๋ฐ์ดํ๋ **์ฐ์๋ 2๊ฐ์ ํฐ๋ฐ์ดํ(`""`)**๋ก ์นํํด์ผ ํจ (`"She said ""Hello"""`).
6. **๊ณต๋ฐฑ ์ ์ง**: ๋ฐ์ดํ ๋ฐ์ ๊ณต๋ฐฑ๋ ์ ํจํ ๋ฌธ์๋ก ๊ฐ์ฃผ๋๋ฏ๋ก ๋ถํ์ํ ๊ณต๋ฐฑ์ ์ฝ์
ํ์ง ๋ง ๊ฒ.
7. **์ด ๊ฐ์ ์ผ์น**: ๋ชจ๋ ํ์ ํค๋์ ๋์ผํ ๊ฐ์์ ํ๋๋ฅผ ๊ฐ์ ธ์ผ ํจ.
---
## 3. [์ด๋ณด์ 1๋จ๊ณ] 3๋ถ ํฌ๋ก์๋: ์ฌ๋ฐ๋ฅธ CSV ์์ฑ & ๊ฒ์ฆ (Hello World)
### โ ํ์ค ์ค์ ์๋ฒฝํ ์์ (`products.csv`)
```csv
id,product_name,description,price,in_stock
1,๊ฒ์ด๋ฐ ๋
ธํธ๋ถ,"16์ธ์น ๋์คํ๋ ์ด, RTX 4080 ํ์ฌ",2450000,true
2,๊ธฐ๊ณ์ ํค๋ณด๋,"ํน์ง: ์ซ๋ํ ํค๊ฐ, ""์ฒญ์ถ"" ์ค์์น",129000,true
3,์ปค์คํ
์ฅํจ๋,"๋ฉํฐ๋ผ์ธ ์ค๋ช
:
- ๋ฐฉ์ ์ฝํ
- ๋ฏธ๋๋ผ ๋ฐฉ์ง ํจ๋",25000,false
4,์ฌ๋ฌด์ฉ ๋ง์ฐ์ค,๋ฌด์์ ํด๋ฆญ ์ง์,35000,true
```
### โก ์๋ชป๋ CSV vs ์ฌ๋ฐ๋ฅธ CSV ๋น๊ต
```csv
# โ ์๋ชป๋ CSV (ํ์ฑ ์๋ฌ ๋๋ ๋ฐ์ดํฐ ๊นจ์ง ๋ฐ์!)
id,name,comment
1,ํ๊ธธ๋,๊ทธ๊ฐ ๋งํ๋ค: "์๋
", ๋ฐ๊ฐ์ต๋๋ค # ๋ฐ์ดํ ์ด์คํ ๋๋ฝ ๋ฐ ์ผํ ๋
ธ์ถ๋ก ์ปฌ๋ผ ๊ฐ์ ๊นจ์ง!
# โ
์ฌ๋ฐ๋ฅธ RFC 4180 CSV
id,name,comment
1,ํ๊ธธ๋,"๊ทธ๊ฐ ๋งํ๋ค: ""์๋
"", ๋ฐ๊ฐ์ต๋๋ค"
```
### โข ํฐ๋ฏธ๋ 1์ค ๊ฒ์ฆ (Python ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ)
```bash
# ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด Python ๊ธฐ๋ณธ ๋ด์ฅ csv ๋ชจ๋๋ก ํ์ฑ ๊ฒ์ฆ
python3 -c "import csv; [print(f'ํ {i}: {row}') for i, row in enumerate(csv.reader(open('products.csv', encoding='utf-8')))]"
```
```text
ํ 0: ['id', 'product_name', 'description', 'price', 'in_stock']
ํ 1: ['1', '๊ฒ์ด๋ฐ ๋
ธํธ๋ถ', '16์ธ์น ๋์คํ๋ ์ด, RTX 4080 ํ์ฌ', '2450000', 'true']
ํ 2: ['2', '๊ธฐ๊ณ์ ํค๋ณด๋', 'ํน์ง: ์ซ๋ํ ํค๊ฐ, "์ฒญ์ถ" ์ค์์น', '129000', 'true']
ํ 3: ['3', '์ปค์คํ
์ฅํจ๋', '๋ฉํฐ๋ผ์ธ ์ค๋ช
:\n- ๋ฐฉ์ ์ฝํ
\n- ๋ฏธ๋๋ผ ๋ฐฉ์ง ํจ๋', '25000', 'false']
ํ 4: ['4', '์ฌ๋ฌด์ฉ ๋ง์ฐ์ค', '๋ฌด์์ ํด๋ฆญ ์ง์', '35000', 'true']
```
---
## 4. [2๋จ๊ณ] ํต์ฌ ๊ธฐ๋ฅ: ์์
ํ๊ธ ๊นจ์ง & UTF-8 BOM ์ธ์ฝ๋ฉ (Excel & BOM)
### ๐ฃ ์์
(Microsoft Excel)์์ ํ๊ธ์ด ๊นจ์ง๋ ์ด์
Windows์ฉ Microsoft Excel์ CSV ํ์ผ์ ์ด ๋ ๊ธฐ๋ณธ์ ์ผ๋ก **ANSI (CP949/EUC-KR)** ์ธ์ฝ๋ฉ์ผ๋ก ์ธ์ํฉ๋๋ค. ์ผ๋ฐ UTF-8 ํ์ผ์ ์ด๋ฉด `รชยฒยรฌยยดรซยฐย` ์ฒ๋ผ ๋ฌธ์๊ฐ ์์ ํ ๊นจ์ง๋๋ค.
- **ํด๊ฒฐ์ฑ
**: ํ์ผ ๋งจ ์์ 3๋ฐ์ดํธ์ **UTF-8 BOM (Byte Order Mark: `\xEF\xBB\xBF`)**์ ๋ถ์ฌ์ฃผ๋ฉด, ์์
์ด "์ด ํ์ผ์ UTF-8์ด๋ค!"๋ผ๊ณ ์๋ ์ธ์ํ์ฌ ํ๊ธ์ด ๊นจ๋ํ๊ฒ ์ด๋ฆฝ๋๋ค.
---
### ๐ ๏ธ ์ธ์ด/๋๊ตฌ๋ณ UTF-8 BOM ์ฝ์
์๋ผ์ธ ์นํธ์ํธ
#### [Shell / Linux]
```bash
# ๊ธฐ์กด UTF-8 ํ์ผ์ BOM ๊ฐ์ ์ถ๊ฐ
printf '\xEF\xBB\xBF' | cat - original.csv > excel_safe.csv
# ํ์ผ์ BOM ์กด์ฌ ์ฌ๋ถ ํ์ธ
file excel_safe.csv
# ์ถ๋ ฅ: excel_safe.csv: Unicode text, UTF-8 (with BOM) text
```
#### [Python / Pandas]
```python
import pandas as pd
df = pd.DataFrame({'์ด๋ฆ': ['ํ๊ธธ๋', '์ด์์ '], '๋ถ์': ['๊ฐ๋ฐํ', '๊ธฐํํ']})
# encoding='utf-8-sig' ์ต์
์ ์ฃผ๋ฉด UTF-8 BOM์ด ์๋์ผ๋ก ์ฝ์
๋จ!
df.to_csv('output_excel.csv', index=False, encoding='utf-8-sig')
```
#### [Node.js]
```javascript
import fs from 'node:fs';
const csvData = '์ด๋ฆ,์ง๊ธ\n๊น์ฒ ์,์์์ฐ๊ตฌ์\n';
// ๋งจ ์์ UTF-8 BOM(\uFEFF)์ ๋ง๋ถ์ฌ์ ํ์ผ ์ ์ฅ
fs.writeFileSync('report.csv', '\uFEFF' + csvData, 'utf8');
```
---
## 5. [2๋จ๊ณ] ํต์ฌ ๊ธฐ๋ฅ: CLI ๊ณ ์ ๋ถ์ ๋๊ตฌ ์นํธ์ํธ (xsv & csvkit)
์๋ฐฑ๋ง ๊ฑด์ ๋์ฉ๋ CSV ํ์ผ์ ์์
๋ก ์ด์ง ์๊ณ ํฐ๋ฏธ๋์์ ์ฆ์ ๋ถ์/ํํฐ๋งํ๋ ๋ช
๋ น์ด ๋ชจ์์
๋๋ค.
### ๐ฆ `xsv` ์ด๊ณ ์ ๋ช
๋ น์ด ๋ชจ์
```bash
# [1] ์ ์ฒด ๋ฐ์ดํฐ ์ปฌ๋ผ ๋ฐ ํ ๊ฐ์ ํ์ธ
xsv count products.csv
# [2] ์ปฌ๋ผ ํค๋ ๋ชฉ๋ก๊ณผ ์ธ๋ฑ์ค ๋ฒํธ ํ์ธ
xsv headers products.csv
# 1 id
# 2 product_name
# 3 description
# 4 price
# 5 in_stock
# [3] ํน์ ์ปฌ๋ผ๋ง ์ถ์ถํ์ฌ ์ ๋ ฌ (product_name๊ณผ price)
xsv select product_name,price products.csv | xsv sort -R -s price
# [4] ์กฐ๊ฑด ํํฐ๋ง (price ์ปฌ๋ผ์์ 100000 ์ด์์ธ ํญ๋ชฉ ๊ฒ์)
xsv search -s product_name "ํค๋ณด๋" products.csv
# [5] ์ปฌ๋ผ๋ณ ๊ธฐ์ด ํต๊ณ(์ต์๊ฐ, ์ต๋๊ฐ, ํ๊ท ๋ฑ) 0.1์ด ๋ง์ ์ฐ์ถ
xsv stats -s price products.csv | xsv table
# [6] ๋ ๊ฐ์ CSV ํ์ผ์ ํค(Key) ๊ธฐ์ค์ผ๋ก ๊ณ ์ JOIN
xsv join id users.csv user_id orders.csv > joined.csv
```
---
### ๐ `csvkit` ์ ํธ๋ฆฌํฐ ๋ชจ์
```bash
# [1] ์ฝ์์์ ๋งํฌ๋ค์ด ํ์ฒ๋ผ ์์๊ฒ ์์ ์ถ๋ ฅ (Pretty Print)
csvlook products.csv
# [2] ๋ชจ๋ ์ปฌ๋ผ์ ๋ฐ์ดํฐ ํ์
๊ณผ ์ ๋ํฌ ๊ฐ ํต๊ณ ์์ฝ
csvstat products.csv
# [3] CSV๋ฅผ ์ฆ์์์ SQL ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์ทจ๊ธํ์ฌ ์ฟผ๋ฆฌ ์คํ
csvsql --query "SELECT product_name, price FROM products WHERE price > 50000" products.csv
# [4] JSON ๋ฐ์ดํฐ๋ฅผ CSV๋ก ์๋ ๋ณํ
in2csv data.json > data.csv
```
---
## 6. [3๋จ๊ณ] ์ค๋ฌด ์์ฉ: ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ณ ์์ ํ์ฑ (Python & Java)
### โ Python: `csv` ๋ฐ `pandas` ํ์ค ํ์ดํ๋ผ์ธ
```python
import csv
from dataclasses import dataclass
@dataclass
class Product:
id: int
name: str
price: int
# [1] DictReader๋ฅผ ์ฌ์ฉํ ์์ ํ ๋์
๋๋ฆฌ ๊ธฐ๋ฐ ์ฝ๊ธฐ
products = []
with open('products.csv', mode='r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
products.append(Product(
id=int(row['id']),
name=row['product_name'],
price=int(row['price'])
))
# [2] ๋ฐ์ดํ ์๋ ์ด์ค์ผ์ดํ ์ฐ๊ธฐ (QUOTE_MINIMAL ๊ธฐ๋ณธ๊ฐ)
with open('output.csv', mode='w', encoding='utf-8-sig', newline='') as f:
writer = csv.writer(f, quoting=csv.QUOTE_MINIMAL)
writer.writerow(['์์ด๋', '์ํ๋ช
', '๊ฐ๊ฒฉ'])
for p in products:
writer.writerow([p.id, p.name, p.price])
```
---
### โก Java: OpenCSV ๊ธฐ๋ฐ ๋์ฉ๋ ์ฒ๋ฆฌ (`build.gradle`)
```groovy
dependencies {
implementation 'com.opencsv:opencsv:5.9'
}
```
```java
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.nio.charset.StandardCharsets;
public class CsvProcessor {
public static void main(String[] args) throws Exception {
// [1] CSV ์์ ์ฝ๊ธฐ (๋ฐ์ดํ ๋ฐ ๊ฐํ ์๋ฒฝ ์ง์)
try (CSVReader reader = new CSVReader(new FileReader("products.csv", StandardCharsets.UTF_8))) {
String[] nextLine;
while ((nextLine = reader.readNext()) != null) {
System.out.println("์ํ๋ช
: " + nextLine[1] + " / ๊ฐ๊ฒฉ: " + nextLine[3]);
}
}
// [2] CSV ์ฐ๊ธฐ
try (CSVWriter writer = new CSVWriter(new FileWriter("export.csv", StandardCharsets.UTF_8))) {
writer.writeNext(new String[]{"ID", "์ด๋ฆ", "์ค๋ช
"});
writer.writeNext(new String[]{"101", "๋ชจ๋ํฐ", "4K, 144Hz \"์ด๊ณ ํ์ง\""});
}
}
}
```
---
## 7. [3๋จ๊ณ] ์ค๋ฌด ์์ฉ: RDBMS ๋์ฉ๋ ์ํฌํธ & ์ต์คํฌํธ (PostgreSQL & MySQL)
์๋ฐฑ๋ง ๊ฑด์ CSV ๋ฐ์ดํฐ๋ฅผ 1์ด๋น ์์ญ๋ง ํ ์๋๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ผ๊ด ์ ์ฌ(Bulk Load)ํฉ๋๋ค.
### ๐ PostgreSQL: `COPY` ๋ช
๋ น์ด
```sql
-- [1] ์๋ฒ ๋ก์ปฌ ํ์ผ์์ ๊ณ ์ ์ํฌํธ
COPY products(id, product_name, description, price, in_stock)
FROM '/var/lib/postgresql/data/products.csv'
WITH (
FORMAT csv,
HEADER true,
DELIMITER ',',
QUOTE '"',
ESCAPE '"',
ENCODING 'UTF8'
);
-- [2] ํด๋ผ์ด์ธํธ ๋จธ์ ์์ psql์ ํตํ ์๊ฒฉ ์ํฌํธ (\copy)
\copy products FROM 'products.csv' WITH (FORMAT csv, HEADER true);
-- [3] ํ
์ด๋ธ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ CSV๋ก ์ต์คํฌํธ
\copy (SELECT id, product_name, price FROM products WHERE price >= 100000) TO 'expensive_products.csv' WITH (FORMAT csv, HEADER true);
```
---
### ๐ฌ MySQL / MariaDB: `LOAD DATA INFILE`
```sql
-- [1] ๊ณ ์ ๋๋ ๋ฐ์ดํฐ ์ํฌํธ
LOAD DATA LOCAL INFILE '/path/to/products.csv'
INTO TABLE products
CHARACTER SET utf8mb4
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
ESCAPED BY '"'
LINES TERMINATED BY '\r\n'
IGNORE 1 LINES -- ํค๋ ํ ๊ฑด๋๋ฐ๊ธฐ
(id, product_name, description, price, in_stock);
```
---
## 8. ์ค๋ฌด ์ฃผ์์ฌํญ & ํธ๋ฌ๋ธ์ํ
(Best Practices)
### โ ๏ธ ์ด๋ณด์๊ฐ ๊ฐ์ฅ ํํ ๊ฒช๋ 5๋ ์ค์ ๋ฐ ํด๊ฒฐ์ฑ
| ์ฆ์ ๋ฐ ๋ฌธ์ ์ | ์์ธ ๋ถ์ | ํ์คํ ํด๊ฒฐ ๊ฐ์ด๋ |
| :--- | :--- | :--- |
| **์์
์์ ์ ํ๋ฒํธ ์์๋ฆฌ 0 ์ ์ค (`0101234` โ `101234`)** | ์์
์ด ์ซ์๋ก ์๋ ๋ณํํ์ฌ ์์๋ฆฌ 0์ ์ ๊ฑฐํจ | CSV ์์ฑ ์ `="01012345678"` ํ์์ผ๋ก ์์ ๋ฌธ์์ด ์ฒ๋ฆฌํ๊ฑฐ๋, ํ
์คํธ ์ปฌ๋ผ์ผ๋ก ๋ช
์ |
| **๋ฌธ์์ด ๋ด ํฐ๋ฐ์ดํ ํ์ฑ ์๋ฌ (`Unquoted fields...`)** | ๋ฐ์ดํ๋ฅผ ์ด์คํ(`""`)ํ์ง ์๊ณ ์ญ์ฌ๋์(`\"`)๋ก ์๋ชป ์ฒ๋ฆฌ | RFC 4180 ํ์ค์ ํฐ๋ฐ์ดํ 2๊ฐ(`""`)๋ฅผ ์ด์ค์ผ์ดํ๋ก ์ฌ์ฉํด์ผ ํจ |
| **๋ ์ง ํ์์ด ์์
์์ `#` ๊ธฐํธ๋ก ๊นจ์ง** | ๋ ์ง ์์์ด ๊ตญ๊ฐ๋ณ ๋ก์ผ์ผ๊ณผ ์ถฉ๋ํ๊ฑฐ๋ ์ด ํญ ๋ถ์กฑ | ๊ธ๋ก๋ฒ ํ์ค์ธ `YYYY-MM-DD HH:MM:SS` ํฌ๋งท์ผ๋ก ํต์ผ |
| **ํ์ผ ๋งจ ๋์ ๋น ํ์ด ์ฝ์
๋์ด DB ์ ์ฌ ์คํจ** | ๋ง์ง๋ง ์ค๋ฐ๊ฟ ๊ฐํ๋ฌธ์ ๋ค์ ๋น ๋ ์ฝ๋๊ฐ ์ธ์๋จ | ํ์ ์ค์ ์์ `skip_empty_lines = true` ํ์ฑํ ๋๋ ํ์ดํ๋ผ์ธ ์ ์ |
| **๊ตฌ๋ถ์ ํผ๋ (์ ๋ฝ์ CSV ์ธ๋ฏธ์ฝ๋ก `;` vs ์ผํ `,`)** | ์ ๋ฝ ๊ตญ๊ฐ ์์
์ ์์์ ์ `,`๋ก ์ฐ๋ฏ๋ก CSV ๊ตฌ๋ถ์๋ก `;` ์ฑํ | ํ์ผ ์๋จ์ `sep=,` ๋๋ ํฐ๋ธ๋ฅผ ๋ช
์ํ๊ฑฐ๋ TSV(ํญ ๊ตฌ๋ถ์ `\t`) ์ฌ์ฉ ๊ณ ๋ ค |
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์