์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ”„ CSV ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์™„์ „ ์ •๋ณต ์†Œ์Šค์ฝ”๋“œ ๊ฐ€์ด๋“œ
๐Ÿ’ป ํ”„๋กœ๊ทธ๋žจ / ์†Œ์Šค์ฝ”๋“œ

๐Ÿ”„ CSV ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์™„์ „ ์ •๋ณต ์†Œ์Šค์ฝ”๋“œ ๊ฐ€์ด๋“œ

Python ยท JavaScript ยท Java ยท Go ยท PHP ๋‹ค๊ตญ์–ด ์‹ค์ „ ์˜ˆ์ œ + ์—ฃ์ง€์ผ€์ด์Šค ์™„๋ฒฝ ๋Œ€์‘ ๐Ÿ’ก

๐Ÿ“‹ ๋ชฉ์ฐจ

01CSV์™€ JSON, ์–˜๋„ค ๋„๋Œ€์ฒด ๋ญ์•ผ? ๐Ÿค”
02๋ณ€ํ™˜์ด ์™œ ํ•„์š”ํ•œ์ง€ โ€” ์‹ค๋ฌด ์‹œ๋‚˜๋ฆฌ์˜ค ์ด์ •๋ฆฌ
03CSV ๊ตฌ์กฐ ์™„์ „ ํ•ด๋ถ€ โ€” ์•Œ๊ณ  ๋ณ€ํ™˜ํ•˜์ž
04JSON ๊ตฌ์กฐ ์™„์ „ ํ•ด๋ถ€ โ€” ๋ชฉ์ ์ง€๋ฅผ ์•Œ์•„์•ผ ๊ธธ์ด ๋ณด์ธ๋‹ค
05Python์œผ๋กœ CSV โ†’ JSON ๋ณ€ํ™˜ (๊ธฐ์ดˆ ~ ๊ณ ๊ธ‰)
06JavaScript / Node.js๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ
07Java๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ (Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)
08Go ์–ธ์–ด๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ
09PHP๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ
10์—ฃ์ง€์ผ€์ด์Šค & ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ์™„๋ฒฝ ๊ฐ€์ด๋“œ
11๋Œ€์šฉ๋Ÿ‰ CSV ์ฒ˜๋ฆฌ ์ „๋žต โ€” ๋ฉ”๋ชจ๋ฆฌ ๊ฑฑ์ • ๋
12์‹ค์ „ ํ™œ์šฉ ํŒจํ„ด & ๋งˆ๋ฌด๋ฆฌ

๐Ÿค” 01. CSV์™€ JSON, ์–˜๋„ค ๋„๋Œ€์ฒด ๋ญ์•ผ?

๊ฐœ๋ฐœํ•˜๋‹ค ๋ณด๋ฉด ๋ฐ์ดํ„ฐ ํ˜•์‹ ๋•Œ๋ฌธ์— ๋จธ๋ฆฌ ์‹ธ๋งค๋Š” ์ˆœ๊ฐ„์ด ๊ผญ ํ•œ ๋ฒˆ์”ฉ ์˜จ๋‹ค.
"์—‘์…€์—์„œ ๋ฝ‘์€ CSV ํŒŒ์ผ์ธ๋ฐ, API๋Š” JSON๋งŒ ๋ฐ›๋Š”๋‹ค๊ณ ?!" ๐Ÿซ 
์ด๋Ÿฐ ์ƒํ™ฉ, ํ•œ ๋ฒˆ์ฏค ๊ฒช์–ด๋ดค์ง€? ์˜ค๋Š˜์€ ์ด ๋‘ ์นœ๊ตฌ๋ฅผ ์™„์ „ํžˆ ์ •๋ณตํ•ด๋ณด์ž.

๐Ÿ“„ CSV Comma-Separated Values name,age,city Alice,30,Seoul Bob,25,Busan Carol,28,Incheon โ€ข ๋‹จ์ˆœ ํ…์ŠคํŠธ ๊ตฌ์กฐ โ€ข ์—‘์…€ ํ˜ธํ™˜ ์ตœ๊ฐ• ๐Ÿ”„ ๋ณ€ํ™˜ CSV โ†’ JSON ๐Ÿ—‚๏ธ JSON JavaScript Object Notation [ {"name":"Alice", "age":30, "city":"Seoul"}, {"name":"Bob"...} ] โ€ข ๊ณ„์ธต ๊ตฌ์กฐ ํ‘œํ˜„ โ€ข API ํ‘œ์ค€ ํฌ๋งท ๋ฐ์ดํ„ฐ ํฌ๋งท ๋ณ€ํ™˜ โ€” ๊ฐœ๋ฐœ์ž์˜ ์ผ์ƒ ๐Ÿš€

CSV(Comma-Separated Values)๋Š” ๋ง ๊ทธ๋Œ€๋กœ ์‰ผํ‘œ๋กœ ๊ฐ’์„ ๊ตฌ๋ถ„ํ•˜๋Š” ํ…์ŠคํŠธ ํŒŒ์ผ์ด์•ผ.
์—‘์…€์—์„œ "๋‹ค๋ฅธ ์ด๋ฆ„์œผ๋กœ ์ €์žฅ โ†’ CSV" ํ•˜๋ฉด ๋ฐ”๋กœ ๋‚˜์˜ค๋Š” ๊ทธ ํŒŒ์ผ.
๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋คํ”„, ํ†ต๊ณ„ ๋ฐ์ดํ„ฐ, ํšŒ์› ๋ชฉ๋ก ๊ฐ™์€ ํ‘œ ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ์ „ํ†ต์ ์œผ๋กœ ๋งŽ์ด ์จ์™”์–ด.

JSON(JavaScript Object Notation)์€ 2000๋…„๋Œ€ ์ดˆ๋ฐ˜ ๋”๊ธ€๋ผ์Šค ํฌ๋กํฌ๋“œ๊ฐ€ ์ •๋ฆฝํ•œ ๋ฐ์ดํ„ฐ ๊ตํ™˜ ํฌ๋งท์ด์•ผ.
์ด๋ฆ„์— JavaScript๊ฐ€ ๋“ค์–ด๊ฐ€์ง€๋งŒ ์‚ฌ์‹ค์ƒ ์–ธ์–ด ๋…๋ฆฝ์ ์ธ ๋ฒ”์šฉ ํฌ๋งท์œผ๋กœ ์ž๋ฆฌ์žก์•˜๊ณ ,
ํ˜„์žฌ REST API, NoSQL DB, ์„ค์ • ํŒŒ์ผ ๋“ฑ ๊ฑฐ์˜ ๋ชจ๋“  ๊ณณ์—์„œ ์“ฐ์ด๋Š” ์‚ฌ์‹ค์ƒ์˜ ํ‘œ์ค€์ด์•ผ. ๐ŸŒ

๐Ÿ“„ CSV ํŠน์ง•

โœ… ํŒŒ์ผ ํฌ๊ธฐ ์ž‘์Œ
โœ… ์—‘์…€ ์ง์ ‘ ์—ด๊ธฐ ๊ฐ€๋Šฅ
โœ… ์‚ฌ๋žŒ์ด ์ฝ๊ธฐ ์‰ฌ์›€
โŒ ๊ณ„์ธต ๊ตฌ์กฐ ํ‘œํ˜„ ๋ถˆ๊ฐ€
โŒ ๋ฐ์ดํ„ฐ ํƒ€์ž… ๊ตฌ๋ถ„ ์—†์Œ
โŒ ํ‘œ์ค€ ์ŠคํŽ™์ด ๋А์Šจํ•จ
๐Ÿ—‚๏ธ JSON ํŠน์ง•

โœ… ๊ณ„์ธต ๊ตฌ์กฐ ํ‘œํ˜„ ๊ฐ€๋Šฅ
โœ… ๋ฐ์ดํ„ฐ ํƒ€์ž… ๋ช…ํ™•
โœ… API ํ‘œ์ค€ ํฌ๋งท
โœ… ํŒŒ์‹ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ’๋ถ€
โŒ CSV๋ณด๋‹ค ํŒŒ์ผ ํฌ๊ธฐ ํผ
โŒ ์—‘์…€ ์ง์ ‘ ์—ด๊ธฐ ๋ถˆํŽธ

๐Ÿข 02. ๋ณ€ํ™˜์ด ์™œ ํ•„์š”ํ•œ์ง€ โ€” ์‹ค๋ฌด ์‹œ๋‚˜๋ฆฌ์˜ค ์ด์ •๋ฆฌ

"๊ทธ๋ƒฅ ์ฒ˜์Œ๋ถ€ํ„ฐ JSON์œผ๋กœ ์“ฐ๋ฉด ๋˜์ง€ ์•Š๋‚˜?" ๋ผ๊ณ  ์ƒ๊ฐํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ,
ํ˜„์‹ค์€ ๊ทธ๋ ‡๊ฒŒ ํ˜ธ๋ฝํ˜ธ๋ฝํ•˜์ง€ ์•Š์•„. ๐Ÿ˜… ์‹ค๋ฌด์—์„œ CSV โ†’ JSON ๋ณ€ํ™˜์ด ํ•„์š”ํ•œ ์ƒํ™ฉ์„ ๋ณด์ž.

1
๋ ˆ๊ฑฐ์‹œ ์‹œ์Šคํ…œ ์—ฐ๋™
์˜ค๋ž˜๋œ ERP, ํšŒ๊ณ„ ์‹œ์Šคํ…œ๋“ค์€ ๋Œ€๋ถ€๋ถ„ CSV๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด๋ณด๋‚ด. ๊ทผ๋ฐ ์ƒˆ๋กœ ๋งŒ๋“œ๋Š” API ์„œ๋ฒ„๋Š” JSON๋งŒ ๋ฐ›๋Š” ์ƒํ™ฉ. ์ด ๊ฐญ์„ ๋ฉ”์›Œ์•ผ ํ•ด.
2
๋ฐ์ดํ„ฐ ๋ถ„์„ ํŒŒ์ดํ”„๋ผ์ธ
๋ฐ์ดํ„ฐ ํŒ€์—์„œ ์—‘์…€๋กœ ์ •๋ฆฌํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐœ๋ฐœํŒ€ API์— ๋„ฃ์–ด์•ผ ํ•  ๋•Œ. ๊ธฐํš์ž๊ฐ€ CSV๋กœ ์ฃผ๋ฉด ๊ฐœ๋ฐœ์ž๊ฐ€ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•ด์„œ DB์— ๋„ฃ๋Š” ํ๋ฆ„์ด ํ”ํ•ด.
3
๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ž‘์—…
์„œ๋น„์Šค ์ด์ „ ์‹œ ๊ธฐ์กด DB ๋ฐ์ดํ„ฐ๋ฅผ CSV๋กœ ๋คํ”„ โ†’ ์ƒˆ ์‹œ์Šคํ…œ API์— JSON์œผ๋กœ ์—…๋กœ๋“œํ•˜๋Š” ํŒจํ„ด์ด ๋งค์šฐ ์ผ๋ฐ˜์ ์ด์•ผ.
4
์™ธ๋ถ€ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
๊ณต๊ณต๋ฐ์ดํ„ฐํฌํ„ธ, ํ†ต๊ณ„์ฒญ ๋“ฑ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” ๋Œ€๋ถ€๋ถ„ CSV์•ผ. ์ด๊ฑธ ์›น ์•ฑ์—์„œ ์“ฐ๋ ค๋ฉด JSON์œผ๋กœ ๋ณ€ํ™˜์ด ํ•„์š”ํ•ด.
5
์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ ๊ฐœ๋ฐœ
์žฌ๋Šฅ๋„ท์ฒ˜๋Ÿผ ๋‹ค์–‘ํ•œ ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ค๋ฃจ๋Š” ํ”Œ๋žซํผ์—์„œ๋„ ํšŒ์› ๋ฐ์ดํ„ฐ, ๊ฑฐ๋ž˜ ๋‚ด์—ญ ๋“ฑ์„ CSV๋กœ ๋ฐ›์•„์„œ JSON API๋กœ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„.
๐Ÿ’ก ํ•ต์‹ฌ ํฌ์ธํŠธ: CSV โ†’ JSON ๋ณ€ํ™˜์€ ๋‹จ์ˆœํ•œ ํฌ๋งท ๋ณ€๊ฒฝ์ด ์•„๋‹ˆ๋ผ, ๋ฐ์ดํ„ฐ ํŒŒ์ดํ”„๋ผ์ธ์˜ ํ•ต์‹ฌ ์—ฐ๊ฒฐ๊ณ ๋ฆฌ์•ผ. ์ž˜ ๋งŒ๋“ค์–ด๋‘๋ฉด ๋‘๊ณ ๋‘๊ณ  ์จ๋จน์„ ์ˆ˜ ์žˆ์–ด!

๐Ÿ”ฌ 03. CSV ๊ตฌ์กฐ ์™„์ „ ํ•ด๋ถ€ โ€” ์•Œ๊ณ  ๋ณ€ํ™˜ํ•˜์ž

๋ณ€ํ™˜ ์ฝ”๋“œ๋ฅผ ์งœ๊ธฐ ์ „์— CSV๊ฐ€ ์–ด๋–ป๊ฒŒ ์ƒ๊ฒผ๋Š”์ง€ ์ œ๋Œ€๋กœ ์•Œ์•„์•ผ ํ•ด.
"๊ทธ๋ƒฅ ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„ํ•˜๋Š” ๊ฑฐ ์•„๋ƒ?" ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋‚˜์ค‘์— ์—ฃ์ง€์ผ€์ด์Šค์—์„œ ํ„ฐ์ง„๋‹ค. ๐Ÿ’ฃ

๐Ÿ“Œ ๊ธฐ๋ณธ ๊ตฌ์กฐ

<span class="tx-comment"># ๊ธฐ๋ณธ CSV ๊ตฌ์กฐ</span>
<span class="tx-string">name,age,city,score</span>
<span class="tx-string">Alice,30,Seoul,95.5</span>
<span class="tx-string">Bob,25,Busan,88.0</span>
<span class="tx-string">Carol,28,Incheon,92.3</span>

์ฒซ ๋ฒˆ์งธ ์ค„์ด ํ—ค๋”(Header)์•ผ โ€” ๊ฐ ์ปฌ๋Ÿผ์˜ ์ด๋ฆ„์„ ์ •์˜ํ•ด.
๋‘ ๋ฒˆ์งธ ์ค„๋ถ€ํ„ฐ๊ฐ€ ๋ฐ์ดํ„ฐ ํ–‰(Row)์ด๊ณ , ๊ฐ ๊ฐ’์€ ์‰ผํ‘œ๋กœ ๊ตฌ๋ถ„๋ผ.

โš ๏ธ ์•Œ์•„์•ผ ํ•  CSV์˜ ํ•จ์ •๋“ค

