Post

[Snowflake 101] Day 5: NoSQL은 필요 없다, 반정형 데이터와 Variant

[Snowflake 101] Day 5: NoSQL은 필요 없다, 반정형 데이터와 Variant

현대 데이터 엔지니어링에서 가장 골치 아픈 존재는 JSON, XML, Avro 같은 반정형 데이터(Semi-structured Data)다.

애플리케이션 로그, 모바일 이벤트, 외부 API 응답은 대부분 JSON 형태다. 과거에는 이를 분석하기 위해 두 가지 방법 중 하나를 택했다.

  1. Hadoop이나 MongoDB 같은 별도의 NoSQL 저장소를 구축한다. (시스템 복잡도 증가)
  2. ETL 과정에서 파서(Parser)를 짜서 RDBMS 테이블 스키마에 맞춰 억지로 펴 넣는다. (스키마 변경 시 대응 불가)

Snowflake는 VARIANT라는 데이터 타입을 통해 이 문제를 해결한다. JSON을 그대로 로드하고, 표준 SQL로 조회한다. 별도의 NoSQL DB는 필요 없다.

1. Schema-on-Read: 일단 넣고 나중에 정의한다

Snowflake에서는 테이블 스키마를 미리 정의할 필요 없이 JSON 데이터를 통째로 넣을 수 있다. 이를 Schema-on-Read 방식이라고 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- 테이블 생성 (단 하나의 컬럼만 있으면 된다)
CREATE TABLE raw_events (
    src VARIANT
);

-- JSON 데이터 적재 예시
INSERT INTO raw_events 
SELECT PARSE_JSON('{
    "event_type": "click",
    "user": {
        "id": 101,
        "name": "Jane",
        "preferences": ["dark_mode", "beta_tester"]
    },
    "timestamp": 1704067200
}');

VARIANT 타입은 단순한 텍스트 저장(String)이 아니다. Snowflake는 내부적으로 JSON의 구조를 파악하여, 데이터를 압축하고 바이너리 형태로 최적화해 저장한다.

2. 직관적인 JSON 쿼리

저장된 JSON 데이터를 조회할 때는 콜론(:) 표기법을 사용한다. 계층 구조가 깊어도 점(.)을 찍어서 접근할 수 있다.

1
2
3
4
5
6
7
8
SELECT 
    src:event_type::string           AS event_type,
    src:user.id::int                 AS user_id,
    src:user.name::string            AS user_name,
    src:timestamp::timestamp         AS event_time
FROM raw_events
WHERE src:event_type = 'click';

  • 경로 탐색: 컬럼명:키이름.하위키 형태로 접근한다.
  • 타입 캐스팅: ::타입 (예: ::string, ::int)을 붙여 SQL 데이터 타입으로 변환한다. 이를 붙이지 않으면 따옴표가 포함된 JSON 값이 반환된다.

3. 배열(Array) 다루기: FLATTEN

JSON 안에 배열([])이 있는 경우, 이를 여러 개의 행(Row)으로 펼쳐야 할 때가 있다. 이때 LATERAL FLATTEN 구문을 사용한다.

위 예시 데이터에서 preferences 배열을 펼쳐보자.

1
2
3
4
5
6
SELECT 
    src:user.name::string AS user_name,
    value::string         AS preference
FROM raw_events,
LATERAL FLATTEN(input => src:user.preferences);

결과:

user_namepreference
Janedark_mode
Janebeta_tester

Join 문법 없이 쉼표(,)와 LATERAL FLATTEN만으로 비정규화된 데이터를 정규화된 테이블 형태로 변환할 수 있다.

4. 성능의 비밀: 자동 컬럼화

“JSON을 쿼리하면 풀 스캔(Full Scan) 때문에 느리지 않나?”

Snowflake는 영리하다. VARIANT 컬럼에 데이터가 들어오면, 백그라운드 프로세스가 자주 사용되는 경로(Key)를 감지하여 내부적으로 별도의 컬럼처럼 추출해 저장한다.

사용자가 src:user.id를 조회할 때, Snowflake는 JSON 전체를 뒤지는 것이 아니라 내부적으로 추출된 user.id 컬럼 데이터만 읽는다. 덕분에 정형 데이터 테이블을 조회하는 것과 거의 동일한 성능을 낸다.

Series Wrap-up

5일간 Snowflake의 핵심 기능을 훑어보았다.

1
2
3
4
5
6
7
8
9
Day 1 (Architecture): Storage와 Compute의 분리로 무한한 확장성을 얻었다.

Day 2 (Warehouse): 필요할 때 켜고 끄며 비용과 성능을 제어한다.

Day 3 (Time Travel): 백업 없이도 과거 데이터를 즉시 조회하고 복구한다.

Day 4 (Cloning): 운영 데이터를 비용 없이 복제해 개발 환경을 만든다.

Day 5 (Variant): 정형 데이터와 반정형 데이터를 한 곳에서 처리한다.

Snowflake는 단순한 데이터 웨어하우스가 아니다. Data Lake의 유연성과 Data Warehouse의 관리 용이성을 합친 ‘Data Cloud’다. 이 도구를 얼마나 잘 활용하느냐에 따라 데이터 팀의 생산성은 극적으로 달라질 수 있다.

This post is licensed under CC BY 4.0 by the author.