S3에 쌓인 수백만 개의 Parquet 파일은 그 자체로는 "그냥 바이트 덩어리"다. 어떤 컬럼이 있고, 타입이 무엇이며, 어떤 키로 파티션되어 있는지를 아무도 모르면 SQL 한 줄도 던질 수 없다. 이 "데이터에 대한 데이터", 즉 메타데이터를 중앙에서 관리하는 것이 AWS Glue Data Catalog다. 시험에서 "Athena가 S3를 조회한다", "Redshift Spectrum이 외부 테이블을 읽는다", "EMR이 Hive 메타스토어를 공유한다"는 시나리오가 나오면 그 뒤에는 거의 항상 Glue Data Catalog가 있다.
Data Catalog는 본질적으로 Apache Hive 메타스토어와 호환되는 관리형 메타데이터 저장소다. Hive 메타스토어를 RDS에 직접 띄워 운영해 본 사람이라면, 그 운영 부담(가용성, 백업, 스키마 버전)을 AWS가 대신 져 주는 서비스라고 이해하면 된다.
Catalog는 계층 구조를 가진다.
계층
의미
예시
Database
테이블의 논리적 그룹(네임스페이스)
sales_db
Table
스키마 + S3 위치 + 형식 정의
orders
Partition
테이블 내부의 물리적 분할
year=2026/month=06
Column
컬럼 이름과 데이터 타입
order_id: bigint
여기서 가장 중요한 점은 테이블이 데이터를 담지 않는다는 것이다. 테이블은 "실제 데이터는 s3://bucket/orders/에 있고, Parquet 형식이며, 컬럼은 이렇다"는 포인터이자 명세일 뿐이다. 이 분리 덕분에 같은 S3 데이터를 Athena, Redshift Spectrum, EMR, Glue ETL이 모두 동일한 스키마로 읽을 수 있다.
-- Catalog에 등록된 테이블 정의(개념적 DDL)CREATE EXTERNAL TABLE sales_db.orders ( order_id BIGINT, amount DECIMAL(10,2), status STRING)PARTITIONED BY (year INT, month INT)STORED AS PARQUETLOCATION 's3://my-lake/orders/';
"스키마-온-리드(schema-on-read)"는 데이터 레이크의 핵심 사상이다. 전통적 데이터베이스(스키마-온-라이트)는 데이터를 쓰는 순간 스키마를 강제한다. 반대로 데이터 레이크는 원본을 그대로 S3에 저장하고, 읽는 시점에 스키마를 적용한다. Data Catalog는 바로 이 "읽는 시점의 스키마"를 보관하는 곳이다. 같은 파일에 여러 테이블(다른 스키마)을 매핑할 수도 있는 유연성이 여기서 나온다.
테이블을 일일이 DDL로 등록하는 건 비현실적이다. 컬럼이 수십 개고 파티션이 매일 늘어나는 데이터에서는 더더욱 그렇다. Glue 크롤러(Crawler)는 지정한 데이터 저장소(주로 S3 경로)를 스캔해, 파일 형식과 스키마를 추론하고, 그 결과를 Catalog에 테이블/파티션으로 자동 등록한다.
크롤러의 동작 단계는 다음과 같다.
1. 데이터 저장소 연결 (S3 경로, JDBC, DynamoDB 등)
2. Classifier로 형식 판별 (Parquet/JSON/CSV/ORC/Avro...)
3. 스키마 추론 (컬럼명, 타입)
4. 파티션 구조 탐지 (폴더 경로의 key=value 패턴)
5. Catalog에 테이블 생성 또는 기존 테이블 업데이트
크롤러는 Classifier로 형식을 먼저 판별한다. 내장 Classifier가 Parquet, ORC, Avro, JSON, CSV, XML 등 표준 형식을 인식하고, 비표준 형식은 Custom Classifier(Grok 패턴, 정규식 등)를 직접 정의해 처리한다. 여러 Classifier를 우선순위 순으로 시도하다가 처음으로 매칭되는 것을 채택한다.
크롤러는 비싸거나 느릴 수 있다. S3 객체가 수백만 개면 매번 전체를 스캔하는 비용이 만만치 않다. 그래서 증분 크롤(Incremental crawl) 옵션이 있다. "마지막 크롤 이후 새로 추가된 폴더만" 스캔하도록 설정하면 비용과 시간이 크게 줄어든다. 또한 새 파티션만 늘어나는 테이블이라면, 크롤러 대신 ALTER TABLE ADD PARTITION이나 파티션 프로젝션(Partition Projection, Athena)으로 크롤러 자체를 없앨 수도 있다.
크롤러가 가장 똑똑하게 처리하는 부분이 파티션 탐지다. S3 경로가 s3://my-lake/orders/year=2026/month=06/day=25/ 처럼 Hive 스타일 key=value 형식이면, 크롤러는 year, month, day를 파티션 컬럼으로 자동 인식한다.
s3://my-lake/orders/
├── year=2025/month=12/ ← 파티션 (year=2025, month=12)
├── year=2026/month=05/
└── year=2026/month=06/ ← 새로 추가되면 크롤러가 파티션 등록
파티션이 중요한 이유는 쿼리 프루닝(partition pruning) 때문이다. WHERE year=2026 AND month=06으로 조회하면 Athena는 해당 폴더의 파일만 스캔한다. 파티션이 없으면 매번 전체 데이터를 스캔하므로, 스캔량 기반 과금인 Athena에서는 비용이 폭증한다.
⚠️ 함정: 폴더가 key=value가 아니라 그냥 2026/06/25/라면 크롤러는 파티션 이름을 자동으로 못 만들고 partition_0, partition_1 같은 무의미한 이름을 붙인다. 데이터 적재 단계에서 처음부터 Hive 스타일 경로를 쓰는 것이 정석이다. 이미 비-Hive 경로로 쌓였다면, 테이블 정의에서 파티션을 수동 매핑하거나 Athena 파티션 프로젝션으로 우회한다.
크롤러의 스키마 추론은 "파일 샘플을 읽어 타입을 추측"하는 것이라 완벽하지 않다. 대표적인 함정 두 가지를 알아야 한다.
첫째, 타입 추론 충돌. 같은 컬럼이 어떤 파일에서는 정수(123), 다른 파일에서는 문자열("N/A")로 나타나면 크롤러는 더 넓은 타입(string)으로 올리거나, 폴더별로 다른 스키마를 만들어 버린다.
둘째, 스키마 병합과 테이블 분리. 한 경로 아래 호환되지 않는 스키마의 파일들이 섞여 있으면, 크롤러는 하나의 테이블이 아니라 여러 테이블로 쪼갤 수 있다. 이를 막으려면 크롤러 설정에서 "데이터 호환 시 단일 스키마로 결합" 옵션을 켠다.
🎯 시나리오: 매일 새 파일이 s3://lake/events/dt=YYYY-MM-DD/에 적재된다. 분석가는 Athena로 어제 데이터만 빠르게 조회하고 싶다. 구성은 (1) 데이터를 처음부터 dt= Hive 경로로 적재 → (2) Glue 크롤러를 매일 새벽 증분 크롤로 스케줄 → (3) 크롤러가 새 dt= 파티션을 Catalog에 등록 → (4) 분석가는 WHERE dt='2026-06-25'로 해당 파티션만 스캔. 새 파티션만 늘어나므로 크롤러 대신 파티션 프로젝션으로 비용을 더 줄일 수도 있다.
새 파티션 폴더가 S3에 생겨도 Catalog가 모르면 Athena는 그 데이터를 읽지 못한다. "어제 데이터가 조회되지 않는다"는 증상의 대부분이 이 등록 누락이다. 등록 방법은 넷이다.
방법
동작
맞는 상황
주의
크롤러
S3를 스캔해 스키마와 파티션을 함께 추론·등록
스키마를 모르거나 바뀔 수 있을 때
스캔 시간만큼 과금되고, 실행 주기만큼 반영이 늦다
ALTER TABLE ADD PARTITION
지정한 파티션을 위치와 함께 등록
적재 파이프라인이 새 파티션을 알고 있을 때, 비-Hive 경로
파이프라인에 등록 단계를 넣어야 한다
MSCK REPAIR TABLE
테이블 위치를 훑어 Hive 스타일 파티션을 한꺼번에 추가
테이블을 처음 만들었거나 메타데이터와 S3가 어긋났는지 불확실할 때
추가만 하고 삭제는 하지 않는다. 파티션이 많으면 타임아웃이 난다
파티션 프로젝션
등록하지 않고 테이블 속성의 규칙으로 쿼리 시점에 계산
날짜·정수·열거값처럼 규칙이 예측 가능하고 파티션이 매우 많을 때
Athena에서만 동작한다
-- 적재 직후 파이프라인이 직접 파티션을 등록 (비-Hive 경로도 가능)ALTER TABLE events ADD IF NOT EXISTS PARTITION (dt = '2026-06-25') LOCATION 's3://lake/events/2026/06/25/';-- 파티션 프로젝션: 등록 없이 규칙으로 계산ALTER TABLE events SET TBLPROPERTIES ( 'projection.enabled' = 'true', 'projection.dt.type' = 'date', 'projection.dt.format' = 'yyyy-MM-dd', 'projection.dt.range' = '2024-01-01,NOW', 'storage.location.template' = 's3://lake/events/dt=${dt}/');
파티션 프로젝션이 빠른 이유와 그 대가를 함께 알아야 한다.
일반 테이블은 쿼리 때 Athena가 Catalog에 GetPartitions를 호출해 파티션 목록을 받아온 뒤 프루닝한다. 파티션이 아주 많으면 이 조회 자체가 느려진다. 프로젝션은 이 호출을 건너뛰고 테이블 속성만으로 파티션 위치를 계산한다.
프로젝션을 켠 테이블에서 Athena는 Catalog에 등록된 파티션 메타데이터를 무시한다. 크롤러가 등록해 둔 파티션이 있어도 쓰지 않는다.
범위 밖 값을 조회하면 오류 없이 0행이 반환된다. 범위 끝을 NOW가 아닌 고정 날짜로 두면, 그 날짜 이후 데이터가 아무 경고 없이 안 보이게 된다.
S3에 데이터가 없는 파티션도 계산은 된다(결과만 비어 있다). 계산되는 파티션의 절반 넘게 비어 있다면 일반 파티션이 오히려 낫다.
SHOW PARTITIONS에는 프로젝션으로 계산된 파티션이 나오지 않는다.
⚠️ 함정: 파티션 프로젝션은 Athena로 조회할 때만 적용된다. 같은 테이블을 Redshift Spectrum, Athena for Spark, EMR이 읽으면 Catalog에 등록된 표준 파티션 메타데이터를 쓴다. "크롤러를 없애 비용을 줄인다"는 보기가 정답이 되려면 소비자가 Athena뿐이어야 한다. 엔진이 여럿이면 증분 크롤이나 적재 시점의 ADD PARTITION이 답이다.
크롤러 설정에서 시험과 실무가 모두 묻는 것은 두 가지다. "다음 실행에서 어디까지 다시 볼 것인가"와 "스키마가 달라졌을 때 Catalog를 어떻게 할 것인가".
재크롤 방식
스캔 범위
특징
전체 크롤(기본)
대상 경로 전부
스키마 변경과 삭제까지 반영한다. 가장 느리고 비싸다
새 하위 폴더만(CRAWL_NEW_FOLDERS_ONLY)
직전 크롤 이후 추가된 폴더
첫 실행은 전체 크롤이다. 기존 파티션의 수정·삭제는 감지하지 못한다
S3 이벤트 기반
SQS에 쌓인 S3 이벤트가 가리키는 폴더
첫 실행은 전체 목록 조회다. 이벤트가 없으면 스캔할 것이 없다
증분 크롤을 켜면 스키마 갱신과 삭제 반영이 꺼진다. 갱신·삭제 동작이 모두 LOG로 고정되어, 스키마가 호환되지 않는 객체는 Catalog에 추가되지 않고 CloudWatch Logs에 기록만 남는다. 스키마가 안정된 데이터에 맞는 옵션이며, 큰 스키마 변경이 있을 때는 잠시 전체 크롤로 바꿨다가 되돌린다.
기본 동작은 "데이터에 맞춰 Catalog 스키마를 갱신"이다. ETL이 다듬어 둔 테이블 정의를 크롤러가 덮어쓰는 일을 막으려면 "새 컬럼만 추가"(MergeNewColumns)나 "변경을 무시하고 기록만"(UpdateBehavior=LOG)으로 바꾼다.
파티션이 테이블 스키마를 물려받게 할 수 있다(InheritFromTable). 파티션별 스키마가 테이블과 어긋나면 Athena가 HIVE_PARTITION_SCHEMA_MISMATCH로 실패하는데, 이 옵션이 그 어긋남을 없앤다.
한 경로에서 테이블이 여러 개로 쪼개지는 문제.table1/year=2017/의 파일은 컬럼이 A, B이고 table1/year=2018/은 C, D라면, 크롤러는 스키마가 충분히 비슷하지 않다고 보고 year_2017, year_2018 두 테이블을 만든다. "경로당 단일 스키마"(CombineCompatibleSchemas)를 켜면 형식만 호환되면 A·B·C·D를 가진 한 테이블에 year 파티션으로 묶는다.
# 새 폴더만 크롤 + 스키마가 다른 객체는 로그만 남김aws glue update-crawler \ --name orders-crawler \ --recrawl-policy RecrawlBehavior=CRAWL_NEW_FOLDERS_ONLY \ --schema-change-policy UpdateBehavior=LOG,DeleteBehavior=LOG# 한 경로 아래 스키마가 조금씩 달라도 테이블 하나로 묶기aws glue update-crawler \ --name orders-crawler \ --configuration '{"Version":1.0,"Grouping":{"TableGroupingPolicy":"CombineCompatibleSchemas"}}'
크롤러는 S3 대상 테이블에 기본으로 파티션 인덱스를 만든다. 파티션이 수백만 개인 테이블에서 "특정 연·월 파티션만" 찾을 때 전체 목록을 받아와 거르지 않고, Catalog가 인덱스로 서버 쪽에서 걸러 돌려준다. Glue ETL의 catalogPartitionPredicate 옵션이 바로 이 인덱스를 쓴다. 파티션 수가 문제의 원인일 때 프로젝션(Athena 전용)과 파티션 인덱스(엔진 공통)가 두 갈래 해법이다.
크롤러: 실행하는 동안 쓴 DPU-시간을 초 단위로 과금한다. 객체가 많을수록 목록 조회와 샘플 읽기가 길어지므로, 비용을 줄이는 길은 스캔 범위를 줄이는 것뿐이다.
Data Catalog: 저장된 객체(테이블·테이블 버전·파티션 등) 수와 요청 수가 기준이다. 처음 100만 객체 저장과 100만 건 요청까지는 무료 구간이다. 파티션이 수백만 개로 불어나면 저장 비용과 파티션 조회 지연이 함께 늘어난다 — 고카디널리티 컬럼으로 파티셔닝하면 안 되는 또 하나의 이유다.
크롤러가 만들지 않는 비용: 크롤러는 데이터를 복사하지 않으므로 S3 저장 비용이 중복되지 않는다. 쿼리 비용(Athena 스캔량)은 Catalog가 아니라 파일 형식과 파티션 설계가 결정한다.