ํ•จ์ • 1: ๊ฐ’ ์•ˆ์— ์‰ผํ‘œ๊ฐ€ ์žˆ์„ ๋•Œ "Seoul, Gangnam",30,Alice
โ†’ ๊ฐ’์— ์‰ผํ‘œ๊ฐ€ ํฌํ•จ๋˜๋ฉด ํฐ๋”ฐ์˜ดํ‘œ๋กœ ๊ฐ์‹ธ์•ผ ํ•ด. ์ด๊ฑธ ์ฒ˜๋ฆฌ ๋ชปํ•˜๋ฉด ํŒŒ์‹ฑ์ด ๊นจ์ ธ.
ํ•จ์ • 2: ๊ฐ’ ์•ˆ์— ์ค„๋ฐ”๊ฟˆ์ด ์žˆ์„ ๋•Œ "์ฒซ ๋ฒˆ์งธ ์ค„
๋‘ ๋ฒˆ์งธ ์ค„",30,Alice
โ†’ ๋ฉ€ํ‹ฐ๋ผ์ธ ๊ฐ’๋„ ํฐ๋”ฐ์˜ดํ‘œ๋กœ ๊ฐ์‹ธ. ์ด๊ฑธ ๋ชจ๋ฅด๋ฉด ํ–‰ ํŒŒ์‹ฑ์ด ์™„์ „ํžˆ ๋ง๊ฐ€์ ธ.
ํ•จ์ • 3: ํฐ๋”ฐ์˜ดํ‘œ ์ด์Šค์ผ€์ดํ”„ "๊ทธ๋Š” ""์•ˆ๋…•""์ด๋ผ๊ณ  ๋งํ–ˆ๋‹ค",30
โ†’ ๊ฐ’ ์•ˆ์— ํฐ๋”ฐ์˜ดํ‘œ๊ฐ€ ์žˆ์œผ๋ฉด ๋‘ ๋ฒˆ ์จ์„œ ์ด์Šค์ผ€์ดํ”„ํ•ด. "" = "
ํ•จ์ • 4: ์ธ์ฝ”๋”ฉ ๋ฌธ์ œ ํ•œ๊ธ€ CSV๋Š” UTF-8 ๋˜๋Š” EUC-KR๋กœ ์ €์žฅ๋  ์ˆ˜ ์žˆ์–ด.
ํŠนํžˆ ์œˆ๋„์šฐ ์—‘์…€์—์„œ ์ €์žฅํ•œ CSV๋Š” EUC-KR(CP949)์ธ ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„์„œ ์—ด ๋•Œ ์ธ์ฝ”๋”ฉ ์ง€์ • ํ•„์ˆ˜!
ํ•จ์ • 5: BOM(Byte Order Mark) UTF-8 BOM์ด ๋ถ™์€ CSV๋Š” ์ฒซ ๋ฒˆ์งธ ํ—ค๋” ์•ž์— ๋ณด์ด์ง€ ์•Š๋Š” ๋ฌธ์ž(\ufeff)๊ฐ€ ๋ถ™์–ด.
์ด๊ฑธ ์ฒ˜๋ฆฌ ์•ˆ ํ•˜๋ฉด ์ฒซ ๋ฒˆ์งธ ์ปฌ๋Ÿผ ์ด๋ฆ„์ด ์ด์ƒํ•˜๊ฒŒ ํŒŒ์‹ฑ๋ผ.
โœ… RFC 4180์ด CSV์˜ ๊ณต์‹ ์ŠคํŽ™์ด์•ผ. ์™„๋ฒฝํ•œ ํ‘œ์ค€์€ ์•„๋‹ˆ์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ด ์ŠคํŽ™์„ ๋”ฐ๋ผ. ๋ณ€ํ™˜ ์ฝ”๋“œ ์งค ๋•Œ ์ง์ ‘ ํŒŒ์‹ฑํ•˜์ง€ ๋ง๊ณ  ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์“ฐ๋Š” ๊ฒŒ ์ •๋‹ต์ด์•ผ!

๐Ÿ—‚๏ธ 04. JSON ๊ตฌ์กฐ ์™„์ „ ํ•ด๋ถ€ โ€” ๋ชฉ์ ์ง€๋ฅผ ์•Œ์•„์•ผ ๊ธธ์ด ๋ณด์ธ๋‹ค

CSV๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ "์–ด๋–ค ๋ชจ์–‘์˜ JSON์œผ๋กœ ๋งŒ๋“ค ๊ฒƒ์ธ๊ฐ€"๋ฅผ ๋จผ์ € ๊ฒฐ์ •ํ•ด์•ผ ํ•ด.
์ฃผ๋กœ ์“ฐ์ด๋Š” ํŒจํ„ด์ด ๋ช‡ ๊ฐ€์ง€ ์žˆ๊ฑฐ๋“ .

๐Ÿ“ ํŒจํ„ด 1: ๋ฐฐ์—ด of ๊ฐ์ฒด (๊ฐ€์žฅ ์ผ๋ฐ˜์ )

<span class="tx-comment">// ๊ฐ ํ–‰์ด ํ•˜๋‚˜์˜ ๊ฐ์ฒด๊ฐ€ ๋˜๋Š” ํŒจํ„ด</span>
<span class="tx-keyword">[</span>
  <span class="tx-keyword">{</span> <span class="tx-string">"name"</span>: <span class="tx-string">"Alice"</span>, <span class="tx-string">"age"</span>: <span class="tx-num">30</span>, <span class="tx-string">"city"</span>: <span class="tx-string">"Seoul"</span> <span class="tx-keyword">}</span>,
  <span class="tx-keyword">{</span> <span class="tx-string">"name"</span>: <span class="tx-string">"Bob"</span>,   <span class="tx-string">"age"</span>: <span class="tx-num">25</span>, <span class="tx-string">"city"</span>: <span class="tx-string">"Busan"</span> <span class="tx-keyword">}</span>,
  <span class="tx-keyword">{</span> <span class="tx-string">"name"</span>: <span class="tx-string">"Carol"</span>, <span class="tx-string">"age"</span>: <span class="tx-num">28</span>, <span class="tx-string">"city"</span>: <span class="tx-string">"Incheon"</span> <span class="tx-keyword">}</span>
<span class="tx-keyword">]</span>

๊ฐ€์žฅ ํ”ํ•˜๊ณ  ์ง๊ด€์ ์ธ ํŒจํ„ด. API ์‘๋‹ต, DB ์‚ฝ์ž… ๋“ฑ ๋Œ€๋ถ€๋ถ„์˜ ์šฉ๋„์— ์ ํ•ฉํ•ด.

๐Ÿ“ ํŒจํ„ด 2: ๋ž˜ํผ ๊ฐ์ฒด ํฌํ•จ

<span class="tx-comment">// ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ํ•จ๊ป˜ ๋‹ด๋Š” ํŒจํ„ด</span>
<span class="tx-keyword">{</span>
  <span class="tx-string">"total"</span>: <span class="tx-num">3</span>,
  <span class="tx-string">"data"</span>: <span class="tx-keyword">[</span>
    <span class="tx-keyword">{</span> <span class="tx-string">"name"</span>: <span class="tx-string">"Alice"</span>, <span class="tx-string">"age"</span>: <span class="tx-num">30</span> <span class="tx-keyword">}</span>,
    <span class="tx-keyword">{</span> <span class="tx-string">"name"</span>: <span class="tx-string">"Bob"</span>,   <span class="tx-string">"age"</span>: <span class="tx-num">25</span> <span class="tx-keyword">}</span>
  <span class="tx-keyword">]</span>
<span class="tx-keyword">}</span>

๐Ÿ“ ํŒจํ„ด 3: ํ‚ค-๊ฐ’ ๋งต (ํŠน์ • ์ปฌ๋Ÿผ์„ ํ‚ค๋กœ)

<span class="tx-comment">// name์„ ํ‚ค๋กœ ์‚ฌ์šฉํ•˜๋Š” ํŒจํ„ด</span>
<span class="tx-keyword">{</span>
  <span class="tx-string">"Alice"</span>: <span class="tx-keyword">{</span> <span class="tx-string">"age"</span>: <span class="tx-num">30</span>, <span class="tx-string">"city"</span>: <span class="tx-string">"Seoul"</span> <span class="tx-keyword">}</span>,
  <span class="tx-string">"Bob"</span>:   <span class="tx-keyword">{</span> <span class="tx-string">"age"</span>: <span class="tx-num">25</span>, <span class="tx-string">"city"</span>: <span class="tx-string">"Busan"</span> <span class="tx-keyword">}</span>
<span class="tx-keyword">}</span>

ํŠน์ • ํ‚ค๋กœ ๋น ๋ฅด๊ฒŒ ์กฐํšŒํ•ด์•ผ ํ•  ๋•Œ ์œ ์šฉํ•ด. ๋‹จ, ํ‚ค๊ฐ€ ์œ ๋‹ˆํฌํ•ด์•ผ ํ•ด.

๐Ÿ’ก ๋ฐ์ดํ„ฐ ํƒ€์ž… ์ฃผ์˜! CSV๋Š” ๋ชจ๋“  ๊ฐ’์ด ๋ฌธ์ž์—ด์ด์•ผ. JSON์œผ๋กœ ๋ณ€ํ™˜ํ•  ๋•Œ ์ˆซ์ž๋Š” number๋กœ, ๋ถˆ๋ฆฌ์–ธ์€ boolean์œผ๋กœ ํƒ€์ž… ๋ณ€ํ™˜์„ ํ•ด์ค˜์•ผ ์ œ๋Œ€๋กœ ๋œ JSON์ด ๋ผ. ์ด ์ฒ˜๋ฆฌ๋ฅผ ์•ˆ ํ•˜๋ฉด "age": "30"์ฒ˜๋Ÿผ ์ˆซ์ž๊ฐ€ ๋ฌธ์ž์—ด๋กœ ๋“ค์–ด๊ฐ€๋ฒ„๋ ค.

๐Ÿ 05. Python์œผ๋กœ CSV โ†’ JSON ๋ณ€ํ™˜ (๊ธฐ์ดˆ ~ ๊ณ ๊ธ‰)

Python์€ CSV ๋ณ€ํ™˜์— ์žˆ์–ด์„œ ์ง„์งœ ์ตœ๊ฐ•์ด์•ผ. ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํ•˜๊ณ ,
pandas๊นŒ์ง€ ์“ฐ๋ฉด ๋ชป ํ•˜๋Š” ๊ฒŒ ์—†์–ด. ๋‹จ๊ณ„๋ณ„๋กœ ์‚ดํŽด๋ณด์ž!

๐Ÿ”ฐ ๋ฐฉ๋ฒ• 1: ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์‚ฌ์šฉ (csv + json)

<span class="tx-keyword">import</span> csv
<span class="tx-keyword">import</span> json

