데이터 저장소 - S3 Data Lake 설계
파일 포맷, 파티셔닝, 스토리지 클래스, Glue Data Catalog
목차
- S3 Data Lake 아키텍처
- 파일 포맷 비교
- 파티셔닝 전략
- S3 스토리지 클래스 및 수명 주기
- AWS Glue Data Catalog
- 정리 및 다음 세션 예고
1. S3 Data Lake 아키텍처
"Data Lake는 단순한 저장소가 아니라, 데이터의 가치를 극대화하는 아키텍처 패턴이다."
— AWS Data Lake Best Practices
1.1 Data Lake 계층 구조
효과적인 Data Lake는 데이터의 처리 단계에 따라 계층을 분리합니다. 각 계층은 명확한 목적과 데이터 품질 수준을 가집니다.
┌─────────────────────────────────────────────────────────────────────┐
│ S3 Data Lake │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ Raw / Landing Zone ││
│ │ s3://data-lake-raw/ ││
│ │ ├── database/orders/2024/01/15/ ││
│ │ ├── logs/clickstream/2024/01/15/ ││
│ │ └── saas/salesforce/opportunities/ ││
│ │ ││
│ │ • 원본 데이터 그대로 저장 (Immutable) ││
│ │ • 스키마 검증 없음 ││
│ │ • 데이터 리니지 추적용 ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ ETL/ELT │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ Curated / Processed Zone ││
│ │ s3://data-lake-curated/ ││
│ │ ├── orders/ (Parquet, 파티셔닝) ││
│ │ ├── customers/ (중복 제거, 정규화) ││
│ │ └── products/ (스키마 적용) ││
│ │ ││
│ │ • 정제된 데이터 (Cleansed) ││
│ │ • 표준 스키마 적용 ││
│ │ • Parquet/ORC 포맷 ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │ │
│ ▼ Aggregation │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ Analytics / Consumption Zone ││
│ │ s3://data-lake-analytics/ ││
│ │ ├── daily_sales_summary/ (집계 테이블) ││
│ │ ├── customer_360/ (통합 뷰) ││
│ │ └── ml_features/ (피처 스토어) ││
│ │ ││
│ │ • 비즈니스 로직 적용 ││
│ │ • 분석 최적화 (Pre-aggregated) ││
│ │ • BI 도구 직접 연결 ││
│ └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘1.2 S3 버킷 설계 전략
✅ 권장 패턴
# 계층별 버킷 분리
s3://company-data-lake-raw/
s3://company-data-lake-curated/
s3://company-data-lake-analytics/
# 또는 단일 버킷 + 접두사
s3://company-data-lake/
├── raw/
├── curated/
└── analytics/- • 계층별 IAM 정책 분리 용이
- • 수명 주기 정책 개별 적용
- • 비용 추적 명확
❌ 피해야 할 패턴
# 날짜를 최상위로
s3://data-lake/2024/01/15/orders/
s3://data-lake/2024/01/15/customers/
# 환경 혼재
s3://data-lake/prod/orders/
s3://data-lake/dev/orders/- • 테이블 단위 관리 어려움
- • 파티션 프루닝 비효율
- • 환경 분리 불명확
1.3 네이밍 컨벤션
# 권장 S3 경로 구조
s3://{bucket}/{layer}/{domain}/{table}/
{partition_key}={partition_value}/
{filename}.{format}
# 예시
s3://acme-data-lake-curated/
sales/ # 도메인
orders/ # 테이블
year=2024/ # 파티션
month=01/
day=15/
part-00000.parquet
# 파일명 규칙
{table}_{timestamp}_{uuid}.{format}
orders_20240115T103000_abc123.parquet버킷명
{company}-{purpose}-{env}
acme-data-lake-prod
경로
소문자, 언더스코어
snake_case 권장
파티션
Hive 스타일
key=value 형식
1.4 데이터 계층별 특성
| 특성 | Raw | Curated | Analytics |
|---|---|---|---|
| 데이터 형태 | 원본 그대로 | 정제/표준화 | 집계/변환 |
| 포맷 | JSON, CSV, XML | Parquet, ORC | Parquet |
| 스키마 | Schema-on-Read | Schema-on-Write | Schema-on-Write |
| 보존 기간 | 장기 (규정 준수) | 중기 | 단기~중기 |
| 스토리지 클래스 | IA/Glacier | Standard/IA | Standard |
| 접근 빈도 | 낮음 | 중간 | 높음 |
2. 파일 포맷 비교
데이터 레이크에서 파일 포맷 선택은 쿼리 성능, 저장 비용, 스키마 진화에 직접적인 영향을 미칩니다. 각 포맷의 특성을 이해하고 적절히 선택해야 합니다.
2.1 주요 파일 포맷
| 특성 | Parquet | ORC | Avro | JSON/CSV |
|---|---|---|---|---|
| 저장 방식 | 컬럼형 | 컬럼형 | 행 기반 | 행 기반 |
| 압축률 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| 분석 쿼리 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐ |
| 스키마 진화 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 쓰기 속도 | ⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| AWS 지원 | 최적 | |||
| 주요 사용처 | 분석, DW | Hive, Presto | 스트리밍, CDC | Raw 데이터 |
2.2 Parquet 심층 분석
Parquet은 AWS 데이터 레이크의 사실상 표준 포맷입니다. 컬럼형 저장으로 분석 쿼리에 최적화되어 있습니다.
┌─────────────────────────────────────────────────────────────────────┐
│ Parquet 파일 구조 │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ File ││
│ │ ┌─────────────────────────────────────────────────────────────┐││
│ │ │ Row Group 1 │││
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │││
│ │ │ │Column A │ │Column B │ │Column C │ │Column D │ │││
│ │ │ │ Chunk │ │ Chunk │ │ Chunk │ │ Chunk │ │││
│ │ │ │ │ │ │ │ │ │ │ │││
│ │ │ │ [Stats] │ │ [Stats] │ │ [Stats] │ │ [Stats] │ │││
│ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │││
│ │ └─────────────────────────────────────────────────────────────┘││
│ │ ┌─────────────────────────────────────────────────────────────┐││
│ │ │ Row Group 2 │││
│ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │││
│ │ │ │Column A │ │Column B │ │Column C │ │Column D │ │││
│ │ │ │ Chunk │ │ Chunk │ │ Chunk │ │ Chunk │ │││
│ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │││
│ │ └─────────────────────────────────────────────────────────────┘││
│ │ ┌─────────────────────────────────────────────────────────────┐││
│ │ │ Footer (Metadata) │││
│ │ │ • Schema │││
│ │ │ • Row Group locations │││
│ │ │ • Column statistics (min/max/null count) │││
│ │ └─────────────────────────────────────────────────────────────┘││
│ └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘
컬럼형 저장의 장점:
• SELECT col_a, col_b만 읽으면 해당 컬럼 청크만 스캔
• 같은 타입 데이터가 연속 → 압축 효율 극대화
• Column Statistics로 불필요한 Row Group 스킵Parquet 최적화 설정
- • Row Group Size: 128MB~1GB
- • Page Size: 1MB (기본값)
- • 압축: Snappy (속도), ZSTD (압축률)
- • Dictionary: 카디널리티 낮은 컬럼
Athena 최적 파일 크기
- • 권장: 128MB ~ 512MB
- • 너무 작으면: S3 요청 오버헤드
- • 너무 크면: 병렬 처리 비효율
- • Glue로 파일 병합 가능
2.3 포맷 변환 예시
// PySpark - JSON to Parquet 변환
from pyspark.sql import SparkSession
spark = SparkSession.builder \
.appName("JSON to Parquet") \
.config("spark.sql.parquet.compression.codec", "snappy") \
.getOrCreate()
# JSON 읽기
df = spark.read.json("s3://data-lake-raw/orders/2024/01/15/")
# 스키마 적용 및 변환
df_cleaned = df.select(
df.order_id.cast("long"),
df.customer_id.cast("long"),
df.amount.cast("decimal(10,2)"),
df.order_date.cast("date"),
df.status.cast("string")
)
# Parquet로 저장 (파티셔닝 포함)
df_cleaned.write \
.mode("overwrite") \
.partitionBy("order_date") \
.option("compression", "snappy") \
.parquet("s3://data-lake-curated/orders/")// AWS Glue - 포맷 변환 Job
import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from awsglue.context import GlueContext
from pyspark.context import SparkContext
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
# Glue Catalog에서 읽기
datasource = glueContext.create_dynamic_frame.from_catalog(
database="raw_db",
table_name="orders_json"
)
# 스키마 변환
mapped = ApplyMapping.apply(
frame=datasource,
mappings=[
("order_id", "string", "order_id", "long"),
("amount", "string", "amount", "decimal(10,2)"),
("order_date", "string", "order_date", "date")
]
)
# Parquet로 저장
glueContext.write_dynamic_frame.from_options(
frame=mapped,
connection_type="s3",
connection_options={
"path": "s3://data-lake-curated/orders/",
"partitionKeys": ["order_date"]
},
format="parquet",
format_options={"compression": "snappy"}
)2.4 포맷 선택 가이드
Parquet 선택
분석 쿼리, Athena/Redshift Spectrum, 대부분의 Curated/Analytics 계층
ORC 선택
Hive 기반 워크로드, EMR Hive, 기존 Hadoop 에코시스템
Avro 선택
스트리밍 데이터, Kafka, 스키마 진화가 빈번한 경우
JSON/CSV 유지
Raw 계층 원본 보존, 외부 시스템 연동, 디버깅용
3. 파티셔닝 전략
파티셔닝은 데이터를 논리적으로 분할하여 쿼리 성능을 최적화하는 핵심 전략입니다. 적절한 파티셔닝은 스캔 데이터량을 줄여 비용과 시간을 절약합니다.
3.1 파티셔닝 기본 개념
┌─────────────────────────────────────────────────────────────────────┐
│ 파티션 프루닝 효과 │
│ │
│ 쿼리: SELECT * FROM orders WHERE order_date = '2024-01-15' │
│ │
│ 파티셔닝 없음 (Full Scan) │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ ████████████████████████████████████████████████████████████████ ││
│ │ 전체 데이터 스캔: 1TB → 비용: $5.00, 시간: 60초 ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │
│ 날짜 파티셔닝 (Partition Pruning) │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ orders/ ││
│ │ ├── year=2024/ ││
│ │ │ ├── month=01/ ││
│ │ │ │ ├── day=14/ ░░░░░░░░ (스킵) ││
│ │ │ │ ├── day=15/ ████████ (스캔) ← 이것만! ││
│ │ │ │ └── day=16/ ░░░░░░░░ (스킵) ││
│ │ 스캔 데이터: 3GB → 비용: $0.015, 시간: 2초 ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │
│ 💰 비용 절감: 99.7% | ⚡ 성능 향상: 30x │
└─────────────────────────────────────────────────────────────────────┘3.2 파티션 키 선택
좋은 파티션 키
- ✓날짜/시간: 대부분의 쿼리가 시간 범위 필터
- ✓지역/국가: 지역별 분석이 빈번한 경우
- ✓카테고리: 카디널리티가 적절한 경우 (10~1000)
- ✓테넌트 ID: 멀티테넌트 환경
피해야 할 파티션 키
- ✗고유 ID: 파티션 수 폭발 (수백만 개)
- ✗타임스탬프: 초/밀리초 단위는 과도함
- ✗쿼리에 안 쓰는 컬럼: 프루닝 효과 없음
- ✗불균형 분포: 특정 파티션에 데이터 집중
3.3 파티셔닝 패턴
패턴 1: 시간 기반 파티셔닝
# 일별 파티셔닝 (가장 일반적)
s3://bucket/orders/year=2024/month=01/day=15/
# 시간별 파티셔닝 (고빈도 데이터)
s3://bucket/logs/year=2024/month=01/day=15/hour=10/
# 월별 파티셔닝 (저빈도 데이터)
s3://bucket/reports/year=2024/month=01/대부분의 분석 쿼리가 시간 범위를 포함하므로 가장 효과적
패턴 2: 복합 파티셔닝
# 지역 + 날짜
s3://bucket/sales/region=us-east/year=2024/month=01/day=15/
# 테넌트 + 날짜
s3://bucket/events/tenant_id=acme/year=2024/month=01/day=15/쿼리 패턴에 따라 2~3개 키 조합. 순서는 카디널리티 낮은 것 먼저
패턴 3: 버킷팅 (Bucketing)
# 고카디널리티 컬럼을 해시로 분산
# customer_id를 100개 버킷으로 분산
s3://bucket/orders/bucket=00/
s3://bucket/orders/bucket=01/
...
s3://bucket/orders/bucket=99/
# Spark에서 버킷팅
df.write.bucketBy(100, "customer_id").saveAsTable("orders")JOIN 성능 최적화. 같은 버킷 키로 조인하면 셔플 최소화
3.4 파티션 수 관리
⚠️ 파티션 수 주의사항
- • Athena: 테이블당 최대 100만 파티션
- • Glue Catalog: 파티션 메타데이터 조회 오버헤드
- • S3: 파티션당 최소 128MB 파일 권장
| 파티션 전략 | 연간 파티션 수 | 권장 사용 사례 |
|---|---|---|
| 연도별 | 1 | 아카이브, 연간 보고서 |
| 월별 | 12 | 월간 분석, 저빈도 데이터 |
| 일별 | 365 | 일반적인 트랜잭션 데이터 |
| 시간별 | 8,760 | 로그, 이벤트 스트림 |
| 지역(10) + 일별 | 3,650 | 글로벌 서비스 |
3.5 파티션 추가 자동화
// Glue Crawler vs MSCK REPAIR vs Partition Projection
-- 방법 1: MSCK REPAIR (수동)
MSCK REPAIR TABLE orders;
-- 새 파티션 자동 감지, 대량 파티션 시 느림
-- 방법 2: ALTER TABLE (명시적)
ALTER TABLE orders ADD PARTITION (year='2024', month='01', day='15')
LOCATION 's3://bucket/orders/year=2024/month=01/day=15/';
-- 방법 3: Partition Projection (권장) ⭐
CREATE EXTERNAL TABLE orders (
order_id BIGINT,
amount DECIMAL(10,2)
)
PARTITIONED BY (
year STRING,
month STRING,
day STRING
)
STORED AS PARQUET
LOCATION 's3://bucket/orders/'
TBLPROPERTIES (
'projection.enabled' = 'true',
'projection.year.type' = 'integer',
'projection.year.range' = '2020,2030',
'projection.month.type' = 'integer',
'projection.month.range' = '1,12',
'projection.month.digits' = '2',
'projection.day.type' = 'integer',
'projection.day.range' = '1,31',
'projection.day.digits' = '2',
'storage.location.template' =
's3://bucket/orders/year=${year}/month=${month}/day=${day}/'
);
-- 메타스토어 조회 없이 파티션 경로 계산 → 빠름!4. S3 스토리지 클래스 및 수명 주기
S3는 다양한 스토리지 클래스를 제공하여 접근 빈도와 비용을 최적화할 수 있습니다. 수명 주기 정책으로 자동 전환을 설정하면 비용을 크게 절감할 수 있습니다.
4.1 S3 스토리지 클래스
| 클래스 | 저장 비용 | 검색 비용 | 최소 보관 | 사용 사례 |
|---|---|---|---|---|
| Standard | $0.023/GB | 없음 | 없음 | 자주 접근하는 데이터 |
| Intelligent-Tiering | 자동 최적화 | 없음 | 없음 | 접근 패턴 불확실 |
| Standard-IA | $0.0125/GB | $0.01/GB | 30일 | 월 1회 접근 |
| One Zone-IA | $0.01/GB | $0.01/GB | 30일 | 재생성 가능 데이터 |
| Glacier IR | $0.004/GB | $0.03/GB | 90일 | 분기별 접근 |
| Glacier Flexible | $0.0036/GB | $0.03/GB | 90일 | 연 1-2회 접근 |
| Glacier Deep Archive | $0.00099/GB | $0.02/GB | 180일 | 규정 준수 아카이브 |
* 가격은 us-east-1 기준, 실제 가격은 리전별로 다름
4.2 Data Lake 계층별 권장 클래스
┌─────────────────────────────────────────────────────────────────────┐
│ Data Lake 스토리지 전략 │
│ │
│ Analytics Zone (자주 접근) │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ S3 Standard ││
│ │ • 일일 대시보드 데이터 ││
│ │ • 실시간 분석 결과 ││
│ │ • ML 피처 스토어 ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │
│ Curated Zone (주기적 접근) │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ S3 Standard-IA 또는 Intelligent-Tiering ││
│ │ • 최근 3개월 데이터: Standard ││
│ │ • 3개월~1년 데이터: Standard-IA ││
│ │ • 1년 이상: Glacier IR ││
│ └─────────────────────────────────────────────────────────────────┘│
│ │
│ Raw Zone (거의 접근 안 함) │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ S3 Standard-IA → Glacier ││
│ │ • 최근 30일: Standard (재처리 대비) ││
│ │ • 30일~1년: Standard-IA ││
│ │ • 1년 이상: Glacier Flexible ││
│ │ • 7년 이상: Glacier Deep Archive (규정 준수) ││
│ └─────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────┘4.3 수명 주기 정책 설정
// Terraform - S3 Lifecycle Policy
resource "aws_s3_bucket_lifecycle_configuration" "data_lake" {
bucket = aws_s3_bucket.data_lake.id
# Raw Zone 정책
rule {
id = "raw-zone-lifecycle"
status = "Enabled"
filter {
prefix = "raw/"
}
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 365
storage_class = "GLACIER"
}
transition {
days = 2555 # 7년
storage_class = "DEEP_ARCHIVE"
}
# 10년 후 삭제 (규정에 따라 조정)
expiration {
days = 3650
}
}
# Curated Zone 정책
rule {
id = "curated-zone-lifecycle"
status = "Enabled"
filter {
prefix = "curated/"
}
transition {
days = 90
storage_class = "STANDARD_IA"
}
transition {
days = 365
storage_class = "GLACIER_IR"
}
}
# Analytics Zone - Intelligent Tiering
rule {
id = "analytics-zone-lifecycle"
status = "Enabled"
filter {
prefix = "analytics/"
}
transition {
days = 0
storage_class = "INTELLIGENT_TIERING"
}
}
# 불완전 멀티파트 업로드 정리
rule {
id = "abort-incomplete-multipart"
status = "Enabled"
abort_incomplete_multipart_upload {
days_after_initiation = 7
}
}
}4.4 비용 최적화 계산
💰 비용 절감 예시: 100TB Raw 데이터
수명 주기 없음 (모두 Standard)
- 100TB × $0.023 = $2,300/월
- 연간: $27,600
수명 주기 적용
- 10TB Standard: $230
- 30TB Standard-IA: $375
- 60TB Glacier: $216
- 합계: $821/월
- 절감: 64%
⚠️ 주의사항
- 최소 보관 기간 전 삭제 시 조기 삭제 요금 발생
- Glacier 검색 시 복원 시간 고려 (분~시간)
- 작은 파일 많으면 요청 비용이 저장 비용보다 클 수 있음
- Athena는 Glacier 직접 쿼리 불가 (복원 필요)
5. AWS Glue Data Catalog
AWS Glue Data Catalog은 데이터 레이크의 중앙 메타데이터 저장소입니다. Athena, Redshift Spectrum, EMR 등 다양한 서비스가 공유하는 스키마 정보를 관리합니다.
5.1 Data Catalog 구조
┌─────────────────────────────────────────────────────────────────────┐
│ AWS Glue Data Catalog │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐│
│ │ Catalog (Account Level) ││
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────────┐││
│ │ │ Database: raw_db │││
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │││
│ │ │ │Table: orders│ │Table: users │ │Table: logs │ │││
│ │ │ │ │ │ │ │ │ │││
│ │ │ │• Columns │ │• Columns │ │• Columns │ │││
│ │ │ │• Partitions │ │• Partitions │ │• Partitions │ │││
│ │ │ │• Location │ │• Location │ │• Location │ │││
│ │ │ │• Format │ │• Format │ │• Format │ │││
│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │││
│ │ └─────────────────────────────────────────────────────────────┘││
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────────┐││
│ │ │ Database: curated_db │││
│ │ │ ┌─────────────┐ ┌─────────────┐ │││
│ │ │ │Table: orders│ │Table: users │ (정제된 스키마) │││
│ │ │ └─────────────┘ └─────────────┘ │││
│ │ └─────────────────────────────────────────────────────────────┘││
│ │ ││
│ │ ┌─────────────────────────────────────────────────────────────┐││
│ │ │ Database: analytics_db │││
│ │ │ ┌─────────────┐ ┌─────────────┐ │││
│ │ │ │Table: daily │ │Table: kpi │ (집계 테이블) │││
│ │ │ │_sales │ │_summary │ │││
│ │ │ └─────────────┘ └─────────────┘ │││
│ │ └─────────────────────────────────────────────────────────────┘││
│ └─────────────────────────────────────────────────────────────────┘│
│ │
│ 연동 서비스: Athena, Redshift Spectrum, EMR, Glue ETL │
└─────────────────────────────────────────────────────────────────────┘5.2 테이블 생성 방법
방법 1: Glue Crawler (자동 스키마 추론)
resource "aws_glue_crawler" "orders" {
name = "orders-crawler"
database_name = aws_glue_catalog_database.raw.name
role = aws_iam_role.glue.arn
s3_target {
path = "s3://data-lake-raw/orders/"
}
schema_change_policy {
delete_behavior = "LOG"
update_behavior = "UPDATE_IN_DATABASE"
}
schedule = "cron(0 1 * * ? *)" # 매일 새벽 1시
}S3 데이터를 스캔하여 자동으로 스키마 추론. 새 파티션 자동 등록.
방법 2: DDL (명시적 스키마 정의)
-- Athena에서 실행
CREATE EXTERNAL TABLE curated_db.orders (
order_id BIGINT,
customer_id BIGINT,
amount DECIMAL(10,2),
status STRING,
created_at TIMESTAMP
)
PARTITIONED BY (
year STRING,
month STRING,
day STRING
)
STORED AS PARQUET
LOCATION 's3://data-lake-curated/orders/'
TBLPROPERTIES (
'parquet.compression' = 'SNAPPY',
'projection.enabled' = 'true',
'projection.year.type' = 'integer',
'projection.year.range' = '2020,2030',
'projection.month.type' = 'integer',
'projection.month.range' = '1,12',
'projection.month.digits' = '2',
'projection.day.type' = 'integer',
'projection.day.range' = '1,31',
'projection.day.digits' = '2',
'storage.location.template' =
's3://data-lake-curated/orders/year=${year}/month=${month}/day=${day}/'
);스키마를 명시적으로 정의. Partition Projection으로 메타스토어 조회 최소화.
방법 3: Terraform (IaC)
resource "aws_glue_catalog_table" "orders" {
name = "orders"
database_name = aws_glue_catalog_database.curated.name
table_type = "EXTERNAL_TABLE"
parameters = {
"classification" = "parquet"
"projection.enabled" = "true"
"projection.year.type" = "integer"
"projection.year.range" = "2020,2030"
}
storage_descriptor {
location = "s3://data-lake-curated/orders/"
input_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetInputFormat"
output_format = "org.apache.hadoop.hive.ql.io.parquet.MapredParquetOutputFormat"
ser_de_info {
serialization_library = "org.apache.hadoop.hive.ql.io.parquet.serde.ParquetHiveSerDe"
}
columns {
name = "order_id"
type = "bigint"
}
columns {
name = "amount"
type = "decimal(10,2)"
}
}
partition_keys {
name = "year"
type = "string"
}
partition_keys {
name = "month"
type = "string"
}
}5.3 스키마 진화 관리
✅ 안전한 스키마 변경
- • 새 컬럼 추가 (끝에)
- • 컬럼 타입 확장 (INT → BIGINT)
- • 새 파티션 키 추가
❌ 위험한 스키마 변경
- • 컬럼 삭제
- • 컬럼 순서 변경
- • 타입 축소 (BIGINT → INT)
- • 컬럼 이름 변경
// 안전한 스키마 진화 예시
-- 새 컬럼 추가 (Parquet은 컬럼 이름으로 매핑)
ALTER TABLE orders ADD COLUMNS (
discount DECIMAL(10,2),
shipping_address STRING
);
-- 기존 데이터: discount, shipping_address는 NULL로 읽힘
-- 새 데이터: 새 컬럼 값 포함
-- Glue Crawler 스키마 업데이트 정책
schema_change_policy {
delete_behavior = "LOG" # 컬럼 삭제 감지 시 로그만
update_behavior = "UPDATE_IN_DATABASE" # 새 컬럼 자동 추가
}5.4 Data Catalog 검색
AWS Glue Data Catalog Search
테이블, 컬럼, 파티션을 검색하여 데이터 자산을 발견할 수 있습니다.
// AWS CLI - Data Catalog 검색
# 테이블 검색
aws glue search-tables \
--search-text "orders" \
--max-results 10
# 특정 데이터베이스의 테이블 목록
aws glue get-tables \
--database-name curated_db
# 테이블 상세 정보
aws glue get-table \
--database-name curated_db \
--name orders
# 파티션 목록
aws glue get-partitions \
--database-name curated_db \
--table-name orders \
--max-results 1006. 정리 및 다음 세션 예고
6.1 핵심 요약
3-Layer Architecture
Raw → Curated → Analytics 계층으로 데이터 품질과 접근성을 단계적으로 향상. 각 계층별로 스토리지 클래스와 보존 정책을 다르게 적용.
Parquet 포맷
AWS 데이터 레이크의 사실상 표준. 컬럼형 저장으로 분석 쿼리 최적화. Snappy 압축, 128MB~512MB 파일 크기 권장.
파티셔닝
쿼리 패턴에 맞는 파티션 키 선택이 핵심. 날짜 기반이 가장 일반적. Partition Projection으로 메타스토어 조회 오버헤드 제거.
스토리지 클래스
접근 빈도에 따라 Standard → IA → Glacier 전환. 수명 주기 정책으로 자동화하여 60% 이상 비용 절감 가능.
Glue Data Catalog
중앙 메타데이터 저장소. Athena, Redshift Spectrum, EMR이 공유. Crawler로 자동 스키마 추론 또는 DDL로 명시적 정의.
6.2 Best Practices 체크리스트
📁 버킷 설계
- ☐ 계층별 버킷/접두사 분리
- ☐ 일관된 네이밍 컨벤션
- ☐ Hive 스타일 파티션 경로
📄 파일 포맷
- ☐ Curated/Analytics는 Parquet
- ☐ 파일 크기 128MB~512MB
- ☐ Snappy 또는 ZSTD 압축
🗂️ 파티셔닝
- ☐ 쿼리 패턴 기반 파티션 키
- ☐ 파티션 수 100만 이하
- ☐ Partition Projection 활용
💰 비용 최적화
- ☐ 수명 주기 정책 설정
- ☐ Intelligent-Tiering 검토
- ☐ 불완전 업로드 정리
6.3 다음 세션 예고
Session 4: 배치 처리 - AWS Glue & EMR
대용량 데이터 변환과 ETL 파이프라인 구축 방법을 학습합니다.
- AWS Glue ETL 심층 분석 (Spark, Python Shell)
- Glue Job Bookmark와 증분 처리
- Amazon EMR 아키텍처 및 클러스터 설계
- EMR Serverless vs EMR on EC2
- Step Functions를 활용한 워크플로우 오케스트레이션
6.4 실습 과제
과제 1: Data Lake 버킷 설계
3-Layer 구조의 S3 버킷을 생성하고, 각 계층에 맞는 수명 주기 정책을 설정하세요. Terraform 또는 CloudFormation으로 IaC 구현.
과제 2: Parquet 변환 파이프라인
JSON 샘플 데이터를 Parquet으로 변환하는 Glue Job을 작성하세요. 날짜 기반 파티셔닝을 적용하고 Athena에서 쿼리 성능을 비교하세요.
과제 3: 비용 분석
1TB 데이터에 대해 스토리지 클래스별 월간 비용을 계산하세요. 수명 주기 정책 적용 전후의 연간 비용 차이를 분석하세요.