Parquet과 CSV 파일 형식 비교¶
한 줄 요약: CSV는 표를 한 줄씩 글자로 적은 파일이고, Parquet은 표를 열(세로줄)별로 묶어 타입과 함께 압축한 파일이다. 차이는 전송 시간만이 아니라 디스크 크기, 서버 처리, 받은 뒤 분석까지 이어진다. CSV가 이기는 곳은 "엑셀에서 바로 열린다" 하나다.
📍 만난 상황¶
- 2026-11-11 11:39 — disaster-dashboard 다운로드를 sqlway 직접 질의로 바꾸는 구조를 정리한 직후였다(→ 작업 5). 이 구조에서 Parquet과 CSV 중 성능이 좋은 쪽이 어디냐고 물었다 → 그날 일지
- 도로굴착 전체 90,439행으로 실제로 재 봤다. Parquet이 앞섰다.
- CSV도 gzip 전송 압축을 켜면 받는 시간만 비슷해졌다.
- 그래서 "Parquet 기본, CSV 선택"으로 정했고, 받기 단추를 스플릿 버튼으로 바꿨다.
🧩 쉽게 말하면¶
공책 명단과 라벨 붙은 서류함을 떠올리면 된다.
CSV는 공책에 한 사람씩 "이름, 날짜, 건수"를 손글씨로 한 줄씩 쓴 명단이다. 누구나 펼쳐 볼 수 있다. 하지만 두껍다. 숫자도 날짜도 다 손글씨라 읽는 사람이 다시 해석해야 한다.
Parquet은 "이름"끼리, "날짜"끼리, "건수"끼리 따로 모아 압축 포장한 서류함이다. 칸마다 "날짜", "정수" 라벨이 붙어 있다. 얇고 원하는 칸만 꺼내기 쉽다. 대신 전용 도구로 열어야 한다.
📖 제대로 알기¶
CSV¶
- CSV(Comma-Separated Values: 쉼표로 나눈 값): 한 줄이 한 행, 쉼표로 칸을 나누는 텍스트 파일.
- 타입 정보가 없다.
2026-11-05도3도 모두 글자다. - 규칙이 느슨하다. 구분자, 따옴표, 줄바꿈, 인코딩(글자를 바이트로 바꾸는 방식)이 도구마다 조금씩 다르다.
Parquet¶
- Parquet: Apache 재단의 오픈소스 열 지향(columnar: 같은 열의 값을 붙여 저장) 이진(사람이 바로 읽을 수 없는 바이트) 형식.
- 구조: 파일 → 행 그룹(row group: 행을 일정 개수씩 묶은 덩어리) → 열별 조각(column chunk) → 페이지.
- 푸터(footer: 파일 끝 정보): 스키마(열 이름과 타입)와 열별 통계(최솟값·최댓값 등)를 담는다.
- 타입 보존: 정수, 실수, 시각, 문자열, NULL(값 없음)을 구분해 저장한다.
두 가지 "압축"을 구분하기¶
- 파일 압축: 파일 자체가 압축된 상태. Parquet은 안에서 ZSTD·Snappy 같은 방식으로 이미 압축돼 있다.
- 전송 압축(HTTP
Content-Encoding: gzip): 보내는 동안만 압축하고, 받는 쪽에서 풀린다. 디스크에는 원래 크기로 남는다.
⚙️ 어떻게 동작하나¶
같은 표 3행을 두 형식으로 저장하면 이렇게 된다.
[CSV] 행 단위, 모두 글자
구,시각,건수
강남구,2026-11-05 10:00,3
강남구,2026-11-05 10:10,3
서초구,2026-11-05 10:00,1
[Parquet] 열 단위, 타입 있음
구(문자열): 강남구 ×2, 서초구
시각(시각): 10:00, 10:10, 10:00
건수(정수): 3 ×2, 1
- 쓰기: 행을 행 그룹으로 모은다. 그 안에서 열별로 나눈다.
- 인코딩: 같은 열에는 비슷한 값이 모여 있다. 반복은 "값 × 횟수"로(RLE), 자주 나오는 글자는 번호로(사전 인코딩) 바꾼다.
- 압축: 열 조각마다 ZSTD 등으로 한 번 더 압축한다. 비슷한 값끼리 모여 있어서 잘 줄어든다.
- 푸터 기록: 스키마와 통계를 파일 끝에 적는다.
- 읽기: 푸터를 먼저 읽는다. 필요한 열만 꺼내고, 통계상 조건에 안 맞는 행 그룹은 건너뛴다.
CSV는 읽을 때 처음부터 한 줄씩 끝까지 읽어야 한다. 쉼표로 자르고, 글자를 숫자·날짜로 추측해서 바꾼다.
왜 열 단위로 설계했나: 분석 질의는 "열 몇 개만, 행은 아주 많이" 읽는 경우가 많다. 열을 붙여 두면 필요한 열만 읽고, 압축도 잘 된다.
🧪 예시¶
-
예시 1 · 일상에 빗대어: 9만 명 명단을 보낸다고 하자. 공책(CSV)은 74권, 서류함(Parquet)은 4~5칸이다. 택배 상자에 꾹꾹 눌러 담으면(gzip) 공책도 운송비는 비슷해진다. 하지만 받은 사람 책상에는 다시 74권이 펼쳐진다.
-
예시 2 · 실제로는: 오늘 잰 값이다(sqlway 직접 질의, 도로굴착 90,439행, 각 2~3회).
| 형식 (전송 압축) | 오가는 크기 | 전체 시간 |
|---|---|---|
| Parquet (없음) | 4.5MB | 1.07~1.18초 |
| Parquet (gzip) | 4.4MB | 0.96~1.16초 |
| CSV (없음) | 74.2MB | 7.3~9.1초 |
| CSV (gzip) | 4.2MB | 1.17~1.38초 |
받은 파일 크기는 Parquet 4.5MB, CSV 74.2MB다. CSV는 gzip으로 받아도 풀려서 74.2MB가 된다. 참고로 예전 방식(매니저 경유 CSV)은 5만 행에서 잘리고 39.7MB에 37.7초였다.
받은 뒤 분석할 때 차이는 이렇다.
import pandas as pd
p = pd.read_parquet("road.parquet") # ①
c = pd.read_csv("road.csv") # ②
p.dtypes # 시각: datetime64, 건수: int64 # ③
c.dtypes # 시각: object(글자) # ④
c["시각"] = pd.to_datetime(c["시각"]) # ⑤
pd.read_parquet("road.parquet", columns=["구"]) # ⑥
⚖️ 비교 · 흔한 오해¶
| 항목 | Parquet | CSV |
|---|---|---|
| 저장 방식 | 열 단위, 이진, 압축 | 행 단위, 텍스트 |
| 타입 | 보존 | 없음 (모두 글자) |
| 빈 값과 NULL | 구분됨 | 구분이 흐림 |
| 사용자 디스크 | 4.5MB | 74.2MB (약 16배) |
| 대시보드 서버 처리 | 그대로 전달만 | 엑셀용 손질 필요 |
| 서버 메모리 | 파일 크기만큼 모았다가 보냄 | 한 줄씩 흘려보낼 수 있음 |
| 엑셀로 바로 열기 | 안 열림 | 바로 열림 |
"엑셀용 손질"은 두 가지다. 한글이 안 깨지게 BOM(파일 앞에 붙이는 "이건 UTF-8" 표시)을 붙이고, =로 시작하는 값이 엑셀에서 수식으로 실행되지 않게 막는다(CSV 수식 주입 방지). gzip을 켜면 이걸 위해 압축 풀기 → 한 줄씩 고치기 → 다시 압축을 해야 해서 CPU를 쓴다.
헷갈렸던 점: "형식 차이 = 전송 시간 차이"¶
- 왜 그렇게 생각하기 쉬운가: 다운로드 속도 문제로 시작한 이야기였다. 게다가 gzip을 켜면 시간이 비슷해지니 "그럼 같은 것 아닌가" 싶다.
- 실제로는: 전송 압축은 이동 중에만 작다. 받으면 16배 크기로 풀리고, 모든 값을 다시 해석해야 하고, 서버는 엑셀용 손질에 CPU를 쓴다.
또 다른 오해: "Parquet은 언제나 몇 배 작다"¶
데이터 성격에 따라 다르다. 도로굴착은 attrs(JSON 글자) 열이 큰 데이터였다. 숫자 위주 데이터라면 Parquet 압축 효과가 더 크다.
🛠️ 실무에서는¶
- 기본값은 Parquet, 엑셀 사용자를 위해 CSV를 선택지로 둔다.
- CSV를 줄 때 체크리스트
- gzip 전송 압축을 켰나?
- BOM을 붙였나? (엑셀 한글 깨짐)
- 수식 주입을 막았나?
- Parquet을 줄 때는 여는 법을 안내한다. 엑셀은 Power Query 같은 기능이 필요하다. 분석가는 pandas나 DuckDB로 바로 읽는다.
- 받을 사람이 누구인지가 가장 먼저 정할 기준이다. 분석가면 Parquet, 엑셀만 쓰는 담당자면 CSV.
- 받은 Parquet 내용을 빨리 보고 싶으면 DuckDB에서
SELECT * FROM 'road.parquet' LIMIT 5;처럼 바로 질의할 수 있다.
❓ 자주 묻는 질문¶
- Q. Parquet은 왜 gzip 전송 압축을 해도 거의 안 줄어드나? A. 이미 안에서 ZSTD로 압축돼 있어서다. 압축한 걸 또 압축하면 거의 안 줄어든다.
- Q. CSV를 zip으로 묶어 주면 되지 않나? A. 전송과 보관 크기는 줄어든다. 하지만 풀면 똑같이 크고, 타입이 없다는 문제는 그대로다.
- Q. Parquet을 메모장으로 열 수 있나? A. 없다. 이진 파일이라 깨진 글자만 보인다. 전용 도구가 필요하다.
🔭 더 깊이 보기¶
- 행 그룹 통계로 읽지 않고 건너뛰는 원리(predicate pushdown) — Parquet이 큰 파일에서도 빠른 진짜 이유라서.
- Apache Arrow — Parquet이 "디스크의 열 형식"이라면 Arrow는 "메모리의 열 형식"이라 함께 알아 두면 좋아서.
- 관련 주제: OLTP와 OLAP의 차이 — 열 지향 형식이 왜 분석(OLAP)에 맞는지 이어진다
- 관련 주제: API 중간 계층 경유와 직접 질의 비교 — Parquet을 받으려고 직접 질의 경로를 쓴 이유
- 관련 주제: 브라우저 다운로드와 Blob 메모리 — 같은 날 다운로드 방식을 따진 주제
📚 참고 자료¶
- 열 기반 스토리지 형식 (AWS 한국어 문서, Amazon Athena 사용 설명서) — 열 지향 형식이 왜 빠르고 작은지 · 링크
- Apache Parquet Documentation (Apache Parquet 공식 문서, 영문) — 파일 구조(행 그룹, 열 조각, 푸터) · 링크
- Reading and Writing Parquet Files (DuckDB 공식 문서, 영문) — Parquet을 SQL로 바로 읽는 법
🗓️ 이력¶
- 2026-11-11: sqlway 직접 질의 구조에서 Parquet과 CSV 중 성능이 좋은 쪽을 물음. 실측으로 Parquet 기본, CSV 선택으로 정함 → 일지