<span class="tx-keyword">def</span> <span class="tx-func">csv_to_json_basic</span>(csv_file_path, json_file_path, encoding=<span class="tx-string">'utf-8'</span>):
    <span class="tx-string">"""
    ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ CSV โ†’ JSON ๋ณ€ํ™˜ ํ•จ์ˆ˜
    ์ฒซ ๋ฒˆ์งธ ํ–‰์„ ํ—ค๋”๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐ์—ด of ๊ฐ์ฒด ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜
    """</span>
    result = []
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(csv_file_path, <span class="tx-string">'r'</span>, encoding=encoding, newline=<span class="tx-string">''</span>) <span class="tx-keyword">as</span> csv_file:
        <span class="tx-comment"># DictReader: ์ฒซ ํ–‰์„ ํ—ค๋”๋กœ ์ž๋™ ์ธ์‹</span>
        reader = csv.DictReader(csv_file)
        
        <span class="tx-keyword">for</span> row <span class="tx-keyword">in</span> reader:
            <span class="tx-comment"># OrderedDict โ†’ ์ผ๋ฐ˜ dict๋กœ ๋ณ€ํ™˜</span>
            result.append(<span class="tx-func">dict</span>(row))
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(json_file_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> json_file:
        json.<span class="tx-func">dump</span>(result, json_file, ensure_ascii=<span class="tx-keyword">False</span>, indent=<span class="tx-num">2</span>)
    
    <span class="tx-func">print</span>(<span class="tx-string">f"โœ… ๋ณ€ํ™˜ ์™„๋ฃŒ: {len(result)}๊ฐœ ํ–‰ ์ฒ˜๋ฆฌ๋จ"</span>)
    <span class="tx-keyword">return</span> result

<span class="tx-comment"># ์‚ฌ์šฉ ์˜ˆ์‹œ</span>
data = <span class="tx-func">csv_to_json_basic</span>(<span class="tx-string">'input.csv'</span>, <span class="tx-string">'output.json'</span>)
โœ… ensure_ascii=False๋ฅผ ๊ผญ ๋„ฃ์–ด์ค˜! ์ด๊ฒŒ ์—†์œผ๋ฉด ํ•œ๊ธ€์ด \uD55C\uAE00 ๊ฐ™์€ ์œ ๋‹ˆ์ฝ”๋“œ ์ด์Šค์ผ€์ดํ”„๋กœ ์ €์žฅ๋ผ์„œ ์ฝ๊ธฐ ํž˜๋“ค์–ด์ ธ.

๐Ÿ”ฅ ๋ฐฉ๋ฒ• 2: ํƒ€์ž… ์ž๋™ ๋ณ€ํ™˜ ํฌํ•จ (์‹ค๋ฌด ์ถ”์ฒœ)

<span class="tx-keyword">import</span> csv
<span class="tx-keyword">import</span> json

<span class="tx-keyword">def</span> <span class="tx-func">auto_convert_type</span>(value):
    <span class="tx-string">"""๋ฌธ์ž์—ด ๊ฐ’์„ ์ ์ ˆํ•œ ํƒ€์ž…์œผ๋กœ ์ž๋™ ๋ณ€ํ™˜"""</span>
    <span class="tx-keyword">if</span> value == <span class="tx-string">''</span> <span class="tx-keyword">or</span> value <span class="tx-keyword">is</span> <span class="tx-keyword">None</span>:
        <span class="tx-keyword">return</span> <span class="tx-keyword">None</span>
    
    <span class="tx-comment"># ๋ถˆ๋ฆฌ์–ธ ์ฒดํฌ</span>
    <span class="tx-keyword">if</span> value.lower() <span class="tx-keyword">in</span> (<span class="tx-string">'true'</span>, <span class="tx-string">'yes'</span>, <span class="tx-string">'1'</span>):
        <span class="tx-keyword">return</span> <span class="tx-keyword">True</span>
    <span class="tx-keyword">if</span> value.lower() <span class="tx-keyword">in</span> (<span class="tx-string">'false'</span>, <span class="tx-string">'no'</span>, <span class="tx-string">'0'</span>):
        <span class="tx-keyword">return</span> <span class="tx-keyword">False</span>
    
    <span class="tx-comment"># ์ •์ˆ˜ ์ฒดํฌ</span>
    <span class="tx-keyword">try</span>:
        int_val = <span class="tx-func">int</span>(value)
        <span class="tx-keyword">return</span> int_val
    <span class="tx-keyword">except</span> ValueError:
        <span class="tx-keyword">pass</span>
    
    <span class="tx-comment"># ์‹ค์ˆ˜ ์ฒดํฌ</span>
    <span class="tx-keyword">try</span>:
        float_val = <span class="tx-func">float</span>(value)
        <span class="tx-keyword">return</span> float_val
    <span class="tx-keyword">except</span> ValueError:
        <span class="tx-keyword">pass</span>
    
    <span class="tx-comment"># ๊ทธ ์™ธ๋Š” ๋ฌธ์ž์—ด ๊ทธ๋Œ€๋กœ</span>
    <span class="tx-keyword">return</span> value


<span class="tx-keyword">def</span> <span class="tx-func">csv_to_json_typed</span>(csv_path, json_path, encoding=<span class="tx-string">'utf-8'</span>):
    <span class="tx-string">"""ํƒ€์ž… ์ž๋™ ๋ณ€ํ™˜์ด ํฌํ•จ๋œ CSV โ†’ JSON ๋ณ€ํ™˜"""</span>
    result = []
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(csv_path, <span class="tx-string">'r'</span>, encoding=encoding, newline=<span class="tx-string">''</span>) <span class="tx-keyword">as</span> f:
        reader = csv.DictReader(f)
        
        <span class="tx-keyword">for</span> row <span class="tx-keyword">in</span> reader:
            typed_row = {
                key: <span class="tx-func">auto_convert_type</span>(value)
                <span class="tx-keyword">for</span> key, value <span class="tx-keyword">in</span> row.items()
            }
            result.append(typed_row)
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(json_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> f:
        json.<span class="tx-func">dump</span>(result, f, ensure_ascii=<span class="tx-keyword">False</span>, indent=<span class="tx-num">2</span>)
    
    <span class="tx-keyword">return</span> result

๐Ÿš€ ๋ฐฉ๋ฒ• 3: BOM ์ฒ˜๋ฆฌ + ๋‹ค์–‘ํ•œ ์ธ์ฝ”๋”ฉ ์ž๋™ ๊ฐ์ง€

<span class="tx-keyword">import</span> csv
<span class="tx-keyword">import</span> json
<span class="tx-keyword">import</span> chardet  <span class="tx-comment"># pip install chardet</span>

<span class="tx-keyword">def</span> <span class="tx-func">detect_encoding</span>(file_path):
    <span class="tx-string">"""ํŒŒ์ผ ์ธ์ฝ”๋”ฉ ์ž๋™ ๊ฐ์ง€"""</span>
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(file_path, <span class="tx-string">'rb'</span>) <span class="tx-keyword">as</span> f:
        raw = f.<span class="tx-func">read</span>(<span class="tx-num">10000</span>)  <span class="tx-comment"># ์ฒ˜์Œ 10KB๋งŒ ์ฝ์–ด์„œ ๊ฐ์ง€</span>
    result = chardet.<span class="tx-func">detect</span>(raw)
    <span class="tx-keyword">return</span> result[<span class="tx-string">'encoding'</span>]


<span class="tx-keyword">def</span> <span class="tx-func">csv_to_json_smart</span>(csv_path, json_path=<span class="tx-keyword">None</span>, 
                       delimiter=<span class="tx-string">','</span>, encoding=<span class="tx-keyword">None</span>):
    <span class="tx-string">"""
    ์Šค๋งˆํŠธ CSV โ†’ JSON ๋ณ€ํ™˜
    - ์ธ์ฝ”๋”ฉ ์ž๋™ ๊ฐ์ง€
    - BOM ์ฒ˜๋ฆฌ
    - ์ปค์Šคํ…€ ๊ตฌ๋ถ„์ž ์ง€์›
    - json_path๊ฐ€ None์ด๋ฉด ๊ฒฐ๊ณผ๋งŒ ๋ฐ˜ํ™˜
    """</span>
    <span class="tx-keyword">if</span> encoding <span class="tx-keyword">is</span> <span class="tx-keyword">None</span>:
        encoding = <span class="tx-func">detect_encoding</span>(csv_path)
        <span class="tx-func">print</span>(<span class="tx-string">f"๐Ÿ” ๊ฐ์ง€๋œ ์ธ์ฝ”๋”ฉ: {encoding}"</span>)
    
    <span class="tx-comment"># utf-8-sig๋Š” BOM์„ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์คŒ</span>
    <span class="tx-keyword">if</span> encoding <span class="tx-keyword">and</span> encoding.lower() == <span class="tx-string">'utf-8'</span>:
        encoding = <span class="tx-string">'utf-8-sig'</span>
    
    result = []
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(csv_path, <span class="tx-string">'r'</span>, encoding=encoding, newline=<span class="tx-string">''</span>) <span class="tx-keyword">as</span> f:
        reader = csv.DictReader(f, delimiter=delimiter)
        
        <span class="tx-comment"># ํ—ค๋” ๊ณต๋ฐฑ ์ œ๊ฑฐ</span>
        reader.fieldnames = [
            field.<span class="tx-func">strip</span>() <span class="tx-keyword">for</span> field <span class="tx-keyword">in</span> reader.fieldnames
        ]
        
        <span class="tx-keyword">for</span> row <span class="tx-keyword">in</span> reader:
            cleaned = {
                k.<span class="tx-func">strip</span>(): v.<span class="tx-func">strip</span>() <span class="tx-keyword">if</span> v <span class="tx-keyword">else</span> <span class="tx-keyword">None</span>
                <span class="tx-keyword">for</span> k, v <span class="tx-keyword">in</span> row.items()
                <span class="tx-keyword">if</span> k <span class="tx-keyword">is</span> <span class="tx-keyword">not</span> <span class="tx-keyword">None</span>
            }
            result.append(cleaned)
    
    <span class="tx-keyword">if</span> json_path:
        <span class="tx-keyword">with</span> <span class="tx-func">open</span>(json_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> f:
            json.<span class="tx-func">dump</span>(result, f, ensure_ascii=<span class="tx-keyword">False</span>, indent=<span class="tx-num">2</span>)
    
    <span class="tx-keyword">return</span> result

โšก ๋ฐฉ๋ฒ• 4: pandas ํ™œ์šฉ (๋ฐ์ดํ„ฐ ๋ถ„์„ ํ™˜๊ฒฝ)

<span class="tx-keyword">import</span> pandas <span class="tx-keyword">as</span> pd
<span class="tx-keyword">import</span> json

<span class="tx-keyword">def</span> <span class="tx-func">csv_to_json_pandas</span>(csv_path, json_path=<span class="tx-keyword">None</span>, orient=<span class="tx-string">'records'</span>):
    <span class="tx-string">"""
    pandas๋ฅผ ์ด์šฉํ•œ CSV โ†’ JSON ๋ณ€ํ™˜
    orient ์˜ต์…˜:
      'records' : [{col1:val, col2:val}, ...]  โ† ๊ฐ€์žฅ ์ผ๋ฐ˜์ 
      'index'   : {idx: {col1:val, ...}, ...}
      'columns' : {col1: {idx:val, ...}, ...}
      'values'  : [[val1, val2, ...], ...]
      'split'   : {columns:[...], index:[...], data:[[...]]}
    """</span>
    <span class="tx-comment"># ์ธ์ฝ”๋”ฉ ์ž๋™ ์ฒ˜๋ฆฌ (utf-8-sig๋กœ BOM ์ฒ˜๋ฆฌ)</span>
    <span class="tx-keyword">try</span>:
        df = pd.<span class="tx-func">read_csv</span>(csv_path, encoding=<span class="tx-string">'utf-8-sig'</span>)
    <span class="tx-keyword">except</span> UnicodeDecodeError:
        df = pd.<span class="tx-func">read_csv</span>(csv_path, encoding=<span class="tx-string">'cp949'</span>)
    
    <span class="tx-comment"># NaN์„ None์œผ๋กœ ๋ณ€ํ™˜ (JSON null๋กœ ์ง๋ ฌํ™”๋จ)</span>
    df = df.<span class="tx-func">where</span>(pd.<span class="tx-func">notnull</span>(df), <span class="tx-keyword">None</span>)
    
    <span class="tx-comment"># ์ปฌ๋Ÿผ๋ช… ๊ณต๋ฐฑ ์ œ๊ฑฐ</span>
    df.columns = df.columns.<span class="tx-func">str.strip</span>()
    
    json_str = df.<span class="tx-func">to_json</span>(orient=orient, force_ascii=<span class="tx-keyword">False</span>, indent=<span class="tx-num">2</span>)
    
    <span class="tx-keyword">if</span> json_path:
        <span class="tx-keyword">with</span> <span class="tx-func">open</span>(json_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> f:
            f.<span class="tx-func">write</span>(json_str)
    
    <span class="tx-keyword">return</span> json.<span class="tx-func">loads</span>(json_str)


<span class="tx-comment"># ์‚ฌ์šฉ ์˜ˆ์‹œ</span>
data = <span class="tx-func">csv_to_json_pandas</span>(<span class="tx-string">'users.csv'</span>, <span class="tx-string">'users.json'</span>)
<span class="tx-func">print</span>(<span class="tx-string">f"์ด {len(data)}๊ฐœ ๋ ˆ์ฝ”๋“œ ๋ณ€ํ™˜ ์™„๋ฃŒ ๐ŸŽ‰"</span>)
โœ… pandas์˜ ๊ฐ•์ : ์ปฌ๋Ÿผ ํƒ€์ž… ์ž๋™ ์ถ”๋ก , ๋‚ ์งœ ํŒŒ์‹ฑ, ๊ฒฐ์ธก๊ฐ’ ์ฒ˜๋ฆฌ ๋“ฑ์ด ์ž๋™์œผ๋กœ ๋ผ. ๋ฐ์ดํ„ฐ ๋ถ„์„ ํ™˜๊ฒฝ์ด๋ผ๋ฉด pandas๊ฐ€ ์••๋„์ ์œผ๋กœ ํŽธํ•ด.

๐ŸŽฏ ๋ฐฉ๋ฒ• 5: ์ปค๋งจ๋“œ๋ผ์ธ ์›๋ผ์ด๋„ˆ (๋น ๋ฅธ ๋ณ€ํ™˜)

<span class="tx-comment"># ํ„ฐ๋ฏธ๋„์—์„œ ๋ฐ”๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์›๋ผ์ด๋„ˆ</span>
python3 -c <span class="tx-string">"
import csv, json, sys
with open(sys.argv[1], 'r', encoding='utf-8-sig') as f:
    print(json.dumps(list(csv.DictReader(f)), ensure_ascii=False, indent=2))
"</span> input.csv > output.json

๐ŸŸจ 06. JavaScript / Node.js๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž๋ผ๋ฉด JavaScript๋กœ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ถ์„ ๊ฑฐ์•ผ.
๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ Node.js ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ฐฉ๋ฒ• ๋‘˜ ๋‹ค ์•Œ์•„๋ณด์ž!

๐ŸŒ ๋ฐฉ๋ฒ• 1: ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ง์ ‘ ์ฒ˜๋ฆฌ (์ˆœ์ˆ˜ JS)

<span class="tx-comment">/**
 * ๋ธŒ๋ผ์šฐ์ €์—์„œ CSV ๋ฌธ์ž์—ด์„ JSON์œผ๋กœ ๋ณ€ํ™˜
 * ํŒŒ์ผ ์—…๋กœ๋“œ ํ›„ FileReader๋กœ ์ฝ์€ ํ…์ŠคํŠธ๋ฅผ ๋„˜๊ฒจ์ฃผ๋ฉด ๋จ
 */</span>
<span class="tx-keyword">function</span> <span class="tx-func">csvToJson</span>(csvText, delimiter = <span class="tx-string">','</span>) {
  <span class="tx-keyword">const</span> lines = csvText
    .<span class="tx-func">trim</span>()
    .<span class="tx-func">split</span>(<span class="tx-string">'\n'</span>)
    .<span class="tx-func">map</span>(line => line.<span class="tx-func">replace</span>(<span class="tx-string">/\r$/</span>, <span class="tx-string">''</span>)); <span class="tx-comment">// CRLF ์ฒ˜๋ฆฌ</span>
  
  <span class="tx-keyword">if</span> (lines.length === <span class="tx-num">0</span>) <span class="tx-keyword">return</span> [];
  
  <span class="tx-comment">// ํ—ค๋” ํŒŒ์‹ฑ</span>
  <span class="tx-keyword">const</span> headers = <span class="tx-func">parseCSVLine</span>(lines[<span class="tx-num">0</span>], delimiter);
  
  <span class="tx-comment">// ๋ฐ์ดํ„ฐ ํ–‰ ํŒŒ์‹ฑ</span>
  <span class="tx-keyword">return</span> lines
    .<span class="tx-func">slice</span>(<span class="tx-num">1</span>)
    .<span class="tx-func">filter</span>(line => line.<span class="tx-func">trim</span>() !== <span class="tx-string">''</span>) <span class="tx-comment">// ๋นˆ ํ–‰ ์ œ๊ฑฐ</span>
    .<span class="tx-func">map</span>(line => {
      <span class="tx-keyword">const</span> values = <span class="tx-func">parseCSVLine</span>(line, delimiter);
      <span class="tx-keyword">return</span> headers.<span class="tx-func">reduce</span>((obj, header, index) => {
        obj[header.<span class="tx-func">trim</span>()] = <span class="tx-func">autoType</span>(values[index] ?? <span class="tx-string">''</span>);
        <span class="tx-keyword">return</span> obj;
      }, {});
    });
}

<span class="tx-comment">/**
 * CSV ํ•œ ์ค„์„ ํŒŒ์‹ฑ (๋”ฐ์˜ดํ‘œ ์ฒ˜๋ฆฌ ํฌํ•จ)
 */</span>
<span class="tx-keyword">function</span> <span class="tx-func">parseCSVLine</span>(line, delimiter = <span class="tx-string">','</span>) {
  <span class="tx-keyword">const</span> result = [];
  <span class="tx-keyword">let</span> current = <span class="tx-string">''</span>;
  <span class="tx-keyword">let</span> inQuotes = <span class="tx-keyword">false</span>;
  
  <span class="tx-keyword">for</span> (<span class="tx-keyword">let</span> i = <span class="tx-num">0</span>; i < line.length; i++) {
    <span class="tx-keyword">const</span> char = line[i];
    <span class="tx-keyword">const</span> nextChar = line[i + <span class="tx-num">1</span>];
    
    <span class="tx-keyword">if</span> (char === <span class="tx-string">'"'</span>) {
      <span class="tx-keyword">if</span> (inQuotes && nextChar === <span class="tx-string">'"'</span>) {
        <span class="tx-comment">// ์ด์Šค์ผ€์ดํ”„๋œ ๋”ฐ์˜ดํ‘œ ("")</span>
        current += <span class="tx-string">'"'</span>;
        i++;
      } <span class="tx-keyword">else</span> {
        inQuotes = !inQuotes;
      }
    } <span class="tx-keyword">else if</span> (char === delimiter && !inQuotes) {
      result.<span class="tx-func">push</span>(current);
      current = <span class="tx-string">''</span>;
    } <span class="tx-keyword">else</span> {
      current += char;
    }
  }
  result.<span class="tx-func">push</span>(current);
  <span class="tx-keyword">return</span> result;
}

<span class="tx-comment">/**
 * ๋ฌธ์ž์—ด ๊ฐ’์„ ์ ์ ˆํ•œ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜
 */</span>
<span class="tx-keyword">function</span> <span class="tx-func">autoType</span>(value) {
  <span class="tx-keyword">if</span> (value === <span class="tx-string">''</span>) <span class="tx-keyword">return</span> <span class="tx-keyword">null</span>;
  <span class="tx-keyword">if</span> (value.toLowerCase() === <span class="tx-string">'true'</span>) <span class="tx-keyword">return</span> <span class="tx-keyword">true</span>;
  <span class="tx-keyword">if</span> (value.toLowerCase() === <span class="tx-string">'false'</span>) <span class="tx-keyword">return</span> <span class="tx-keyword">false</span>;
  <span class="tx-keyword">if</span> (!<span class="tx-func">isNaN</span>(value) && value !== <span class="tx-string">''</span>) <span class="tx-keyword">return</span> <span class="tx-func">Number</span>(value);
  <span class="tx-keyword">return</span> value;
}

๐Ÿ“ ํŒŒ์ผ ์—…๋กœ๋“œ ์—ฐ๋™ ์˜ˆ์‹œ

<span class="tx-comment">// HTML: <input type="file" id="csvFile" accept=".csv"></span>

document.<span class="tx-func">getElementById</span>(<span class="tx-string">'csvFile'</span>)
  .<span class="tx-func">addEventListener</span>(<span class="tx-string">'change'</span>, <span class="tx-keyword">function</span>(event) {
    <span class="tx-keyword">const</span> file = event.target.files[<span class="tx-num">0</span>];
    <span class="tx-keyword">if</span> (!file) <span class="tx-keyword">return</span>;
    
    <span class="tx-keyword">const</span> reader = <span class="tx-keyword">new</span> <span class="tx-func">FileReader</span>();
    
    reader.onload = <span class="tx-keyword">function</span>(e) {
      <span class="tx-keyword">const</span> csvText = e.target.result;
      <span class="tx-keyword">const</span> jsonData = <span class="tx-func">csvToJson</span>(csvText);
      
      console.<span class="tx-func">log</span>(<span class="tx-string">'๋ณ€ํ™˜ ๊ฒฐ๊ณผ:'</span>, jsonData);
      console.<span class="tx-func">log</span>(<span class="tx-string">'JSON ๋ฌธ์ž์—ด:'</span>, JSON.<span class="tx-func">stringify</span>(jsonData, <span class="tx-keyword">null</span>, <span class="tx-num">2</span>));
      
      <span class="tx-comment">// ๋‹ค์šด๋กœ๋“œ ๊ธฐ๋Šฅ</span>
      <span class="tx-keyword">const</span> blob = <span class="tx-keyword">new</span> <span class="tx-func">Blob</span>(
        [JSON.<span class="tx-func">stringify</span>(jsonData, <span class="tx-keyword">null</span>, <span class="tx-num">2</span>)],
        { type: <span class="tx-string">'application/json'</span> }
      );
      <span class="tx-keyword">const</span> url = URL.<span class="tx-func">createObjectURL</span>(blob);
      <span class="tx-keyword">const</span> a = document.<span class="tx-func">createElement</span>(<span class="tx-string">'a'</span>);
      a.href = url;
      a.download = <span class="tx-string">'output.json'</span>;
      a.<span class="tx-func">click</span>();
    };
    
    reader.<span class="tx-func">readAsText</span>(file, <span class="tx-string">'UTF-8'</span>);
  });

๐ŸŸข Node.js ์„œ๋ฒ„์‚ฌ์ด๋“œ ๋ณ€ํ™˜

<span class="tx-comment">// npm install csv-parse  โ† ๊ฒ€์ฆ๋œ CSV ํŒŒ์‹ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ</span>
<span class="tx-keyword">const</span> { parse } = <span class="tx-func">require</span>(<span class="tx-string">'csv-parse/sync'</span>);
<span class="tx-keyword">const</span> fs = <span class="tx-func">require</span>(<span class="tx-string">'fs'</span>);
<span class="tx-keyword">const</span> path = <span class="tx-func">require</span>(<span class="tx-string">'path'</span>);

<span class="tx-keyword">function</span> <span class="tx-func">csvToJsonFile</span>(inputPath, outputPath) {
  <span class="tx-comment">// BOM ์ œ๊ฑฐ ํ›„ ์ฝ๊ธฐ</span>
  <span class="tx-keyword">let</span> content = fs.<span class="tx-func">readFileSync</span>(inputPath, <span class="tx-string">'utf-8'</span>);
  <span class="tx-keyword">if</span> (content.<span class="tx-func">charCodeAt</span>(<span class="tx-num">0</span>) === <span class="tx-num">0xFEFF</span>) {
    content = content.<span class="tx-func">slice</span>(<span class="tx-num">1</span>); <span class="tx-comment">// BOM ์ œ๊ฑฐ</span>
  }
  
  <span class="tx-keyword">const</span> records = <span class="tx-func">parse</span>(content, {
    columns: <span class="tx-keyword">true</span>,        <span class="tx-comment">// ์ฒซ ํ–‰์„ ํ—ค๋”๋กœ</span>
    skip_empty_lines: <span class="tx-keyword">true</span>, <span class="tx-comment">// ๋นˆ ์ค„ ์Šคํ‚ต</span>
    trim: <span class="tx-keyword">true</span>,           <span class="tx-comment">// ์•ž๋’ค ๊ณต๋ฐฑ ์ œ๊ฑฐ</span>
    cast: <span class="tx-keyword">true</span>,           <span class="tx-comment">// ํƒ€์ž… ์ž๋™ ๋ณ€ํ™˜</span>
    cast_date: <span class="tx-keyword">false</span>      <span class="tx-comment">// ๋‚ ์งœ ์ž๋™ ๋ณ€ํ™˜์€ ๋” (์˜๋„์น˜ ์•Š์€ ๋ณ€ํ™˜ ๋ฐฉ์ง€)</span>
  });
  
  <span class="tx-keyword">const</span> jsonStr = JSON.<span class="tx-func">stringify</span>(records, <span class="tx-keyword">null</span>, <span class="tx-num">2</span>);
  
  <span class="tx-keyword">if</span> (outputPath) {
    fs.<span class="tx-func">writeFileSync</span>(outputPath, jsonStr, <span class="tx-string">'utf-8'</span>);
    console.<span class="tx-func">log</span>(<span class="tx-string">`โœ… ${records.length}๊ฐœ ๋ ˆ์ฝ”๋“œ โ†’ ${outputPath}`</span>);
  }
  
  <span class="tx-keyword">return</span> records;
}

<span class="tx-comment">// ์‹คํ–‰</span>
<span class="tx-keyword">const</span> data = <span class="tx-func">csvToJsonFile</span>(<span class="tx-string">'./data/input.csv'</span>, <span class="tx-string">'./data/output.json'</span>);
๐Ÿ’ก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”์ฒœ: Node.js์—์„œ๋Š” csv-parse๊ฐ€ ๊ฐ€์žฅ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ. RFC 4180 ์™„๋ฒฝ ์ค€์ˆ˜, ์ŠคํŠธ๋ฆฌ๋ฐ ์ง€์›, ํƒ€์ž… ์บ์ŠคํŒ… ๋“ฑ ํ•„์š”ํ•œ ๊ธฐ๋Šฅ์ด ๋‹ค ์žˆ์–ด.

โ˜• 07. Java๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ (Jackson ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)

Java ํ™˜๊ฒฝ์—์„œ๋Š” Jackson๊ณผ OpenCSV ์กฐํ•ฉ์ด ๊ฐ€์žฅ ๊ฐ•๋ ฅํ•ด.
Spring Boot ํ”„๋กœ์ ํŠธ๋ผ๋ฉด Jackson์€ ์ด๋ฏธ ๋“ค์–ด๊ฐ€ ์žˆ์„ ๊ฑฐ์•ผ!

๐Ÿ“ฆ ์˜์กด์„ฑ ์ถ”๊ฐ€ (Maven)

<span class="tx-comment"><!-- pom.xml --></span>
<span class="tx-keyword"><dependency></span>
    <span class="tx-string"><groupId></span>com.opencsv<span class="tx-string"></groupId></span>
    <span class="tx-string"><artifactId></span>opencsv<span class="tx-string"></artifactId></span>
    <span class="tx-string"><version></span>5.7.1<span class="tx-string"></version></span>
<span class="tx-keyword"></dependency></span>
<span class="tx-keyword"><dependency></span>
    <span class="tx-string"><groupId></span>com.fasterxml.jackson.core<span class="tx-string"></groupId></span>
    <span class="tx-string"><artifactId></span>jackson-databind<span class="tx-string"></artifactId></span>
    <span class="tx-string"><version></span>2.15.2<span class="tx-string"></version></span>
<span class="tx-keyword"></dependency></span>

โ˜• ๋ณ€ํ™˜ ์ฝ”๋“œ

<span class="tx-keyword">import</span> com.fasterxml.jackson.databind.ObjectMapper;
<span class="tx-keyword">import</span> com.opencsv.CSVReader;
<span class="tx-keyword">import</span> com.opencsv.exceptions.CsvException;
<span class="tx-keyword">import</span> java.io.*;
<span class="tx-keyword">import</span> java.nio.charset.StandardCharsets;
<span class="tx-keyword">import</span> java.util.*;

<span class="tx-keyword">public class</span> <span class="tx-func">CsvToJsonConverter</span> {
    
    <span class="tx-keyword">private final</span> ObjectMapper objectMapper = <span class="tx-keyword">new</span> <span class="tx-func">ObjectMapper</span>();
    
    <span class="tx-comment">/**
     * CSV ํŒŒ์ผ์„ JSON ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜
     * @param csvFilePath CSV ํŒŒ์ผ ๊ฒฝ๋กœ
     * @return JSON ๋ฌธ์ž์—ด
     */</span>
    <span class="tx-keyword">public</span> String <span class="tx-func">convertToJson</span>(String csvFilePath) 
            <span class="tx-keyword">throws</span> IOException, CsvException {
        
        List<Map<String, String>> result = <span class="tx-keyword">new</span> ArrayList<>();
        
        <span class="tx-keyword">try</span> (
            InputStreamReader isr = <span class="tx-keyword">new</span> <span class="tx-func">InputStreamReader</span>(
                <span class="tx-keyword">new</span> <span class="tx-func">FileInputStream</span>(csvFilePath),
                StandardCharsets.UTF_8
            );
            CSVReader reader = <span class="tx-keyword">new</span> <span class="tx-func">CSVReader</span>(isr)
        ) {
            <span class="tx-comment">// ํ—ค๋” ์ฝ๊ธฐ</span>
            String[] headers = reader.<span class="tx-func">readNext</span>();
            <span class="tx-keyword">if</span> (headers == <span class="tx-keyword">null</span>) <span class="tx-keyword">return</span> <span class="tx-string">"[]"</span>;
            
            <span class="tx-comment">// BOM ์ œ๊ฑฐ</span>
            <span class="tx-keyword">if</span> (headers[<span class="tx-num">0</span>].<span class="tx-func">startsWith</span>(<span class="tx-string">"\uFEFF"</span>)) {
                headers[<span class="tx-num">0</span>] = headers[<span class="tx-num">0</span>].<span class="tx-func">substring</span>(<span class="tx-num">1</span>);
            }
            
            <span class="tx-comment">// ํ—ค๋” ๊ณต๋ฐฑ ์ œ๊ฑฐ</span>
            <span class="tx-keyword">for</span> (<span class="tx-keyword">int</span> i = <span class="tx-num">0</span>; i < headers.length; i++) {
                headers[i] = headers[i].<span class="tx-func">trim</span>();
            }
            
            <span class="tx-comment">// ๋ฐ์ดํ„ฐ ํ–‰ ์ฒ˜๋ฆฌ</span>
            String[] row;
            <span class="tx-keyword">while</span> ((row = reader.<span class="tx-func">readNext</span>()) != <span class="tx-keyword">null</span>) {
                Map<String, String> rowMap = <span class="tx-keyword">new</span> LinkedHashMap<>();
                <span class="tx-keyword">for</span> (<span class="tx-keyword">int</span> i = <span class="tx-num">0</span>; i < headers.length; i++) {
                    String value = (i < row.length) ? row[i].<span class="tx-func">trim</span>() : <span class="tx-string">""</span>;
                    rowMap.<span class="tx-func">put</span>(headers[i], value.isEmpty() ? <span class="tx-keyword">null</span> : value);
                }
                result.<span class="tx-func">add</span>(rowMap);
            }
        }
        
        <span class="tx-keyword">return</span> objectMapper
            .<span class="tx-func">writerWithDefaultPrettyPrinter</span>()
            .<span class="tx-func">writeValueAsString</span>(result);
    }
    
    <span class="tx-comment">// ํŒŒ์ผ๋กœ ์ €์žฅํ•˜๋Š” ๋ฒ„์ „</span>
    <span class="tx-keyword">public void</span> <span class="tx-func">convertToJsonFile</span>(String csvPath, String jsonPath)
            <span class="tx-keyword">throws</span> IOException, CsvException {
        String json = <span class="tx-func">convertToJson</span>(csvPath);
        <span class="tx-keyword">try</span> (FileWriter fw = <span class="tx-keyword">new</span> <span class="tx-func">FileWriter</span>(jsonPath, StandardCharsets.UTF_8)) {
            fw.<span class="tx-func">write</span>(json);
        }
        System.out.<span class="tx-func">println</span>(<span class="tx-string">"โœ… ๋ณ€ํ™˜ ์™„๋ฃŒ: "</span> + jsonPath);
    }
    
    <span class="tx-keyword">public static void</span> <span class="tx-func">main</span>(String[] args) <span class="tx-keyword">throws</span> Exception {
        CsvToJsonConverter converter = <span class="tx-keyword">new</span> <span class="tx-func">CsvToJsonConverter</span>();
        converter.<span class="tx-func">convertToJsonFile</span>(<span class="tx-string">"input.csv"</span>, <span class="tx-string">"output.json"</span>);
    }
}
โœ… Java ํŒ: LinkedHashMap์„ ์“ฐ๋ฉด CSV ์ปฌ๋Ÿผ ์ˆœ์„œ๊ฐ€ JSON์—์„œ๋„ ์œ ์ง€๋ผ. ์ผ๋ฐ˜ HashMap์€ ์ˆœ์„œ๊ฐ€ ๋ณด์žฅ ์•ˆ ๋˜๋‹ˆ๊นŒ ์ฃผ์˜!

๐Ÿน 08. Go ์–ธ์–ด๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

Go๋Š” ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ •๋ง ์ž˜ ๋˜์–ด ์žˆ์–ด์„œ ์™ธ๋ถ€ ํŒจํ‚ค์ง€ ์—†์ด๋„ ๊น”๋”ํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด.
์„ฑ๋Šฅ๋„ ์ตœ๊ณ  ์ˆ˜์ค€์ด๋ผ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ์—๋„ ๊ฐ•ํ•ด! ๐Ÿ’ช

<span class="tx-keyword">package</span> main

<span class="tx-keyword">import</span> (
    <span class="tx-string">"encoding/csv"</span>
    <span class="tx-string">"encoding/json"</span>
    <span class="tx-string">"fmt"</span>
    <span class="tx-string">"os"</span>
    <span class="tx-string">"strings"</span>
    <span class="tx-string">"strconv"</span>
)

<span class="tx-comment">// autoConvert: ๋ฌธ์ž์—ด์„ ์ ์ ˆํ•œ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜</span>
<span class="tx-keyword">func</span> <span class="tx-func">autoConvert</span>(s string) <span class="tx-keyword">interface</span>{} {
    s = strings.<span class="tx-func">TrimSpace</span>(s)
    
    <span class="tx-keyword">if</span> s == <span class="tx-string">""</span> {
        <span class="tx-keyword">return</span> <span class="tx-keyword">nil</span>
    }
    
    <span class="tx-comment">// ๋ถˆ๋ฆฌ์–ธ ์ฒดํฌ</span>
    lower := strings.<span class="tx-func">ToLower</span>(s)
    <span class="tx-keyword">if</span> lower == <span class="tx-string">"true"</span> || lower == <span class="tx-string">"yes"</span> {
        <span class="tx-keyword">return</span> <span class="tx-keyword">true</span>
    }
    <span class="tx-keyword">if</span> lower == <span class="tx-string">"false"</span> || lower == <span class="tx-string">"no"</span> {
        <span class="tx-keyword">return</span> <span class="tx-keyword">false</span>
    }
    
    <span class="tx-comment">// ์ •์ˆ˜ ์ฒดํฌ</span>
    <span class="tx-keyword">if</span> i, err := strconv.<span class="tx-func">ParseInt</span>(s, <span class="tx-num">10</span>, <span class="tx-num">64</span>); err == <span class="tx-keyword">nil</span> {
        <span class="tx-keyword">return</span> i
    }
    
    <span class="tx-comment">// ์‹ค์ˆ˜ ์ฒดํฌ</span>
    <span class="tx-keyword">if</span> f, err := strconv.<span class="tx-func">ParseFloat</span>(s, <span class="tx-num">64</span>); err == <span class="tx-keyword">nil</span> {
        <span class="tx-keyword">return</span> f
    }
    
    <span class="tx-keyword">return</span> s
}

<span class="tx-comment">// CsvToJson: CSV ํŒŒ์ผ์„ JSON์œผ๋กœ ๋ณ€ํ™˜</span>
<span class="tx-keyword">func</span> <span class="tx-func">CsvToJson</span>(csvPath, jsonPath string) error {
    <span class="tx-comment">// CSV ํŒŒ์ผ ์—ด๊ธฐ</span>
    csvFile, err := os.<span class="tx-func">Open</span>(csvPath)
    <span class="tx-keyword">if</span> err != <span class="tx-keyword">nil</span> {
        <span class="tx-keyword">return</span> fmt.<span class="tx-func">Errorf</span>(<span class="tx-string">"CSV ํŒŒ์ผ ์—ด๊ธฐ ์‹คํŒจ: %w"</span>, err)
    }
    <span class="tx-keyword">defer</span> csvFile.<span class="tx-func">Close</span>()
    
    reader := csv.<span class="tx-func">NewReader</span>(csvFile)
    reader.TrimLeadingSpace = <span class="tx-keyword">true</span>
    
    <span class="tx-comment">// ํ—ค๋” ์ฝ๊ธฐ</span>
    headers, err := reader.<span class="tx-func">Read</span>()
    <span class="tx-keyword">if</span> err != <span class="tx-keyword">nil</span> {
        <span class="tx-keyword">return</span> fmt.<span class="tx-func">Errorf</span>(<span class="tx-string">"ํ—ค๋” ์ฝ๊ธฐ ์‹คํŒจ: %w"</span>, err)
    }
    
    <span class="tx-comment">// BOM ์ œ๊ฑฐ</span>
    <span class="tx-keyword">if</span> len(headers) > <span class="tx-num">0</span> {
        headers[<span class="tx-num">0</span>] = strings.<span class="tx-func">TrimPrefix</span>(headers[<span class="tx-num">0</span>], <span class="tx-string">"\uFEFF"</span>)
    }
    
    <span class="tx-keyword">var</span> result []map[string]<span class="tx-keyword">interface</span>{}
    
    <span class="tx-comment">// ๋ฐ์ดํ„ฐ ํ–‰ ์ฒ˜๋ฆฌ</span>
    <span class="tx-keyword">for</span> {
        row, err := reader.<span class="tx-func">Read</span>()
        <span class="tx-keyword">if</span> err != <span class="tx-keyword">nil</span> {
            <span class="tx-keyword">break</span> <span class="tx-comment">// EOF ๋˜๋Š” ์—๋Ÿฌ</span>
        }
        
        rowMap := make(map[string]<span class="tx-keyword">interface</span>{})
        <span class="tx-keyword">for</span> i, header := <span class="tx-keyword">range</span> headers {
            <span class="tx-keyword">if</span> i < len(row) {
                rowMap[strings.<span class="tx-func">TrimSpace</span>(header)] = <span class="tx-func">autoConvert</span>(row[i])
            } <span class="tx-keyword">else</span> {
                rowMap[strings.<span class="tx-func">TrimSpace</span>(header)] = <span class="tx-keyword">nil</span>
            }
        }
        result = <span class="tx-func">append</span>(result, rowMap)
    }
    
    <span class="tx-comment">// JSON ํŒŒ์ผ ์ €์žฅ</span>
    jsonFile, err := os.<span class="tx-func">Create</span>(jsonPath)
    <span class="tx-keyword">if</span> err != <span class="tx-keyword">nil</span> {
        <span class="tx-keyword">return</span> fmt.<span class="tx-func">Errorf</span>(<span class="tx-string">"JSON ํŒŒ์ผ ์ƒ์„ฑ ์‹คํŒจ: %w"</span>, err)
    }
    <span class="tx-keyword">defer</span> jsonFile.<span class="tx-func">Close</span>()
    
    encoder := json.<span class="tx-func">NewEncoder</span>(jsonFile)
    encoder.<span class="tx-func">SetIndent</span>(<span class="tx-string">""</span>, <span class="tx-string">"  "</span>)
    encoder.<span class="tx-func">SetEscapeHTML</span>(<span class="tx-keyword">false</span>) <span class="tx-comment">// ํ•œ๊ธ€ ์ด์Šค์ผ€์ดํ”„ ๋ฐฉ์ง€</span>
    
    <span class="tx-keyword">if</span> err := encoder.<span class="tx-func">Encode</span>(result); err != <span class="tx-keyword">nil</span> {
        <span class="tx-keyword">return</span> fmt.<span class="tx-func">Errorf</span>(<span class="tx-string">"JSON ์ธ์ฝ”๋”ฉ ์‹คํŒจ: %w"</span>, err)
    }
    
    fmt.<span class="tx-func">Printf</span>(<span class="tx-string">"โœ… ๋ณ€ํ™˜ ์™„๋ฃŒ: %d๊ฐœ ๋ ˆ์ฝ”๋“œ\n"</span>, len(result))
    <span class="tx-keyword">return</span> <span class="tx-keyword">nil</span>
}

<span class="tx-keyword">func</span> <span class="tx-func">main</span>() {
    <span class="tx-keyword">if</span> err := <span class="tx-func">CsvToJson</span>(<span class="tx-string">"input.csv"</span>, <span class="tx-string">"output.json"</span>); err != <span class="tx-keyword">nil</span> {
        fmt.<span class="tx-func">Fprintf</span>(os.Stderr, <span class="tx-string">"์—๋Ÿฌ: %v\n"</span>, err)
        os.<span class="tx-func">Exit</span>(<span class="tx-num">1</span>)
    }
}
๐Ÿ’ก Go ํŒ: encoder.SetEscapeHTML(false)๋ฅผ ๊ผญ ๋„ฃ์–ด์ค˜! ์ด๊ฒŒ ์—†์œผ๋ฉด <, >, & ๊ฐ™์€ ๋ฌธ์ž๊ฐ€ ์œ ๋‹ˆ์ฝ”๋“œ ์ด์Šค์ผ€์ดํ”„๋กœ ๋ณ€ํ™˜๋ผ. ํ•œ๊ธ€๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด์Šค์ผ€์ดํ”„๋  ์ˆ˜ ์žˆ์–ด.

๐Ÿ˜ 09. PHP๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ

PHP๋Š” ์›น ์„œ๋ฒ„ ์‚ฌ์ด๋“œ์—์„œ CSV ์—…๋กœ๋“œ ์ฒ˜๋ฆฌํ•  ๋•Œ ๋งŽ์ด ์“ฐ์—ฌ.
๋‚ด์žฅ ํ•จ์ˆ˜๋งŒ์œผ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด!

<span class="tx-keyword"><?php</span>

<span class="tx-comment">/**
 * CSV ํŒŒ์ผ์„ JSON์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
 * 
 * @param string $csvPath CSV ํŒŒ์ผ ๊ฒฝ๋กœ
 * @param string|null $jsonPath ์ €์žฅํ•  JSON ํŒŒ์ผ ๊ฒฝ๋กœ (null์ด๋ฉด ๋ฐ˜ํ™˜๋งŒ)
 * @param string $encoding ํŒŒ์ผ ์ธ์ฝ”๋”ฉ (๊ธฐ๋ณธ: UTF-8)
 * @param string $delimiter ๊ตฌ๋ถ„์ž (๊ธฐ๋ณธ: ์‰ผํ‘œ)
 * @return array ๋ณ€ํ™˜๋œ ๋ฐ์ดํ„ฐ ๋ฐฐ์—ด
 */</span>
<span class="tx-keyword">function</span> <span class="tx-func">csvToJson</span>(
    string $csvPath,
    ?string $jsonPath = <span class="tx-keyword">null</span>,
    string $encoding = <span class="tx-string">'UTF-8'</span>,
    string $delimiter = <span class="tx-string">','</span>
): array {
    
    <span class="tx-keyword">if</span> (!<span class="tx-func">file_exists</span>($csvPath)) {
        <span class="tx-keyword">throw new</span> <span class="tx-func">RuntimeException</span>(<span class="tx-string">"ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: $csvPath"</span>);
    }
    
    <span class="tx-comment">// ํŒŒ์ผ ๋‚ด์šฉ ์ฝ๊ธฐ</span>
    $content = <span class="tx-func">file_get_contents</span>($csvPath);
    
    <span class="tx-comment">// BOM ์ œ๊ฑฐ</span>
    $content = <span class="tx-func">ltrim</span>($content, <span class="tx-string">"\xEF\xBB\xBF"</span>);
    
    <span class="tx-comment">// ์ธ์ฝ”๋”ฉ ๋ณ€ํ™˜ (EUC-KR โ†’ UTF-8)</span>
    <span class="tx-keyword">if</span> (<span class="tx-func">strtoupper</span>($encoding) !== <span class="tx-string">'UTF-8'</span>) {
        $content = <span class="tx-func">mb_convert_encoding</span>($content, <span class="tx-string">'UTF-8'</span>, $encoding);
    }
    
    <span class="tx-comment">// ์ž„์‹œ ํŒŒ์ผ๋กœ ์ฒ˜๋ฆฌ (๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ)</span>
    $tempFile = <span class="tx-func">tmpfile</span>();
    <span class="tx-func">fwrite</span>($tempFile, $content);
    <span class="tx-func">rewind</span>($tempFile);
    
    $result = [];
    $headers = <span class="tx-keyword">null</span>;
    
    <span class="tx-keyword">while</span> (($row = <span class="tx-func">fgetcsv</span>($tempFile, <span class="tx-num">0</span>, $delimiter)) !== <span class="tx-keyword">false</span>) {
        <span class="tx-comment">// ๋นˆ ํ–‰ ์Šคํ‚ต</span>
        <span class="tx-keyword">if</span> (<span class="tx-func">count</span>($row) === <span class="tx-num">1</span> && $row[<span class="tx-num">0</span>] === <span class="tx-keyword">null</span>) <span class="tx-keyword">continue</span>;
        
        <span class="tx-keyword">if</span> ($headers === <span class="tx-keyword">null</span>) {
            <span class="tx-comment">// ํ—ค๋” ํ–‰ ์ฒ˜๋ฆฌ</span>
            $headers = <span class="tx-func">array_map</span>(<span class="tx-string">'trim'</span>, $row);
        } <span class="tx-keyword">else</span> {
            <span class="tx-comment">// ๋ฐ์ดํ„ฐ ํ–‰ ์ฒ˜๋ฆฌ</span>
            $rowData = [];
            <span class="tx-keyword">foreach</span> ($headers <span class="tx-keyword">as</span> $index => $header) {
                $value = isset($row[$index]) ? <span class="tx-func">trim</span>($row[$index]) : <span class="tx-keyword">null</span>;
                $rowData[$header] = <span class="tx-func">autoConvertType</span>($value);
            }
            $result[] = $rowData;
        }
    }
    
    <span class="tx-func">fclose</span>($tempFile);
    
    <span class="tx-comment">// JSON ํŒŒ์ผ ์ €์žฅ</span>
    <span class="tx-keyword">if</span> ($jsonPath !== <span class="tx-keyword">null</span>) {
        $jsonStr = <span class="tx-func">json_encode</span>(
            $result,
            JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
        );
        <span class="tx-func">file_put_contents</span>($jsonPath, $jsonStr);
        <span class="tx-func">echo</span> <span class="tx-string">"โœ… ๋ณ€ํ™˜ ์™„๋ฃŒ: "</span> . <span class="tx-func">count</span>($result) . <span class="tx-string">"๊ฐœ ๋ ˆ์ฝ”๋“œ\n"</span>;
    }
    
    <span class="tx-keyword">return</span> $result;
}

<span class="tx-comment">/**
 * ๋ฌธ์ž์—ด์„ ์ ์ ˆํ•œ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜
 */</span>
<span class="tx-keyword">function</span> <span class="tx-func">autoConvertType</span>(?string $value): mixed {
    <span class="tx-keyword">if</span> ($value === <span class="tx-keyword">null</span> || $value === <span class="tx-string">''</span>) <span class="tx-keyword">return</span> <span class="tx-keyword">null</span>;
    
    $lower = <span class="tx-func">strtolower</span>($value);
    <span class="tx-keyword">if</span> ($lower === <span class="tx-string">'true'</span>)  <span class="tx-keyword">return</span> <span class="tx-keyword">true</span>;
    <span class="tx-keyword">if</span> ($lower === <span class="tx-string">'false'</span>) <span class="tx-keyword">return</span> <span class="tx-keyword">false</span>;
    
    <span class="tx-keyword">if</span> (<span class="tx-func">is_numeric</span>($value)) {
        <span class="tx-keyword">return</span> <span class="tx-func">strpos</span>($value, <span class="tx-string">'.'</span>) !== <span class="tx-keyword">false</span>
            ? (<span class="tx-keyword">float</span>) $value
            : (<span class="tx-keyword">int</span>) $value;
    }
    
    <span class="tx-keyword">return</span> $value;
}

<span class="tx-comment">// ์‚ฌ์šฉ ์˜ˆ์‹œ</span>
$data = <span class="tx-func">csvToJson</span>(<span class="tx-string">'input.csv'</span>, <span class="tx-string">'output.json'</span>);

<span class="tx-comment">// EUC-KR ํŒŒ์ผ ์ฒ˜๋ฆฌ</span>
$data = <span class="tx-func">csvToJson</span>(<span class="tx-string">'korean.csv'</span>, <span class="tx-string">'korean.json'</span>, <span class="tx-string">'EUC-KR'</span>);

<span class="tx-comment">// TSV(ํƒญ ๊ตฌ๋ถ„) ํŒŒ์ผ ์ฒ˜๋ฆฌ</span>
$data = <span class="tx-func">csvToJson</span>(<span class="tx-string">'data.tsv'</span>, <span class="tx-string">'data.json'</span>, <span class="tx-string">'UTF-8'</span>, <span class="tx-string">"\t"</span>);
โœ… PHP ํŒ: JSON_UNESCAPED_UNICODE ํ”Œ๋ž˜๊ทธ๋ฅผ ๊ผญ ๋„ฃ์–ด์ค˜! ์ด๊ฒŒ ์—†์œผ๋ฉด ํ•œ๊ธ€์ด \uD55C\uAE00๋กœ ์ €์žฅ๋ผ. JSON_PRETTY_PRINT๋Š” ๊ฐœ๋ฐœ ์ค‘์—๋งŒ ์“ฐ๊ณ  ํ”„๋กœ๋•์…˜์—์„  ๋นผ๋ฉด ํŒŒ์ผ ํฌ๊ธฐ๊ฐ€ ์ค„์–ด๋“ค์–ด.

๐Ÿ›ก๏ธ 10. ์—ฃ์ง€์ผ€์ด์Šค & ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ์™„๋ฒฝ ๊ฐ€์ด๋“œ

์ด ์„น์…˜์ด ์ง„์งœ ํ•ต์‹ฌ์ด์•ผ. ๊ธฐ๋ณธ ๋ณ€ํ™˜์€ ๋ˆ„๊ตฌ๋‚˜ ํ•˜๋Š”๋ฐ,
์‹ค๋ฌด์—์„œ ํ„ฐ์ง€๋Š” ๊ฑด ํ•ญ์ƒ ์—ฃ์ง€์ผ€์ด์Šค๊ฑฐ๋“ . ๋ฏธ๋ฆฌ ๋Œ€๋น„ํ•˜์ž! ๐Ÿ”ง

โš ๏ธ CSV ๋ณ€ํ™˜ ์—ฃ์ง€์ผ€์ด์Šค ๋งต ๐Ÿ”ค ์ธ์ฝ”๋”ฉ ๋ฌธ์ œ UTF-8 vs EUC-KR BOM ํฌํ•จ ์—ฌ๋ถ€ โ†’ chardet ์ž๋™๊ฐ์ง€ ์‚ฌ์šฉ ๐Ÿ“ ํŠน์ˆ˜๋ฌธ์ž ์ฒ˜๋ฆฌ ๊ฐ’ ์•ˆ์˜ ์‰ผํ‘œ, ์ค„๋ฐ”๊ฟˆ ํฐ๋”ฐ์˜ดํ‘œ ์ด์Šค์ผ€์ดํ”„ โ†’ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ๐Ÿ”ข ํƒ€์ž… ๋ณ€ํ™˜ ์ˆซ์ž vs ๋ฌธ์ž์—ด ๋‚ ์งœ ํ˜•์‹ ์ฒ˜๋ฆฌ โ†’ ๋ช…์‹œ์  ํƒ€์ž… ์ง€์ • ๊ถŒ์žฅ ๐Ÿ“ญ ๋นˆ ๊ฐ’ ์ฒ˜๋ฆฌ ๋นˆ ๋ฌธ์ž์—ด vs null ๋ˆ„๋ฝ๋œ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ โ†’ ๋ช…์‹œ์  null ์ฒ˜๋ฆฌ ๐Ÿ“ ํ—ค๋” ๋ฌธ์ œ ์ค‘๋ณต ํ—ค๋” ์ด๋ฆ„ ํ—ค๋” ์•ž๋’ค ๊ณต๋ฐฑ โ†’ strip() + ์ค‘๋ณต ์ฒ˜๋ฆฌ ๐Ÿ”€ ์ค„๋ฐ”๊ฟˆ ํ˜•์‹ LF vs CRLF vs CR ์œˆ๋„์šฐ/๋งฅ/๋ฆฌ๋ˆ…์Šค ์ฐจ์ด โ†’ universal newline ์‚ฌ์šฉ ๐Ÿ’ก ํ™ฉ๊ธˆ ๋ฒ•์น™ ์ง์ ‘ ํŒŒ์‹ฑํ•˜์ง€ ๋ง๊ณ  ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์ž! Python: csv ๋ชจ๋“ˆ / JS: csv-parse / Java: OpenCSV / Go: encoding/csv

๐Ÿ”ค ์—ฃ์ง€์ผ€์ด์Šค 1: ์ธ์ฝ”๋”ฉ ์™„๋ฒฝ ์ฒ˜๋ฆฌ (Python)

<span class="tx-keyword">import</span> chardet

<span class="tx-keyword">def</span> <span class="tx-func">safe_read_csv</span>(file_path):
    <span class="tx-string">"""์ธ์ฝ”๋”ฉ ์ž๋™ ๊ฐ์ง€ + ์•ˆ์ „ํ•œ CSV ์ฝ๊ธฐ"""</span>
    
    <span class="tx-comment"># 1๋‹จ๊ณ„: ๋ฐ”์ด๋„ˆ๋ฆฌ๋กœ ์ฝ์–ด์„œ ์ธ์ฝ”๋”ฉ ๊ฐ์ง€</span>
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(file_path, <span class="tx-string">'rb'</span>) <span class="tx-keyword">as</span> f:
        raw_data = f.<span class="tx-func">read</span>()
    
    detected = chardet.<span class="tx-func">detect</span>(raw_data)
    encoding = detected[<span class="tx-string">'encoding'</span>]
    confidence = detected[<span class="tx-string">'confidence'</span>]
    
    <span class="tx-func">print</span>(<span class="tx-string">f"๊ฐ์ง€๋œ ์ธ์ฝ”๋”ฉ: {encoding} (์‹ ๋ขฐ๋„: {confidence:.1%})"</span>)
    
    <span class="tx-comment"># 2๋‹จ๊ณ„: ๊ฐ์ง€๋œ ์ธ์ฝ”๋”ฉ์œผ๋กœ ์‹œ๋„, ์‹คํŒจ์‹œ ๋Œ€์•ˆ ์‹œ๋„</span>
    encodings_to_try = [encoding, <span class="tx-string">'utf-8-sig'</span>, <span class="tx-string">'cp949'</span>, <span class="tx-string">'euc-kr'</span>, <span class="tx-string">'latin-1'</span>]
    
    <span class="tx-keyword">for</span> enc <span class="tx-keyword">in</span> encodings_to_try:
        <span class="tx-keyword">if</span> enc <span class="tx-keyword">is</span> <span class="tx-keyword">None</span>:
            <span class="tx-keyword">continue</span>
        <span class="tx-keyword">try</span>:
            text = raw_data.<span class="tx-func">decode</span>(enc)
            <span class="tx-func">print</span>(<span class="tx-string">f"โœ… {enc}์œผ๋กœ ์„ฑ๊ณต์ ์œผ๋กœ ๋””์ฝ”๋”ฉ"</span>)
            <span class="tx-keyword">return</span> text
        <span class="tx-keyword">except</span> (UnicodeDecodeError, LookupError):
            <span class="tx-keyword">continue</span>
    
    <span class="tx-comment"># ์ตœํ›„์˜ ์ˆ˜๋‹จ: errors='replace'๋กœ ๊ฐ•์ œ ๋””์ฝ”๋”ฉ</span>
    <span class="tx-func">print</span>(<span class="tx-string">"โš ๏ธ ์ธ์ฝ”๋”ฉ ๊ฐ์ง€ ์‹คํŒจ, ๋Œ€์ฒด ๋ฌธ์ž๋กœ ์ฒ˜๋ฆฌ"</span>)
    <span class="tx-keyword">return</span> raw_data.<span class="tx-func">decode</span>(<span class="tx-string">'utf-8'</span>, errors=<span class="tx-string">'replace'</span>)

๐Ÿ“… ์—ฃ์ง€์ผ€์ด์Šค 2: ๋‚ ์งœ ํ˜•์‹ ์ฒ˜๋ฆฌ

<span class="tx-keyword">from</span> datetime <span class="tx-keyword">import</span> datetime
<span class="tx-keyword">import</span> re

<span class="tx-keyword">def</span> <span class="tx-func">parse_date</span>(value):
    <span class="tx-string">"""๋‹ค์–‘ํ•œ ๋‚ ์งœ ํ˜•์‹์„ ISO 8601๋กœ ํ†ต์ผ"""</span>
    date_formats = [
        <span class="tx-string">'%Y-%m-%d'</span>,        <span class="tx-comment"># 2024-01-15</span>
        <span class="tx-string">'%Y/%m/%d'</span>,        <span class="tx-comment"># 2024/01/15</span>
        <span class="tx-string">'%d/%m/%Y'</span>,        <span class="tx-comment"># 15/01/2024</span>
        <span class="tx-string">'%m/%d/%Y'</span>,        <span class="tx-comment"># 01/15/2024</span>
        <span class="tx-string">'%Y.%m.%d'</span>,        <span class="tx-comment"># 2024.01.15</span>
        <span class="tx-string">'%Y-%m-%dT%H:%M:%S'</span>, <span class="tx-comment"># ISO 8601</span>
        <span class="tx-string">'%Y๋…„ %m์›” %d์ผ'</span>,  <span class="tx-comment"># ํ•œ๊ตญ์‹</span>
    ]
    
    <span class="tx-keyword">for</span> fmt <span class="tx-keyword">in</span> date_formats:
        <span class="tx-keyword">try</span>:
            dt = datetime.<span class="tx-func">strptime</span>(value.<span class="tx-func">strip</span>(), fmt)
            <span class="tx-keyword">return</span> dt.<span class="tx-func">isoformat</span>()  <span class="tx-comment"># ISO 8601๋กœ ํ†ต์ผ</span>
        <span class="tx-keyword">except</span> ValueError:
            <span class="tx-keyword">continue</span>
    
    <span class="tx-keyword">return</span> value  <span class="tx-comment"># ํŒŒ์‹ฑ ์‹คํŒจ์‹œ ์›๋ณธ ๋ฐ˜ํ™˜</span>


<span class="tx-comment"># ๋‚ ์งœ ์ปฌ๋Ÿผ์„ ์ง€์ •ํ•ด์„œ ๋ณ€ํ™˜ํ•˜๋Š” ์˜ˆ์‹œ</span>
<span class="tx-keyword">def</span> <span class="tx-func">csv_to_json_with_dates</span>(csv_path, date_columns=None):
    <span class="tx-keyword">import</span> csv, json
    date_columns = date_columns <span class="tx-keyword">or</span> []
    result = []
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(csv_path, <span class="tx-string">'r'</span>, encoding=<span class="tx-string">'utf-8-sig'</span>) <span class="tx-keyword">as</span> f:
        reader = csv.DictReader(f)
        <span class="tx-keyword">for</span> row <span class="tx-keyword">in</span> reader:
            processed = {}
            <span class="tx-keyword">for</span> key, value <span class="tx-keyword">in</span> row.items():
                <span class="tx-keyword">if</span> key <span class="tx-keyword">in</span> date_columns:
                    processed[key] = <span class="tx-func">parse_date</span>(value)
                <span class="tx-keyword">else</span>:
                    processed[key] = value
            result.append(processed)
    
    <span class="tx-keyword">return</span> result

<span class="tx-comment"># ์‚ฌ์šฉ: 'birth_date', 'created_at' ์ปฌ๋Ÿผ์„ ๋‚ ์งœ๋กœ ์ฒ˜๋ฆฌ</span>
data = <span class="tx-func">csv_to_json_with_dates</span>(<span class="tx-string">'users.csv'</span>, [<span class="tx-string">'birth_date'</span>, <span class="tx-string">'created_at'</span>])

๐Ÿ” ์—ฃ์ง€์ผ€์ด์Šค 3: ์ค‘๋ณต ํ—ค๋” ์ฒ˜๋ฆฌ

<span class="tx-keyword">def</span> <span class="tx-func">deduplicate_headers</span>(headers):
    <span class="tx-string">"""์ค‘๋ณต ํ—ค๋”๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌ (name โ†’ name_1, name_2 ...)</span>
<span class="tx-string">    """</span>
    seen = {}
    result = []
    
    <span class="tx-keyword">for</span> header <span class="tx-keyword">in</span> headers:
        header = header.<span class="tx-func">strip</span>()
        <span class="tx-keyword">if</span> header <span class="tx-keyword">in</span> seen:
            seen[header] += <span class="tx-num">1</span>
            result.append(<span class="tx-string">f"{header}_{seen[header]}"</span>)
        <span class="tx-keyword">else</span>:
            seen[header] = <span class="tx-num">0</span>
            result.append(header)
    
    <span class="tx-keyword">return</span> result

<span class="tx-comment"># ์˜ˆ์‹œ: ['name', 'age', 'name', 'name']</span>
<span class="tx-comment"># ๊ฒฐ๊ณผ: ['name', 'age', 'name_1', 'name_2']</span>

๐Ÿ‹๏ธ 11. ๋Œ€์šฉ๋Ÿ‰ CSV ์ฒ˜๋ฆฌ ์ „๋žต โ€” ๋ฉ”๋ชจ๋ฆฌ ๊ฑฑ์ • ๋

100๋งŒ ํ–‰์งœ๋ฆฌ CSV๋ฅผ ํ•œ ๋ฒˆ์— ๋ฉ”๋ชจ๋ฆฌ์— ์˜ฌ๋ฆฌ๋ฉด? ๐Ÿ’€ ์„œ๋ฒ„๊ฐ€ ์ฃฝ์–ด.
๋Œ€์šฉ๋Ÿ‰ ํŒŒ์ผ์€ ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•ด!

๐Ÿ“Š ๋ฉ”๋ชจ๋ฆฌ ์ฒ˜๋ฆฌ ๋ฐฉ์‹ ๋น„๊ต โŒ ์ผ๋ฐ˜ ๋ฐฉ์‹ (์ „์ฒด ๋กœ๋“œ) ์ „์ฒด ํŒŒ์ผ โ†’ RAM โ†’ ์ „์ฒด JSON ๐Ÿ’พ 1GB CSV โ†’ 3~5GB RAM ์‚ฌ์šฉ ๐Ÿ’€ โœ… ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹ ์ฒญํฌ1 ์ฒญํฌ2 ์ฒญํฌ3 ์ฒญํฌ4 ... ๐Ÿ’พ 1GB CSV โ†’ ~50MB RAM ์‚ฌ์šฉ โœ… ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ• ๐Ÿ Python csv.reader ์ดํ„ฐ๋ ˆ์ดํ„ฐ ํ–‰ ๋‹จ์œ„ ์ฒ˜๋ฆฌ ๐ŸŸข Node.js csv-parse ์ŠคํŠธ๋ฆผ pipe ์ฒด์ธ ํ™œ์šฉ ๐Ÿน Go csv.Reader.Read() ๊ณ ๋ฃจํ‹ด ๋ณ‘๋ ฌ ์ฒ˜๋ฆฌ ๐Ÿผ pandas chunksize ํŒŒ๋ผ๋ฏธํ„ฐ ์ฒญํฌ ๋‹จ์œ„ ์ฒ˜๋ฆฌ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ์˜ ํ•ต์‹ฌ: ํ•œ ๋ฒˆ์— ๋‹ค ์ฝ์ง€ ๋ง๊ณ , ์กฐ๊ธˆ์”ฉ ์ฒ˜๋ฆฌํ•˜์ž! ๐Ÿš€

๐Ÿ Python ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ (๋Œ€์šฉ๋Ÿ‰)

<span class="tx-keyword">import</span> csv
<span class="tx-keyword">import</span> json

<span class="tx-keyword">def</span> <span class="tx-func">csv_to_json_streaming</span>(csv_path, json_path, chunk_size=<span class="tx-num">1000</span>):
    <span class="tx-string">"""
    ์ŠคํŠธ๋ฆฌ๋ฐ ๋ฐฉ์‹์œผ๋กœ ๋Œ€์šฉ๋Ÿ‰ CSV๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜
    ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ตœ์†Œํ™”ํ•˜๋ฉด์„œ ์ฒ˜๋ฆฌ
    """</span>
    total_rows = <span class="tx-num">0</span>
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(csv_path, <span class="tx-string">'r'</span>, encoding=<span class="tx-string">'utf-8-sig'</span>, newline=<span class="tx-string">''</span>) <span class="tx-keyword">as</span> csv_file, \
         <span class="tx-func">open</span>(json_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> json_file:
        
        reader = csv.DictReader(csv_file)
        
        <span class="tx-comment"># JSON ๋ฐฐ์—ด ์‹œ์ž‘</span>
        json_file.<span class="tx-func">write</span>(<span class="tx-string">'[\n'</span>)
        
        first = <span class="tx-keyword">True</span>
        
        <span class="tx-keyword">for</span> row <span class="tx-keyword">in</span> reader:
            <span class="tx-keyword">if</span> <span class="tx-keyword">not</span> first:
                json_file.<span class="tx-func">write</span>(<span class="tx-string">',\n'</span>)
            
            <span class="tx-comment"># ๊ฐ ํ–‰์„ ์ฆ‰์‹œ ์ง๋ ฌํ™”ํ•˜์—ฌ ํŒŒ์ผ์— ์”€</span>
            json_str = json.<span class="tx-func">dumps</span>(
                <span class="tx-func">dict</span>(row),
                ensure_ascii=<span class="tx-keyword">False</span>
            )
            json_file.<span class="tx-func">write</span>(<span class="tx-string">f'  {json_str}'</span>)
            
            first = <span class="tx-keyword">False</span>
            total_rows += <span class="tx-num">1</span>
            
            <span class="tx-comment"># ์ง„ํ–‰ ์ƒํ™ฉ ์ถœ๋ ฅ (chunk_size๋งˆ๋‹ค)</span>
            <span class="tx-keyword">if</span> total_rows % chunk_size == <span class="tx-num">0</span>:
                <span class="tx-func">print</span>(<span class="tx-string">f"โณ {total_rows:,}ํ–‰ ์ฒ˜๋ฆฌ ์ค‘..."</span>, end=<span class="tx-string">'\r'</span>)
        
        <span class="tx-comment"># JSON ๋ฐฐ์—ด ์ข…๋ฃŒ</span>
        json_file.<span class="tx-func">write</span>(<span class="tx-string">'\n]'</span>)
    
    <span class="tx-func">print</span>(<span class="tx-string">f"\nโœ… ์™„๋ฃŒ! ์ด {total_rows:,}ํ–‰ ์ฒ˜๋ฆฌ๋จ"</span>)
    <span class="tx-keyword">return</span> total_rows


<span class="tx-comment"># pandas ์ฒญํฌ ์ฒ˜๋ฆฌ ๋ฐฉ์‹</span>
<span class="tx-keyword">def</span> <span class="tx-func">csv_to_json_pandas_chunks</span>(csv_path, json_path, chunksize=<span class="tx-num">10000</span>):
    <span class="tx-string">"""pandas chunksize๋ฅผ ์ด์šฉํ•œ ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ"""</span>
    <span class="tx-keyword">import</span> pandas <span class="tx-keyword">as</span> pd
    
    all_data = []
    
    <span class="tx-keyword">for</span> i, chunk <span class="tx-keyword">in</span> <span class="tx-func">enumerate</span>(
        pd.<span class="tx-func">read_csv</span>(csv_path, encoding=<span class="tx-string">'utf-8-sig'</span>, chunksize=chunksize)
    ):
        <span class="tx-comment"># NaN โ†’ None ๋ณ€ํ™˜</span>
        chunk = chunk.<span class="tx-func">where</span>(pd.<span class="tx-func">notnull</span>(chunk), <span class="tx-keyword">None</span>)
        all_data.extend(chunk.<span class="tx-func">to_dict</span>(orient=<span class="tx-string">'records'</span>))
        <span class="tx-func">print</span>(<span class="tx-string">f"โณ ์ฒญํฌ {i+1} ์ฒ˜๋ฆฌ ์™„๋ฃŒ ({len(all_data):,}ํ–‰)"</span>, end=<span class="tx-string">'\r'</span>)
    
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(json_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> f:
        json.<span class="tx-func">dump</span>(all_data, f, ensure_ascii=<span class="tx-keyword">False</span>)
    
    <span class="tx-func">print</span>(<span class="tx-string">f"\nโœ… ์™„๋ฃŒ! ์ด {len(all_data):,}ํ–‰"</span>)
    <span class="tx-keyword">return</span> all_data

๐ŸŸข Node.js ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ

<span class="tx-keyword">const</span> fs = <span class="tx-func">require</span>(<span class="tx-string">'fs'</span>);
<span class="tx-keyword">const</span> { parse } = <span class="tx-func">require</span>(<span class="tx-string">'csv-parse'</span>);
<span class="tx-keyword">const</span> { stringify } = <span class="tx-func">require</span>(<span class="tx-string">'JSONStream'</span>); <span class="tx-comment">// npm install JSONStream</span>

<span class="tx-keyword">function</span> <span class="tx-func">csvToJsonStream</span>(inputPath, outputPath) {
  <span class="tx-keyword">return new</span> <span class="tx-func">Promise</span>((resolve, reject) => {
    <span class="tx-keyword">let</span> count = <span class="tx-num">0</span>;
    
    <span class="tx-keyword">const</span> readStream = fs.<span class="tx-func">createReadStream</span>(inputPath, { encoding: <span class="tx-string">'utf8'</span> });
    <span class="tx-keyword">const</span> writeStream = fs.<span class="tx-func">createWriteStream</span>(outputPath, { encoding: <span class="tx-string">'utf8'</span> });
    
    <span class="tx-keyword">const</span> csvParser = <span class="tx-func">parse</span>({
      columns: <span class="tx-keyword">true</span>,
      skip_empty_lines: <span class="tx-keyword">true</span>,
      trim: <span class="tx-keyword">true</span>,
      cast: <span class="tx-keyword">true</span>
    });
    
    <span class="tx-keyword">const</span> jsonStringifier = stringify.<span class="tx-func">stringify</span>(<span class="tx-string">'*'</span>);
    
    csvParser.<span class="tx-func">on</span>(<span class="tx-string">'data'</span>, () => {
      count++;
      <span class="tx-keyword">if</span> (count % <span class="tx-num">10000</span> === <span class="tx-num">0</span>) {
        process.stdout.<span class="tx-func">write</span>(<span class="tx-string">`\rโณ ${count.toLocaleString()}ํ–‰ ์ฒ˜๋ฆฌ ์ค‘...`</span>);
      }
    });
    
    writeStream.<span class="tx-func">on</span>(<span class="tx-string">'finish'</span>, () => {
      console.<span class="tx-func">log</span>(<span class="tx-string">`\nโœ… ์™„๋ฃŒ! ์ด ${count.toLocaleString()}ํ–‰`</span>);
      <span class="tx-func">resolve</span>(count);
    });
    
    readStream
      .<span class="tx-func">pipe</span>(csvParser)
      .<span class="tx-func">pipe</span>(jsonStringifier)
      .<span class="tx-func">pipe</span>(writeStream);
    
    readStream.<span class="tx-func">on</span>(<span class="tx-string">'error'</span>, reject);
    csvParser.<span class="tx-func">on</span>(<span class="tx-string">'error'</span>, reject);
  });
}

<span class="tx-comment">// ์‹คํ–‰</span>
<span class="tx-func">csvToJsonStream</span>(<span class="tx-string">'./big_data.csv'</span>, <span class="tx-string">'./big_data.json'</span>)
  .<span class="tx-func">then</span>(count => console.<span class="tx-func">log</span>(<span class="tx-string">`์ฒ˜๋ฆฌ ์™„๋ฃŒ: ${count}ํ–‰`</span>))
  .<span class="tx-func">catch</span>(console.error);
โš ๏ธ ์ฃผ์˜: ๋Œ€์šฉ๋Ÿ‰ JSON ํŒŒ์ผ์€ ๊ทธ ์ž์ฒด๋กœ๋„ ๋ฌธ์ œ๊ฐ€ ๋  ์ˆ˜ ์žˆ์–ด. 100๋งŒ ํ–‰ JSON์„ ํ•œ ๋ฒˆ์— ํŒŒ์‹ฑํ•˜๋Š” ๊ฒƒ๋„ ๋ฉ”๋ชจ๋ฆฌ ์ด์Šˆ๊ฐ€ ์ƒ๊ฒจ. ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋Š” JSON Lines(JSONL) ํ˜•์‹์ด๋‚˜ DB ์ง์ ‘ ์‚ฝ์ž…์„ ๊ณ ๋ คํ•ด๋ด.

๐Ÿ“ JSON Lines (JSONL) ํ˜•์‹ โ€” ๋Œ€์šฉ๋Ÿ‰์˜ ์ง„์งœ ํ•ด๋‹ต

<span class="tx-comment"># JSONL: ๊ฐ ํ–‰์ด ๋…๋ฆฝ์ ์ธ JSON ๊ฐ์ฒด (์ค„๋ฐ”๊ฟˆ์œผ๋กœ ๊ตฌ๋ถ„)</span>
<span class="tx-comment"># {"name":"Alice","age":30,"city":"Seoul"}</span>
<span class="tx-comment"># {"name":"Bob","age":25,"city":"Busan"}</span>
<span class="tx-comment"># {"name":"Carol","age":28,"city":"Incheon"}</span>

<span class="tx-keyword">def</span> <span class="tx-func">csv_to_jsonl</span>(csv_path, jsonl_path):
    <span class="tx-string">"""CSV๋ฅผ JSONL(JSON Lines) ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ โ€” ๋Œ€์šฉ๋Ÿ‰ ์ตœ์ """</span>
    <span class="tx-keyword">import</span> csv, json
    
    count = <span class="tx-num">0</span>
    <span class="tx-keyword">with</span> <span class="tx-func">open</span>(csv_path, <span class="tx-string">'r'</span>, encoding=<span class="tx-string">'utf-8-sig'</span>) <span class="tx-keyword">as</span> csv_f, \
         <span class="tx-func">open</span>(jsonl_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> jsonl_f:
        
        reader = csv.DictReader(csv_f)
        <span class="tx-keyword">for</span> row <span class="tx-keyword">in</span> reader:
            <span class="tx-comment"># ๊ฐ ํ–‰์„ ํ•œ ์ค„์˜ JSON์œผ๋กœ ์ฆ‰์‹œ ๊ธฐ๋ก</span>
            jsonl_f.<span class="tx-func">write</span>(
                json.<span class="tx-func">dumps</span>(<span class="tx-func">dict</span>(row), ensure_ascii=<span class="tx-keyword">False</span>) + <span class="tx-string">'\n'</span>
            )
            count += <span class="tx-num">1</span>
    
    <span class="tx-func">print</span>(<span class="tx-string">f"โœ… JSONL ๋ณ€ํ™˜ ์™„๋ฃŒ: {count:,}ํ–‰"</span>)
    <span class="tx-keyword">return</span> count

๐ŸŽฏ 12. ์‹ค์ „ ํ™œ์šฉ ํŒจํ„ด & ๋งˆ๋ฌด๋ฆฌ

์ง€๊ธˆ๊นŒ์ง€ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ์ •๋ฆฌํ•ด๋ณด์ž.
๊ทธ๋ฆฌ๊ณ  ์ด ๋ชจ๋“  ๊ฑธ ํ•˜๋‚˜๋กœ ๋ฌถ์€ ์™„์„ฑํ˜• ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค๋„ ๋งŒ๋“ค์–ด๋ณผ๊ฒŒ! ๐Ÿš€

๐Ÿ—๏ธ ์™„์„ฑํ˜• Python ์œ ํ‹ธ๋ฆฌํ‹ฐ ํด๋ž˜์Šค

<span class="tx-keyword">import</span> csv
<span class="tx-keyword">import</span> json
<span class="tx-keyword">import</span> os
<span class="tx-keyword">from</span> typing <span class="tx-keyword">import</span> Optional, List, Dict, Any, Callable
<span class="tx-keyword">from</span> datetime <span class="tx-keyword">import</span> datetime


<span class="tx-keyword">class</span> <span class="tx-func">CsvToJsonConverter</span>:
    <span class="tx-string">"""
    ํ”„๋กœ๋•์…˜ ๋ ˆ๋”” CSV โ†’ JSON ๋ณ€ํ™˜๊ธฐ
    
    Features:
    - ์ธ์ฝ”๋”ฉ ์ž๋™ ๊ฐ์ง€ (chardet)
    - BOM ์ž๋™ ์ฒ˜๋ฆฌ
    - ํƒ€์ž… ์ž๋™ ๋ณ€ํ™˜
    - ์ปค์Šคํ…€ ๋ณ€ํ™˜ ํ•จ์ˆ˜ ์ง€์›
    - ์ŠคํŠธ๋ฆฌ๋ฐ ๋ชจ๋“œ (๋Œ€์šฉ๋Ÿ‰)
    - ์ง„ํ–‰ ์ƒํ™ฉ ์ฝœ๋ฐฑ
    - ์—๋Ÿฌ ํ•ธ๋“ค๋ง
    """</span>
    
    <span class="tx-keyword">def</span> <span class="tx-func">__init__</span>(
        self,
        delimiter: str = <span class="tx-string">','</span>,
        encoding: Optional[str] = <span class="tx-keyword">None</span>,
        auto_type: bool = <span class="tx-keyword">True</span>,
        skip_empty_rows: bool = <span class="tx-keyword">True</span>,
        strip_whitespace: bool = <span class="tx-keyword">True</span>
    ):
        self.delimiter = delimiter
        self.encoding = encoding
        self.auto_type = auto_type
        self.skip_empty_rows = skip_empty_rows
        self.strip_whitespace = strip_whitespace
        self._custom_converters: Dict[str, Callable] = {}
    
    <span class="tx-keyword">def</span> <span class="tx-func">add_converter</span>(self, column: str, func: Callable):
        <span class="tx-string">"""ํŠน์ • ์ปฌ๋Ÿผ์— ์ปค์Šคํ…€ ๋ณ€ํ™˜ ํ•จ์ˆ˜ ๋“ฑ๋ก"""</span>
        self._custom_converters[column] = func
        <span class="tx-keyword">return</span> self  <span class="tx-comment"># ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹ ์ง€์›</span>
    
    <span class="tx-keyword">def</span> <span class="tx-func">_detect_encoding</span>(self, file_path: str) -> str:
        <span class="tx-keyword">try</span>:
            <span class="tx-keyword">import</span> chardet
            <span class="tx-keyword">with</span> <span class="tx-func">open</span>(file_path, <span class="tx-string">'rb'</span>) <span class="tx-keyword">as</span> f:
                raw = f.<span class="tx-func">read</span>(<span class="tx-num">50000</span>)
            result = chardet.<span class="tx-func">detect</span>(raw)
            enc = result.get(<span class="tx-string">'encoding'</span>, <span class="tx-string">'utf-8'</span>)
            <span class="tx-keyword">return</span> <span class="tx-string">'utf-8-sig'</span> <span class="tx-keyword">if</span> enc <span class="tx-keyword">and</span> <span class="tx-string">'utf'</span> <span class="tx-keyword">in</span> enc.lower() <span class="tx-keyword">else</span> enc
        <span class="tx-keyword">except</span> ImportError:
            <span class="tx-keyword">return</span> <span class="tx-string">'utf-8-sig'</span>
    
    <span class="tx-keyword">def</span> <span class="tx-func">_convert_value</span>(self, key: str, value: str) -> Any:
        <span class="tx-comment"># ์ปค์Šคํ…€ ๋ณ€ํ™˜๊ธฐ ์šฐ์„  ์ ์šฉ</span>
        <span class="tx-keyword">if</span> key <span class="tx-keyword">in</span> self._custom_converters:
            <span class="tx-keyword">return</span> self._custom_converters[key](value)
        
        <span class="tx-keyword">if</span> self.strip_whitespace <span class="tx-keyword">and</span> value:
            value = value.<span class="tx-func">strip</span>()
        
        <span class="tx-keyword">if</span> value == <span class="tx-string">''</span>:
            <span class="tx-keyword">return</span> <span class="tx-keyword">None</span>
        
        <span class="tx-keyword">if</span> <span class="tx-keyword">not</span> self.auto_type:
            <span class="tx-keyword">return</span> value
        
        lower = value.lower()
        <span class="tx-keyword">if</span> lower <span class="tx-keyword">in</span> (<span class="tx-string">'true'</span>, <span class="tx-string">'yes'</span>): <span class="tx-keyword">return</span> <span class="tx-keyword">True</span>
        <span class="tx-keyword">if</span> lower <span class="tx-keyword">in</span> (<span class="tx-string">'false'</span>, <span class="tx-string">'no'</span>): <span class="tx-keyword">return</span> <span class="tx-keyword">False</span>
        
        <span class="tx-keyword">try</span>: <span class="tx-keyword">return</span> <span class="tx-func">int</span>(value)
        <span class="tx-keyword">except</span> ValueError: <span class="tx-keyword">pass</span>
        
        <span class="tx-keyword">try</span>: <span class="tx-keyword">return</span> <span class="tx-func">float</span>(value)
        <span class="tx-keyword">except</span> ValueError: <span class="tx-keyword">pass</span>
        
        <span class="tx-keyword">return</span> value
    
    <span class="tx-keyword">def</span> <span class="tx-func">convert</span>(
        self,
        csv_path: str,
        json_path: Optional[str] = <span class="tx-keyword">None</span>,
        indent: int = <span class="tx-num">2</span>,
        progress_callback: Optional[Callable] = <span class="tx-keyword">None</span>
    ) -> List[Dict]:
        <span class="tx-string">"""๋ฉ”์ธ ๋ณ€ํ™˜ ๋ฉ”์„œ๋“œ"""</span>
        
        <span class="tx-keyword">if</span> <span class="tx-keyword">not</span> os.path.<span class="tx-func">exists</span>(csv_path):
            <span class="tx-keyword">raise</span> <span class="tx-func">FileNotFoundError</span>(<span class="tx-string">f"ํŒŒ์ผ ์—†์Œ: {csv_path}"</span>)
        
        encoding = self.encoding <span class="tx-keyword">or</span> self.<span class="tx-func">_detect_encoding</span>(csv_path)
        result = []
        
        <span class="tx-keyword">with</span> <span class="tx-func">open</span>(csv_path, <span class="tx-string">'r'</span>, encoding=encoding, newline=<span class="tx-string">''</span>) <span class="tx-keyword">as</span> f:
            reader = csv.DictReader(f, delimiter=self.delimiter)
            
            <span class="tx-keyword">if</span> reader.fieldnames:
                reader.fieldnames = [
                    h.<span class="tx-func">strip</span>() <span class="tx-keyword">for</span> h <span class="tx-keyword">in</span> reader.fieldnames
                ]
            
            <span class="tx-keyword">for</span> i, row <span class="tx-keyword">in</span> <span class="tx-func">enumerate</span>(reader):
                <span class="tx-keyword">if</span> self.skip_empty_rows:
                    <span class="tx-keyword">if</span> <span class="tx-func">all</span>(v == <span class="tx-string">''</span> <span class="tx-keyword">or</span> v <span class="tx-keyword">is</span> <span class="tx-keyword">None</span> <span class="tx-keyword">for</span> v <span class="tx-keyword">in</span> row.values()):
                        <span class="tx-keyword">continue</span>
                
                converted = {
                    k: self.<span class="tx-func">_convert_value</span>(k, v <span class="tx-keyword">or</span> <span class="tx-string">''</span>)
                    <span class="tx-keyword">for</span> k, v <span class="tx-keyword">in</span> row.items()
                    <span class="tx-keyword">if</span> k <span class="tx-keyword">is</span> <span class="tx-keyword">not</span> <span class="tx-keyword">None</span>
                }
                result.append(converted)
                
                <span class="tx-keyword">if</span> progress_callback <span class="tx-keyword">and</span> (i + <span class="tx-num">1</span>) % <span class="tx-num">1000</span> == <span class="tx-num">0</span>:
                    <span class="tx-func">progress_callback</span>(i + <span class="tx-num">1</span>)
        
        <span class="tx-keyword">if</span> json_path:
            <span class="tx-keyword">with</span> <span class="tx-func">open</span>(json_path, <span class="tx-string">'w'</span>, encoding=<span class="tx-string">'utf-8'</span>) <span class="tx-keyword">as</span> f:
                json.<span class="tx-func">dump</span>(result, f, ensure_ascii=<span class="tx-keyword">False</span>, indent=indent)
        
        <span class="tx-keyword">return</span> result


<span class="tx-comment"># ===== ์‚ฌ์šฉ ์˜ˆ์‹œ =====</span>

<span class="tx-comment"># ๊ธฐ๋ณธ ์‚ฌ์šฉ</span>
converter = <span class="tx-func">CsvToJsonConverter</span>()
data = converter.<span class="tx-func">convert</span>(<span class="tx-string">'input.csv'</span>, <span class="tx-string">'output.json'</span>)

<span class="tx-comment"># ๊ณ ๊ธ‰ ์‚ฌ์šฉ: ์ปค์Šคํ…€ ๋ณ€ํ™˜๊ธฐ + ์ง„ํ–‰ ์ƒํ™ฉ ์ถœ๋ ฅ</span>
<span class="tx-keyword">from</span> datetime <span class="tx-keyword">import</span> datetime

converter = (
    <span class="tx-func">CsvToJsonConverter</span>(delimiter=<span class="tx-string">','</span>, auto_type=<span class="tx-keyword">True</span>)
    .<span class="tx-func">add_converter</span>(<span class="tx-string">'birth_date'</span>, <span class="tx-keyword">lambda</span> v: datetime.<span class="tx-func">strptime</span>(v, <span class="tx-string">'%Y-%m-%d'</span>).<span class="tx-func">isoformat</span>() <span class="tx-keyword">if</span> v <span class="tx-keyword">else</span> <span class="tx-keyword">None</span>)
    .<span class="tx-func">add_converter</span>(<span class="tx-string">'phone'</span>, <span class="tx-keyword">lambda</span> v: v.<span class="tx-func">replace</span>(<span class="tx-string">'-'</span>, <span class="tx-string">''</span>) <span class="tx-keyword">if</span> v <span class="tx-keyword">else</span> <span class="tx-keyword">None</span>)
)

data = converter.<span class="tx-func">convert</span>(
    <span class="tx-string">'users.csv'</span>,
    <span class="tx-string">'users.json'</span>,
    progress_callback=<span class="tx-keyword">lambda</span> n: <span class="tx-func">print</span>(<span class="tx-string">f"โณ {n:,}ํ–‰ ์ฒ˜๋ฆฌ ์ค‘..."</span>, end=<span class="tx-string">'\r'</span>)
)
<span class="tx-func">print</span>(<span class="tx-string">f"\n๐ŸŽ‰ ์™„๋ฃŒ! {len(data):,}๊ฐœ ๋ ˆ์ฝ”๋“œ"</span>)

๐ŸŒ REST API ์—”๋“œํฌ์ธํŠธ๋กœ ๋งŒ๋“ค๊ธฐ (Flask)

<span class="tx-keyword">from</span> flask <span class="tx-keyword">import</span> Flask, request, jsonify
<span class="tx-keyword">import</span> csv, json, io

app = <span class="tx-func">Flask</span>(__name__)

<span class="tx-comment"># POST /convert  โ†’  CSV ํŒŒ์ผ ์—…๋กœ๋“œ โ†’ JSON ๋ฐ˜ํ™˜</span>
@app.<span class="tx-func">route</span>(<span class="tx-string">'/convert'</span>, methods=[<span class="tx-string">'POST'</span>])
<span class="tx-keyword">def</span> <span class="tx-func">convert_csv</span>():
    <span class="tx-keyword">if</span> <span class="tx-string">'file'</span> <span class="tx-keyword">not in</span> request.files:
        <span class="tx-keyword">return</span> <span class="tx-func">jsonify</span>({<span class="tx-string">'error'</span>: <span class="tx-string">'ํŒŒ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค'</span>}), <span class="tx-num">400</span>
    
    file = request.files[<span class="tx-string">'file'</span>]
    
    <span class="tx-keyword">if</span> <span class="tx-keyword">not</span> file.filename.<span class="tx-func">endswith</span>(<span class="tx-string">'.csv'</span>):
        <span class="tx-keyword">return</span> <span class="tx-func">jsonify</span>({<span class="tx-string">'error'</span>: <span class="tx-string">'CSV ํŒŒ์ผ๋งŒ ํ—ˆ์šฉ๋ฉ๋‹ˆ๋‹ค'</span>}), <span class="tx-num">400</span>
    
    <span class="tx-keyword">try</span>:
        content = file.<span class="tx-func">read</span>().<span class="tx-func">decode</span>(<span class="tx-string">'utf-8-sig'</span>)
        reader = csv.DictReader(io.<span class="tx-func">StringIO</span>(content))
        result = [<span class="tx-func">dict</span>(row) <span class="tx-keyword">for</span> row <span class="tx-keyword">in</span> reader]
        
        <span class="tx-keyword">return</span> <span class="tx-func">jsonify</span>({
            <span class="tx-string">'success'</span>: <span class="tx-keyword">True</span>,
            <span class="tx-string">'total'</span>: <span class="tx-func">len</span>(result),
            <span class="tx-string">'data'</span>: result
        })
    <span class="tx-keyword">except</span> Exception <span class="tx-keyword">as</span> e:
        <span class="tx-keyword">return</span> <span class="tx-func">jsonify</span>({<span class="tx-string">'error'</span>: <span class="tx-func">str</span>(e)}), <span class="tx-num">500</span>

<span class="tx-keyword">if</span> __name__ == <span class="tx-string">'__main__'</span>:
    app.<span class="tx-func">run</span>(debug=<span class="tx-keyword">True</span>)

๐Ÿ“Š ์–ธ์–ด๋ณ„ ์ถ”์ฒœ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ •๋ฆฌ

๐Ÿ
Python
csv (ํ‘œ์ค€)
pandas
chardet
๐ŸŸจ
JavaScript
csv-parse
papaparse
fast-csv
โ˜•
Java
OpenCSV
Jackson
Apache Commons
๐Ÿน
Go
encoding/csv
(ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ)
๐Ÿ˜
PHP
fgetcsv
(๋‚ด์žฅ ํ•จ์ˆ˜)
๐Ÿ’Ž
Ruby
csv (ํ‘œ์ค€)
smarter_csv

โœ… ์ตœ์ข… ์ฒดํฌ๋ฆฌ์ŠคํŠธ

๐ŸŽฏ ํ”„๋กœ๋•์…˜ ๋ฐฐํฌ ์ „ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•  ๊ฒƒ๋“ค:
  • โ˜‘๏ธ ์ธ์ฝ”๋”ฉ: UTF-8 BOM ์ฒ˜๋ฆฌ + EUC-KR ๋Œ€์‘ ํ™•์ธ
  • โ˜‘๏ธ ํŠน์ˆ˜๋ฌธ์ž: ์‰ผํ‘œ/์ค„๋ฐ”๊ฟˆ/๋”ฐ์˜ดํ‘œ ํฌํ•จ๋œ ๊ฐ’ ํ…Œ์ŠคํŠธ
  • โ˜‘๏ธ ๋นˆ ๊ฐ’: ๋นˆ ๋ฌธ์ž์—ด โ†’ null ๋ณ€ํ™˜ ํ™•์ธ
  • โ˜‘๏ธ ํƒ€์ž…: ์ˆซ์ž/๋ถˆ๋ฆฌ์–ธ ํƒ€์ž… ๋ณ€ํ™˜ ํ™•์ธ
  • โ˜‘๏ธ ํ—ค๋”: ๊ณต๋ฐฑ ์ œ๊ฑฐ, ์ค‘๋ณต ํ—ค๋” ์ฒ˜๋ฆฌ
  • โ˜‘๏ธ ๋Œ€์šฉ๋Ÿ‰: 10๋งŒ ํ–‰ ์ด์ƒ ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ
  • โ˜‘๏ธ ์—๋Ÿฌ ์ฒ˜๋ฆฌ: ํŒŒ์ผ ์—†์Œ, ์ž˜๋ชป๋œ ํ˜•์‹ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ
  • โ˜‘๏ธ ํ•œ๊ธ€: ensure_ascii=False / JSON_UNESCAPED_UNICODE ์„ค์ •
๐ŸŽ‰ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

CSV โ†’ JSON ๋ณ€ํ™˜, ๊ฒ‰๋ณด๊ธฐ์—” ๋‹จ์ˆœํ•ด ๋ณด์ด์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ์ธ์ฝ”๋”ฉ, ํŠน์ˆ˜๋ฌธ์ž, ํƒ€์ž… ๋ณ€ํ™˜, ๋Œ€์šฉ๋Ÿ‰ ์ฒ˜๋ฆฌ ๋“ฑ ์‹ ๊ฒฝ ์จ์•ผ ํ•  ๊ฒŒ ๊ฝค ๋งŽ์•„.

์ด ๊ฐ€์ด๋“œ์—์„œ ๋‹ค๋ฃฌ ์ฝ”๋“œ๋“ค์€ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ˆ˜์ค€์œผ๋กœ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ, ํ”„๋กœ์ ํŠธ์— ๋งž๊ฒŒ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•ด์„œ ํ™œ์šฉํ•ด๋ด! ๐Ÿ’ช

๊ฐœ๋ฐœ ๊ด€๋ จ ๋‹ค์–‘ํ•œ ์žฌ๋Šฅ์„ ์ฐพ๊ณ  ์žˆ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท์—์„œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ, API ๊ฐœ๋ฐœ, ์ž๋™ํ™” ์Šคํฌ๋ฆฝํŠธ ๋“ฑ ๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ ์žฌ๋Šฅ์„ ๊ฐ€์ง„ ์ „๋ฌธ๊ฐ€๋“ค์„ ๋งŒ๋‚˜๋ณผ ์ˆ˜ ์žˆ์–ด. ํ˜ผ์ž ํ•ด๊ฒฐํ•˜๊ธฐ ์–ด๋ ค์šด ๋ณ€ํ™˜ ์ž‘์—…์ด ์žˆ๋‹ค๋ฉด ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ์„ ํƒ์ด์•ผ! ๐Ÿš€
5
์ง€์› ์–ธ์–ด
15+
์†Œ์Šค์ฝ”๋“œ ์˜ˆ์ œ
6
์—ฃ์ง€์ผ€์ด์Šค ๋Œ€์‘
โˆž
ํ™œ์šฉ ๊ฐ€๋Šฅ์„ฑ

CSV JSON Python JavaScript Java Go PHP ๋ฐ์ดํ„ฐ๋ณ€ํ™˜ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.