데이터 거버넌스 (Horizon, 마스킹, 행 접근 정책, 태그, 분류)
Snowflake Horizon 의 거버넌스 기능인 동적 데이터 마스킹, 행 접근 정책, 객체 태그와 태그 기반 마스킹, 민감 데이터 자동 분류, 집계와 프로젝션 정책, 접근 기록과 계보, Trust Center 를 예제와 함께 정리하고 정책 설계 실수를 짚습니다.
Horizon 이 묶는 것
Snowflake Horizon 은 거버넌스, 보안, 발견(카탈로그), 상호 운용 기능을 묶어 부르는 이름입니다. 이 문서는 그중 "누가 어떤 데이터를 어떻게 보나" 를 다루는 정책 기능을 정리합니다. 대부분의 정책 기능은 Enterprise 에디션 이상이 필요합니다.
| 기능 | 질문 | 단위 |
|---|---|---|
| 마스킹 정책 | 이 열의 값을 그대로 보여 줄까, 가릴까 | 열 |
| 행 접근 정책 | 이 행을 보여 줄까 | 행 |
| 태그 | 이 객체, 열은 어떤 성격인가 | 객체, 열 |
| 분류 | 이 열에 개인정보가 있나 | 열 (자동) |
| 집계, 프로젝션 정책 | 개별 행이나 열 값을 꺼내지 못하게 | 테이블, 열 |
| 접근 기록, 계보 | 누가 무엇을 읽고 어디로 흘렀나 | 감사 |
동적 데이터 마스킹
마스킹 정책은 쿼리 시점에 역할이나 조건에 따라 값을 바꿉니다. 원본 데이터는 그대로입니다.
CREATE OR REPLACE MASKING POLICY gov.policies.mask_email
AS (val STRING) RETURNS STRING ->
CASE
WHEN IS_ROLE_IN_SESSION('PII_READER') THEN val
WHEN val IS NULL THEN NULL
ELSE REGEXP_REPLACE(val, '^[^@]+', '****')
END;
ALTER TABLE crm.public.customers
MODIFY COLUMN email SET MASKING POLICY gov.policies.mask_email;CURRENT_ROLE()보다IS_ROLE_IN_SESSION()을 씁니다. 역할 계층과 보조 역할을 반영하기 때문입니다.- 조건부 마스킹은 다른 열 값을 인자로 받아 판단합니다 (예: 동의 여부 열이 TRUE 일 때만 보여 주기).
- 정책은 별도 거버넌스 스키마에 모아 두고, 정책 소유와 적용 권한(
APPLY MASKING POLICY)을 거버넌스 역할에만 줍니다.
행 접근 정책
CREATE OR REPLACE ROW ACCESS POLICY gov.policies.rap_region
AS (region STRING) RETURNS BOOLEAN ->
IS_ROLE_IN_SESSION('SALES_ADMIN')
OR EXISTS (
SELECT 1 FROM gov.policies.region_map m
WHERE m.role_name = CURRENT_ROLE() AND m.region = region
);
ALTER TABLE sales.public.orders ADD ROW ACCESS POLICY gov.policies.rap_region ON (region);- 매핑 테이블 방식은 사람이나 역할이 늘어도 정책을 고치지 않고 표만 고치면 됩니다.
- 정책 본문의 하위 쿼리는 모든 쿼리에 붙습니다. 매핑 테이블은 작게 유지하고, 조인 키를 단순하게 둡니다.
- 테이블 하나에는 행 접근 정책을 하나만 걸 수 있습니다.
태그와 태그 기반 마스킹
CREATE TAG gov.tags.pii ALLOWED_VALUES 'email', 'phone', 'name', 'rrn';
ALTER TABLE crm.public.customers MODIFY COLUMN email SET TAG gov.tags.pii = 'email';
-- 태그에 마스킹 정책을 붙이면 그 태그가 달린 모든 열에 자동 적용
ALTER TAG gov.tags.pii SET MASKING POLICY gov.policies.mask_email;- 태그는 테이블, 스키마, 데이터베이스, 웨어하우스 등에도 붙고 하위 객체로 상속됩니다. 비용 배분 태그(부서, 프로젝트)도 같은 기능입니다.
- 태그 기반 마스킹을 쓰면 새 열이 생겨도 태그만 달면 정책이 따라갑니다. 열마다 정책을 거는 방식보다 관리가 훨씬 쉽습니다.
민감 데이터 분류
Snowflake 는 열의 값과 이름을 보고 개인정보 의미 범주(이름, 이메일, 전화, 주소, 국가별 식별 번호 등) 를 추론해 시스템 태그(SNOWFLAKE.CORE.SEMANTIC_CATEGORY, PRIVACY_CATEGORY)를 달 수 있습니다.
- 분류 프로필을 데이터베이스나 스키마에 걸면 새 테이블을 자동으로 분류하고, 결과에 따라 사용자 태그를 달게 할 수 있습니다.
- 사용자 정의 분류기로 회사 고유의 식별자(사번, 회원 번호)를 정규식으로 잡을 수 있습니다.
- 분류는 추론이므로 결과를 사람이 검토하는 절차를 둡니다.
집계와 프로젝션 정책
- 집계 정책(aggregation policy): 쿼리가 최소 그룹 크기 이상으로 집계할 때만 결과를 돌려줍니다. 개별 고객 행을 꺼내지 못하게 합니다.
- 프로젝션 정책(projection policy): 열을
SELECT결과에 그대로 내보내지는 못하고 조인이나 조건에만 쓰게 합니다. - 두 정책은 Data Clean Rooms 와 외부 공유에서 특히 많이 씁니다.
데이터 이동 정책
2026년 8월 19일 GA 가 된 데이터 이동 정책(data movement policies)은 정해진 데이터가 허용되지 않은 곳(다른 계정, 공유, 복제 대상 등)으로 흘러가는 것을 막는 정책입니다. 적용 범위와 문법은 공식 문서를 기준으로 확인합니다.
감사: 접근 기록과 계보
-- 최근 7일 동안 email 열을 읽은 사용자
SELECT ah.user_name, ah.query_start_time, f.value:"objectName"::STRING AS obj
FROM snowflake.account_usage.access_history ah,
LATERAL FLATTEN(ah.base_objects_accessed) f,
LATERAL FLATTEN(f.value:"columns") c
WHERE c.value:"columnName"::STRING = 'EMAIL'
AND ah.query_start_time >= DATEADD(day, -7, CURRENT_TIMESTAMP());
-- 정책이 걸린 곳
SELECT * FROM TABLE(INFORMATION_SCHEMA.POLICY_REFERENCES(
POLICY_NAME => 'gov.policies.mask_email'));ACCESS_HISTORY는 읽은 열과 쓴 열을 기록해 열 수준 계보를 만듭니다 (Enterprise 이상).- Snowsight 의 계보(Lineage) 화면에서 테이블과 열이 어디서 왔고 어디로 가는지 그래프로 봅니다.
Trust Center
Trust Center 는 계정 보안 설정을 스캐너 패키지로 점검해 위반 항목과 권고를 보여 줍니다. CIS 벤치마크 기반 점검, MFA 미등록 사용자, 과도한 ACCOUNTADMIN, 네트워크 정책 부재 같은 항목을 정기적으로 잡아 줍니다. 결과는 알림으로 받을 수 있습니다.
정책 설계 실수
| 실수 | 결과 | 바로잡기 |
|---|---|---|
정책에 CURRENT_ROLE() = 'X' 하드코딩 | 상속 역할이 막힘 | IS_ROLE_IN_SESSION 과 매핑 테이블 |
| 마스킹 정책 안에서 무거운 UDF 호출 | 모든 쿼리가 느려짐 | 단순 식으로, 무거운 것은 사전 계산 |
| 공유 데이터에 역할 기반 정책 | 소비자 계정에서 기대와 다르게 동작 | CURRENT_ACCOUNT() 나 데이터베이스 역할 기준 |
| 정책 소유자가 개인 | 퇴사 시 관리 불가 | 거버넌스 역할이 소유 |
트러블슈팅
증상: 마스킹이 걸린 열을 조건에 쓰면 결과가 비어 나옵니다.
원인: 조건 비교가 마스킹된 값(
****@x.com)으로 이루어집니다.해결: 그 사용자에게 마스킹 해제가 필요한지 다시 판단하고, 조인만 필요하다면 해시 값을 내보내는 마스킹이나 프로젝션 정책을 검토합니다.
증상: 행 접근 정책을 건 뒤 대시보드가 크게 느려졌습니다.
원인: 정책의 매핑 테이블 하위 쿼리가 크거나 조건이 복잡해 모든 쿼리에 비용이 붙습니다.
해결: Query Profile 에서 정책 하위 쿼리 비용을 보고, 매핑 테이블을 작게 정리하거나 역할 확인 조건을 앞에 둡니다.