🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Delta Lake 보안과 거버넌스 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 14. · 14 Views

Delta Lake 보안과 거버넌스 완벽 가이드

데이터 레이크의 보안을 책임지는 Delta Lake의 핵심 기능들을 살펴봅니다. 접근 제어부터 암호화, 감사 로깅까지 실무에서 바로 적용할 수 있는 보안 거버넌스 전략을 배웁니다.


목차

  1. Delta Lake 보안 개요
  2. 테이블/컬럼 수준 접근 제어
  3. Row-level Security 구현
  4. 데이터 마스킹과 암호화
  5. 감사 로깅 설정
  6. Unity Catalog 연동

1. Delta Lake 보안 개요

어느 날 김데이터 씨는 데이터 엔지니어링 팀의 긴급 회의에 참석했습니다. 보안팀에서 데이터 레이크의 개인정보 접근 이력을 추적해달라는 요청이 들어왔기 때문입니다.

"우리 데이터 레이크에 보안 기능이 있나요?"

Delta Lake 보안은 데이터 레이크의 민감한 정보를 보호하고 접근을 통제하는 종합적인 보안 체계입니다. 마치 은행의 금고처럼 여러 단계의 보안 장치를 제공합니다.

테이블 수준부터 행, 열 단위까지 세밀한 접근 제어가 가능하며, 모든 접근 기록을 추적할 수 있습니다.

다음 코드를 살펴봅시다.

from delta.tables import DeltaTable
from pyspark.sql import SparkSession

# Delta Lake 보안 설정 초기화
spark = SparkSession.builder \
    .appName("DeltaSecurityExample") \
    .config("spark.sql.extensions", "io.delta.sql.DeltaSparkSessionExtension") \
    .config("spark.sql.catalog.spark_catalog", "org.apache.spark.sql.delta.catalog.DeltaCatalog") \
    .getOrCreate()

# 보안 정책이 적용된 테이블 생성
df = spark.createDataFrame([
    (1, "김철수", "010-1234-5678", "VIP"),
    (2, "이영희", "010-2345-6789", "일반")
], ["id", "name", "phone", "grade"])

# Delta 테이블로 저장 (보안 기능 활성화)
df.write.format("delta").mode("overwrite").save("/data/customers")

김데이터 씨는 입사 6개월 차 데이터 엔지니어입니다. 그동안 데이터 파이프라인 구축에만 집중했던 그에게 보안은 낯선 영역이었습니다.

하지만 최근 개인정보보호법 강화로 인해 보안은 더 이상 선택이 아닌 필수가 되었습니다. 회의실에 모인 팀원들 앞에서 박시니어 엔지니어가 화이트보드에 그림을 그리기 시작합니다.

"데이터 레이크 보안은 크게 세 가지 축으로 이루어져 있습니다." Delta Lake 보안이란 무엇일까요? 쉽게 비유하자면, Delta Lake 보안은 마치 최첨단 은행 금고 시스템과 같습니다.

은행에서는 금고실 입장부터 금고 문 개폐, 금고 안의 개별 금고까지 여러 단계의 보안 장치가 있습니다. 또한 모든 출입 기록이 CCTV와 로그로 남습니다.

Delta Lake도 이처럼 데이터에 대한 다층 보안 체계를 제공합니다. 전통적인 데이터 레이크에서는 어떤 문제가 있었을까요?

과거에는 데이터 레이크에 저장된 파일에 접근 권한만 설정할 수 있었습니다. 파일 전체를 볼 수 있거나, 아예 볼 수 없거나 둘 중 하나였습니다.

하지만 실무에서는 더 세밀한 통제가 필요합니다. 예를 들어 마케팅 팀은 고객 이름과 등급은 볼 수 있지만 전화번호는 볼 수 없어야 합니다.

또한 누가 언제 어떤 데이터를 조회했는지 추적이 어려웠습니다. 바로 이런 문제를 해결하기 위해 Delta Lake 보안 체계가 등장했습니다.

Delta Lake는 테이블 수준 접근 제어를 제공합니다. 특정 사용자나 그룹에게만 테이블 조회 권한을 부여할 수 있습니다.

또한 컬럼 수준 접근 제어로 민감한 열은 숨길 수 있습니다. 더 나아가 행 수준 보안을 통해 사용자별로 볼 수 있는 데이터 행을 제한할 수 있습니다.

위의 코드를 살펴보겠습니다. 먼저 Delta Lake를 사용하기 위한 Spark 세션을 초기화합니다.

spark.sql.extensions 설정으로 Delta Lake의 확장 기능을 활성화합니다. 다음으로 고객 정보가 담긴 간단한 데이터프레임을 생성합니다.

이름, 전화번호, 등급 등 민감한 정보가 포함되어 있습니다. 마지막으로 이 데이터를 Delta 포맷으로 저장합니다.

실제 현업에서는 어떻게 활용할까요? 금융 회사의 고객 데이터 레이크를 예로 들어봅시다.

데이터 분석가들은 고객의 구매 패턴을 분석해야 하지만, 개인식별정보는 볼 수 없어야 합니다. Delta Lake를 사용하면 테이블은 공유하되 민감한 컬럼은 마스킹하거나 접근을 차단할 수 있습니다.

또한 모든 조회 기록이 자동으로 로그에 남아 감사 추적이 가능합니다. 보안 체계는 크게 세 가지 축으로 구성됩니다.

첫 번째는 인증과 권한 부여입니다. 누가 어떤 데이터에 접근할 수 있는지 정의합니다.

두 번째는 데이터 보호입니다. 암호화와 마스킹을 통해 데이터 자체를 보호합니다.

세 번째는 감사와 모니터링입니다. 모든 접근 기록을 추적하고 이상 징후를 탐지합니다.

박시니어 엔지니어가 덧붙입니다. "보안은 한 번 설정하고 끝나는 게 아니에요.

지속적으로 모니터링하고 개선해야 합니다." 김데이터 씨는 고개를 끄덕였습니다. 이제 데이터 레이크 보안의 큰 그림이 보이기 시작했습니다.

다음 단계는 각각의 보안 기능을 실제로 구현하는 것입니다. Delta Lake 보안을 제대로 이해하면 규정을 준수하면서도 데이터를 안전하게 활용할 수 있습니다.

여러분도 오늘 배운 내용을 실제 프로젝트에 적용해 보세요.

실전 팁

💡 - 보안은 초기 설계 단계부터 고려해야 합니다. 나중에 추가하면 비용이 몇 배로 증가합니다.

  • Unity Catalog와 함께 사용하면 보안 관리가 훨씬 쉬워집니다.
  • 정기적인 보안 감사를 통해 취약점을 조기에 발견하세요.

2. 테이블/컬럼 수준 접근 제어

회의가 끝난 후 김데이터 씨는 첫 번째 과제를 받았습니다. 마케팅 팀에게는 고객 테이블의 이름과 등급만 보여주고, 전화번호는 숨겨야 한다는 요구사항이었습니다.

"어떻게 하면 같은 테이블에서 팀마다 다른 컬럼을 보여줄 수 있을까요?"

테이블/컬럼 수준 접근 제어는 사용자나 그룹별로 테이블과 특정 컬럼에 대한 접근 권한을 세밀하게 설정하는 기능입니다. 마치 도서관에서 일반 회원과 특별 회원이 접근할 수 있는 서고가 다른 것처럼, 데이터에도 등급별 접근 권한을 부여합니다.

이를 통해 민감한 정보를 보호하면서도 필요한 데이터는 공유할 수 있습니다.

다음 코드를 살펴봅시다.

# 테이블 수준 권한 설정 (SQL)
spark.sql("""
    GRANT SELECT ON TABLE delta.`/data/customers`
    TO `marketing_team`
""")

# 컬럼 수준 권한 설정 - 특정 컬럼만 접근 허용
spark.sql("""
    GRANT SELECT (id, name, grade)
    ON TABLE delta.`/data/customers`
    TO `marketing_team`
""")

# 민감한 컬럼 접근 권한은 데이터팀만
spark.sql("""
    GRANT SELECT (phone)
    ON TABLE delta.`/data/customers`
    TO `data_team`
""")

# 권한 확인
spark.sql("SHOW GRANT ON TABLE delta.`/data/customers`").show()

김데이터 씨는 책상으로 돌아와 노트북을 열었습니다. "같은 테이블인데 사용자마다 다른 컬럼을 보여준다고?" 처음에는 이해가 되지 않았습니다.

하지만 곰곰이 생각해보니 매우 합리적인 요구사항이었습니다. 옆자리의 최주니어 엔지니어가 말했습니다.

"예전에는 테이블을 여러 개 복사해서 팀마다 다른 테이블을 줬어요. 그런데 관리가 너무 힘들었죠." 테이블/컬럼 수준 접근 제어가 정확히 무엇일까요?

쉽게 비유하자면, 이것은 마치 회사의 문서 보관함과 같습니다. 같은 보관함이지만 사원증 등급에 따라 볼 수 있는 서랍이 다릅니다.

일반 직원은 1층 서랍만, 팀장은 1-2층, 임원은 모든 서랍을 열 수 있습니다. Delta Lake의 접근 제어도 이처럼 동일한 데이터 소스에서 권한에 따라 다른 정보를 제공합니다.

전통적인 방식의 문제점은 무엇이었을까요? 과거에는 부서별로 필요한 컬럼만 담긴 별도 테이블을 만들었습니다.

마케팅용 고객 테이블, 분석용 고객 테이블, 운영용 고객 테이블이 따로 존재했습니다. 문제는 원본 데이터가 업데이트될 때마다 모든 복사본을 갱신해야 한다는 점이었습니다.

데이터 불일치가 자주 발생했고, 저장 공간도 낭비되었습니다. 접근 제어를 사용하면 이런 문제가 해결됩니다.

하나의 마스터 테이블만 유지하면 됩니다. 각 사용자가 조회할 때 권한에 따라 자동으로 필터링됩니다.

데이터는 항상 최신 상태를 유지하며, 저장 공간도 절약됩니다. 무엇보다 보안 정책을 중앙에서 일관되게 관리할 수 있습니다.

위의 코드를 단계별로 살펴보겠습니다. 첫 번째 GRANT SELECT 문은 마케팅 팀에게 customers 테이블에 대한 조회 권한을 부여합니다.

하지만 이것만으로는 모든 컬럼을 볼 수 있습니다. 두 번째 문이 핵심입니다.

GRANT SELECT (id, name, grade)처럼 컬럼 목록을 명시하면 해당 컬럼만 접근 가능합니다. 세 번째 문은 민감한 phone 컬럼은 데이터팀만 볼 수 있도록 제한합니다.

실제 프로젝트에서는 어떻게 활용할까요? 전자상거래 회사의 주문 데이터를 생각해봅시다.

주문 테이블에는 고객 정보, 상품 정보, 결제 정보가 모두 들어있습니다. 상품팀은 어떤 상품이 얼마나 팔렸는지만 알면 됩니다.

고객 이름이나 카드 번호는 필요 없습니다. 컬럼 수준 접근 제어를 사용하면 상품팀에게는 상품 관련 컬럼만, 재무팀에게는 결제 관련 컬럼만 보여줄 수 있습니다.

역할 기반 접근 제어(RBAC)를 적용하는 것이 베스트 프랙티스입니다. 개별 사용자가 아닌 역할이나 그룹에 권한을 부여하세요.

예를 들어 marketing_team, data_team처럼 그룹을 만들고 그룹에 권한을 할당합니다. 새로운 마케터가 입사하면 해당 그룹에 추가하기만 하면 됩니다.

일일이 개별 권한을 설정할 필요가 없습니다. 주의할 점도 있습니다.

초보자들이 흔히 하는 실수는 너무 세밀하게 권한을 나누는 것입니다. 컬럼 하나하나마다 다른 권한을 설정하면 관리가 복잡해집니다.

비즈니스 요구사항에 맞춰 적절한 수준으로 그룹화하세요. 또한 권한 변경 사항을 문서화하지 않으면 나중에 누가 왜 이런 권한을 가졌는지 알 수 없게 됩니다.

김데이터 씨는 테스트 환경에서 권한을 설정해보았습니다. 마케팅 계정으로 로그인하니 정말 phone 컬럼이 보이지 않았습니다.

"오, 신기하네요!" 박시니어가 웃으며 말했습니다. "이제 진짜 보안의 시작입니다." 접근 제어를 제대로 설정하면 데이터 유출 위험을 크게 줄일 수 있습니다.

필요한 사람에게 필요한 만큼만 권한을 부여하는 최소 권한 원칙을 따르세요.

실전 팁

💡 - 권한은 그룹 단위로 관리하세요. 개별 사용자 권한은 관리가 어렵습니다.

  • 정기적으로 권한을 검토하고 불필요한 권한은 회수하세요.
  • SHOW GRANT 명령으로 현재 권한 상태를 확인하는 습관을 들이세요.

3. Row-level Security 구현

다음 과제는 더 까다로웠습니다. 지역 매니저들은 자기가 담당하는 지역의 고객 데이터만 볼 수 있어야 했습니다.

"컬럼이 아니라 행을 필터링해야 한다고요?" 김데이터 씨는 당황했습니다.

Row-level Security는 사용자별로 볼 수 있는 데이터 행을 제한하는 보안 기능입니다. 마치 아파트 관리실에서 각 동 관리인이 자기 동의 정보만 볼 수 있는 것처럼, 데이터도 사용자의 속성에 따라 필터링됩니다.

이를 통해 같은 테이블을 공유하면서도 각자에게 관련된 데이터만 보여줄 수 있습니다.

다음 코드를 살펴봅시다.

# Row-level Security를 위한 보안 뷰 생성
spark.sql("""
    CREATE OR REPLACE VIEW customers_filtered AS
    SELECT * FROM delta.`/data/customers`
    WHERE
        -- 관리자는 모든 데이터 조회 가능
        is_member('admin_group') OR
        -- 지역 매니저는 자기 지역만 조회 가능
        (is_member('regional_manager') AND region = current_user_region()) OR
        -- 일반 사용자는 자기 데이터만 조회 가능
        (email = current_user())
""")

# Python에서 동적 필터링 구현
def get_filtered_data(user_role, user_region=None):
    if user_role == "admin":
        return spark.read.format("delta").load("/data/customers")
    elif user_role == "regional_manager":
        df = spark.read.format("delta").load("/data/customers")
        return df.filter(f"region = '{user_region}'")
    else:
        return spark.createDataFrame([], schema)

김데이터 씨는 고민에 빠졌습니다. 컬럼 수준 제어는 이해가 되었지만, 행 수준 제어는 어떻게 구현해야 할까요?

박시니어에게 도움을 청했습니다. "생각보다 간단해요.

뷰를 활용하면 됩니다." 박시니어가 화이트보드에 그림을 그리기 시작했습니다. Row-level Security란 무엇일까요?

쉽게 비유하자면, 이것은 마치 병원의 전자차트 시스템과 같습니다. 의사는 자기가 담당하는 환자의 차트만 볼 수 있습니다.

같은 차트 시스템이지만 로그인한 의사에 따라 보이는 환자 목록이 다릅니다. 시스템이 자동으로 "담당의사 = 현재 사용자"라는 조건을 추가해서 데이터를 필터링하는 것입니다.

기존 방식의 한계는 무엇이었을까요? 예전에는 지역별로 테이블을 따로 만들었습니다.

서울 고객 테이블, 부산 고객 테이블, 대구 고객 테이블이 별도로 존재했습니다. 새로운 지역이 추가될 때마다 테이블을 만들어야 했고, 전국 데이터를 분석하려면 모든 테이블을 조인해야 했습니다.

관리 포인트가 기하급수적으로 증가했습니다. Row-level Security는 이 문제를 우아하게 해결합니다.

하나의 통합 테이블만 유지하면 됩니다. 사용자가 조회할 때 자동으로 WHERE 조건이 추가됩니다.

서울 매니저가 조회하면 WHERE region = '서울'이, 부산 매니저가 조회하면 WHERE region = '부산'이 자동으로 붙습니다. 사용자는 필터가 적용된 줄도 모릅니다.

그냥 자기 권한으로 볼 수 있는 데이터가 전부라고 생각합니다. 코드를 자세히 살펴보겠습니다.

첫 번째 방법은 보안 뷰를 사용하는 것입니다. CREATE VIEW 문에서 사용자 역할을 체크합니다.

is_member('admin_group')은 현재 사용자가 관리자 그룹에 속하는지 확인합니다. 관리자라면 모든 데이터를 볼 수 있습니다.

is_member('regional_manager')는 지역 매니저인지 체크하고, current_user_region() 함수로 담당 지역을 가져와 필터링합니다. 두 번째 방법은 애플리케이션 레벨에서 필터링하는 것입니다.

Python 함수에서 사용자 역할을 파라미터로 받아 동적으로 데이터를 필터링합니다. 이 방식은 더 유연하지만 애플리케이션 코드에 보안 로직이 섞입니다.

가능하면 데이터베이스 레벨에서 처리하는 것이 안전합니다. 실무에서는 어떻게 활용할까요?

대형 유통 체인점을 운영하는 회사가 있다고 가정해봅시다. 전국에 200개 매장이 있고, 각 매장 매니저는 자기 매장의 매출 데이터만 볼 수 있어야 합니다.

Row-level Security를 적용하면 매니저들은 동일한 대시보드를 사용하지만 각자 자기 매장 데이터만 보게 됩니다. 본사 임원은 모든 매장 데이터를 볼 수 있습니다.

성능 최적화도 중요합니다. 필터 조건에 사용되는 컬럼에는 반드시 인덱스를 생성하세요.

region 컬럼으로 필터링한다면 해당 컬럼에 Z-ORDER를 적용하면 쿼리 성능이 크게 향상됩니다. Delta Lake의 데이터 스키핑 기능과 함께 사용하면 불필요한 파일 읽기를 줄일 수 있습니다.

주의할 점이 있습니다. 보안 뷰를 만들 때 실수로 필터 조건을 빠뜨리면 모든 데이터가 노출될 수 있습니다.

반드시 테스트 환경에서 충분히 검증한 후 프로덕션에 적용하세요. 또한 current_user() 같은 함수가 제대로 작동하는지 확인해야 합니다.

인증 시스템과 연동이 올바르게 되어 있어야 합니다. 김데이터 씨는 테스트 계정으로 뷰를 조회해보았습니다.

서울 매니저 계정으로는 서울 데이터만, 부산 매니저 계정으로는 부산 데이터만 보였습니다. "완벽하네요!" 박시니어가 고개를 끄덕였습니다.

"이제 거버넌스의 핵심을 이해한 거예요." Row-level Security를 잘 활용하면 복잡한 권한 관리를 단순화할 수 있습니다. 사용자 경험은 해치지 않으면서 강력한 보안을 제공합니다.

실전 팁

💡 - 필터 컬럼에 Z-ORDER를 적용하여 성능을 최적화하세요.

  • 보안 뷰의 WHERE 조건은 간단명료하게 작성하세요. 복잡하면 실수하기 쉽습니니다.
  • 정기적으로 누가 어떤 데이터를 조회하는지 모니터링하세요.

4. 데이터 마스킹과 암호화

보안 회의에서 CISO가 말했습니다. "개발 환경에서 실제 전화번호가 보이면 안 됩니다.

마스킹 처리가 필요합니다." 김데이터 씨는 또 하나의 과제를 받았습니다. "데이터를 가리는 건 또 어떻게 하죠?"

데이터 마스킹은 민감한 정보를 가려서 보여주는 기술입니다. 전화번호를 010-****-5678처럼 일부만 보여주거나, 이름을 김**로 표시하는 것입니다.

암호화는 데이터를 읽을 수 없는 형태로 변환하여 저장하고, 권한이 있는 사용자만 복호화할 수 있게 합니다. 마치 편지를 봉투에 넣어 봉인하는 것과 같습니다.

다음 코드를 살펴봅시다.

from pyspark.sql.functions import col, regexp_replace, sha2, when, lit

# 전화번호 마스킹 함수
def mask_phone(df):
    return df.withColumn(
        "phone_masked",
        # 중간 4자리를 ****로 치환
        regexp_replace(col("phone"), r"(\d{3})-(\d{4})-(\d{4})", r"$1-****-$3")
    )

# 이름 마스킹 함수
def mask_name(df):
    return df.withColumn(
        "name_masked",
        # 첫 글자만 남기고 나머지는 *
        concat(substring(col("name"), 1, 1), lit("**"))
    )

# 개인정보 암호화 (SHA-256 해시)
def encrypt_sensitive_data(df):
    return df.withColumn(
        "phone_encrypted",
        sha2(col("phone"), 256)  # 256비트 해시
    )

# 역할별 마스킹 적용
def apply_masking_by_role(df, user_role):
    if user_role == "admin":
        return df  # 관리자는 원본 데이터
    elif user_role == "analyst":
        return mask_phone(mask_name(df))  # 분석가는 마스킹된 데이터
    else:
        return encrypt_sensitive_data(df)  # 그 외는 암호화

김데이터 씨는 마스킹이라는 개념을 처음 들어봤습니다. "데이터를 가린다는 게 무슨 뜻이죠?" 최주니어가 자기 신용카드를 꺼내 보여줬습니다.

"카드 번호 보이죠? 1234---5678 이런 식으로 표시되잖아요.

이게 마스킹이에요." 박시니어가 설명을 이어갔습니다. "개발자나 분석가들은 실제 전화번호를 알 필요가 없어요.

패턴만 알면 되죠." 데이터 마스킹이란 정확히 무엇일까요? 쉽게 비유하자면, 마스킹은 마치 증명사진의 눈 가리기와 같습니다.

얼굴의 전체적인 윤곽은 볼 수 있지만 누구인지 정확히 식별할 수는 없습니다. 데이터도 마찬가지입니다.

전화번호 형식은 유지하지만 실제 번호는 알 수 없게 만듭니다. 이렇게 하면 개발과 테스트는 가능하면서도 개인정보는 보호됩니다.

암호화는 마스킹보다 더 강력합니다. 암호화는 데이터를 완전히 다른 형태로 변환합니다.

010-1234-5678a8f5f167f44f4964e6c998dee827110c처럼 의미 없는 문자열이 됩니다. 특별한 키가 없으면 원본을 절대 알 수 없습니다.

반면 마스킹은 일부는 보이기 때문에 패턴 분석이 가능합니다. 개발 환경에서 실제 데이터를 사용하면 어떤 문제가 있을까요?

과거에는 프로덕션 데이터베이스를 그대로 복사해서 개발 환경에 썼습니다. 개발자 노트북에 수백만 건의 실제 고객 전화번호와 이메일이 저장되어 있었습니다.

노트북을 분실하거나 해킹당하면 대규모 개인정보 유출 사고로 이어집니다. 실제로 많은 회사가 이런 사고를 겪었습니다.

마스킹을 사용하면 안전하게 개발할 수 있습니다. 프로덕션 데이터를 개발 환경으로 복사할 때 자동으로 마스킹을 적용합니다.

개발자는 데이터의 형식과 분포는 알 수 있지만 실제 값은 모릅니다. 애플리케이션 테스트는 정상적으로 할 수 있으면서 개인정보 유출 위험은 없습니다.

코드를 단계별로 분석해보겠습니다. mask_phone 함수는 정규표현식을 사용합니다.

(\d{3})-(\d{4})-(\d{4}) 패턴으로 전화번호를 세 그룹으로 나눕니다. 그리고 $1-****-$3로 치환하여 중간 4자리를 별표로 바꿉니다.

mask_name 함수는 이름의 첫 글자만 남기고 나머지를 별표로 처리합니다. 암호화는 해시 함수를 사용합니다.

sha2(col("phone"), 256)은 전화번호를 SHA-256 해시로 변환합니다. 이것은 단방향 암호화입니다.

해시 값에서 원본을 역산할 수 없습니다. 만약 원본으로 복구해야 한다면 AES 같은 양방향 암호화를 사용해야 합니다.

실무에서는 역할별로 다른 수준의 마스킹을 적용합니다. 예를 들어 고객센터 직원은 고객 확인을 위해 전화번호 뒷자리를 봐야 합니다.

하지만 마케팅 분석가는 전화번호를 전혀 볼 필요가 없습니다. apply_masking_by_role 함수처럼 역할에 따라 동적으로 마스킹 수준을 조정할 수 있습니다.

Delta Lake의 컬럼 마스킹 기능도 활용하세요. 최신 버전의 Delta Lake는 테이블 정의 시 마스킹 정책을 설정할 수 있습니다.

쿼리할 때마다 자동으로 마스킹이 적용됩니다. 애플리케이션 코드를 수정할 필요가 없어 관리가 편합니다.

주의할 점이 있습니다. 마스킹된 데이터로 통계 분석을 하면 결과가 왜곡될 수 있습니다.

예를 들어 전화번호 중복 체크를 하는데 중간이 모두 ****이면 제대로 검사가 안 됩니다. 분석 목적에 따라 마스킹 방법을 선택해야 합니다.

때로는 토큰화(Tokenization)가 더 적합할 수 있습니다. 김데이터 씨는 마스킹 함수를 테스트해보았습니다.

개발 환경에서 조회한 데이터는 모두 010-****-1234 형식으로 나왔습니다. "이제 안심하고 개발할 수 있겠네요!" CISO도 만족스러운 표정을 지었습니다.

데이터 마스킹과 암호화는 방어의 마지막 보루입니다. 접근 제어를 뚫더라도 데이터 자체가 보호되어 있으면 피해를 최소화할 수 있습니다.

실전 팁

💡 - 개발/스테이징 환경에는 항상 마스킹된 데이터를 사용하세요.

  • 단방향 해시와 양방향 암호화의 차이를 이해하고 목적에 맞게 선택하세요.
  • 암호화 키는 별도의 안전한 키 관리 시스템(KMS)에 보관하세요.

5. 감사 로깅 설정

어느 날 보안팀에서 긴급 요청이 들어왔습니다. "지난 주에 특정 고객 데이터에 누가 접근했는지 확인해주세요." 김데이터 씨는 당황했습니다.

"로그를 남기고 있지 않았는데요?"

감사 로깅은 데이터에 대한 모든 접근과 변경 이력을 기록하는 기능입니다. 누가, 언제, 어떤 데이터를, 무엇을 했는지 모두 추적합니다.

마치 은행의 거래 내역처럼 모든 활동이 기록되어 나중에 검토하고 분석할 수 있습니다. 이는 규정 준수와 보안 사고 대응에 필수적입니다.

다음 코드를 살펴봅시다.

from datetime import datetime
import json

# Delta Lake 감사 로그 활성화
spark.sql("""
    ALTER TABLE delta.`/data/customers`
    SET TBLPROPERTIES (
        'delta.logRetentionDuration' = '365 days',
        'delta.enableChangeDataFeed' = 'true'
    )
""")

# 접근 로그 기록 함수
def log_data_access(user_id, table_name, operation, row_count):
    log_entry = {
        "timestamp": datetime.now().isoformat(),
        "user_id": user_id,
        "table_name": table_name,
        "operation": operation,
        "row_count": row_count,
        "ip_address": get_client_ip()
    }

    # 로그를 별도 테이블에 저장
    log_df = spark.createDataFrame([log_entry])
    log_df.write.format("delta").mode("append").save("/audit/access_logs")

    return log_entry

# 데이터 조회 시 자동 로깅
def read_with_audit(table_path, user_id):
    # 데이터 읽기
    df = spark.read.format("delta").load(table_path)

    # 접근 로그 기록
    log_data_access(
        user_id=user_id,
        table_name=table_path,
        operation="SELECT",
        row_count=df.count()
    )

    return df

# 변경 이력 조회
def get_change_history(table_path, start_version, end_version):
    return spark.read.format("delta") \
        .option("readChangeFeed", "true") \
        .option("startingVersion", start_version) \
        .option("endingVersion", end_version) \
        .load(table_path)

김데이터 씨는 식은땀을 흘렸습니다. 누가 데이터에 접근했는지 기록이 없다니, 이건 큰 문제였습니다.

박시니어가 진지한 표정으로 말했습니다. "로깅은 선택이 아니라 필수예요.

GDPR, 개인정보보호법 모두 접근 이력 보관을 요구합니다." 보안 사고가 발생하면 가장 먼저 확인하는 것이 로그입니다. 감사 로깅이란 무엇일까요?

쉽게 비유하자면, 감사 로그는 마치 건물의 출입 기록과 같습니다. 누가 언제 어느 문으로 들어왔고 나갔는지 모두 기록됩니다.

CCTV 영상도 보관됩니다. 나중에 사건이 발생하면 이 기록을 분석해서 범인을 찾습니다.

데이터 로그도 마찬가지입니다. 모든 접근과 변경을 기록해두면 문제 발생 시 추적이 가능합니다.

로그가 없으면 어떤 문제가 생길까요? 데이터 유출 사고가 발생했다고 가정해봅시다.

누가 데이터를 빼갔는지 알 수 없습니다. 언제부터 유출되었는지도 모릅니다.

피해 범위를 파악할 수도 없습니다. 규제 당국에 보고할 자료도 없습니다.

회사의 신뢰도는 땅에 떨어지고 막대한 벌금을 물게 됩니다. Delta Lake의 로깅 기능은 매우 강력합니다.

먼저 트랜잭션 로그가 자동으로 기록됩니다. 모든 테이블 변경사항이 JSON 파일로 저장됩니다.

누가 언제 어떤 데이터를 추가, 수정, 삭제했는지 모두 추적할 수 있습니다. Change Data Feed를 활성화하면 변경된 행의 이전 값과 이후 값을 모두 볼 수 있습니다.

코드를 자세히 살펴보겠습니다. 첫 번째 SQL 문은 테이블 속성을 설정합니다.

delta.logRetentionDuration을 365일로 설정하면 1년간의 트랜잭션 로그가 보관됩니다. 기본값은 30일이므로 규정에 맞게 연장해야 합니다.

delta.enableChangeDataFeed는 변경 데이터 캡처를 활성화합니다. log_data_access 함수는 사용자 정의 접근 로그를 기록합니다.

누가(user_id), 언제(timestamp), 어떤 테이블에(table_name), 무엇을 했는지(operation), 얼마나 많은 행을 조회했는지(row_count) 기록합니다. IP 주소도 함께 저장하면 비정상 접근 패턴을 탐지할 수 있습니다.

이 로그는 별도의 Delta 테이블에 Append로 저장됩니다. read_with_audit 함수는 데이터 읽기와 로깅을 결합합니다.

데이터를 읽을 때마다 자동으로 로그가 기록됩니다. 애플리케이션 코드에서 이 함수를 사용하도록 강제하면 빠짐없이 로그를 남길 수 있습니다.

물론 성능 영향도 고려해야 합니다. df.count()는 비용이 큰 연산이므로 샘플링이나 메타데이터 사용을 검토하세요.

실무에서는 로그 분석이 중요합니다. 단순히 로그를 쌓기만 해서는 의미가 없습니다.

정기적으로 분석해야 합니다. 예를 들어 평소에 월 10건 조회하던 사용자가 갑자기 10만 건을 조회하면 의심스럽습니다.

새벽 2시에 접근하는 것도 비정상입니다. 이런 이상 징후를 자동으로 탐지하는 모니터링 시스템을 구축하세요.

변경 이력 조회 기능도 매우 유용합니다. get_change_history 함수는 특정 버전 범위의 변경사항을 조회합니다.

"지난주 화요일에 이 고객 데이터가 어떻게 바뀌었나요?"라는 질문에 정확히 답할 수 있습니다. 실수로 데이터를 삭제했을 때 복구도 가능합니다.

주의할 점이 있습니다. 로그를 너무 오래 보관하면 저장 비용이 증가합니다.

규정에서 요구하는 최소 기간만 보관하고, 오래된 로그는 저렴한 콜드 스토리지로 이동하세요. 또한 로그 자체도 민감한 정보입니다.

누가 어떤 환자 정보를 조회했는지가 담긴 로그는 그 자체로 개인정보입니다. 로그에도 접근 제어를 적용해야 합니다.

김데이터 씨는 즉시 로깅 기능을 구현했습니다. 다음 날 보안팀의 요청이 왔을 때, 깔끔한 접근 이력 보고서를 제출할 수 있었습니다.

"완벽합니다!" 보안팀장이 엄지를 치켜세웠습니다. 감사 로깅은 사후 대응뿐만 아니라 예방에도 효과적입니다.

모든 행동이 기록된다는 것을 알면 내부자의 악의적 접근도 줄어듭니다.

실전 팁

💡 - 로그 보관 기간은 규정(GDPR: 6년, 국내: 3년)을 확인하고 설정하세요.

  • 로그는 변경 불가능한(immutable) 스토리지에 저장하여 위변조를 방지하세요.
  • 이상 징후 자동 탐지 알림을 설정하여 실시간으로 대응하세요.

6. Unity Catalog 연동

회사가 성장하면서 데이터 자산이 늘어났습니다. 수십 개의 Delta 테이블, 수백 개의 컬럼, 수많은 사용자 권한을 관리하는 것이 한계에 다다랐습니다.

박시니어가 말했습니다. "이제 Unity Catalog를 도입할 때가 됐어요."

Unity Catalog는 Databricks의 통합 거버넌스 솔루션입니다. 데이터, 모델, 노트북 등 모든 자산을 한 곳에서 관리하고, 세밀한 접근 제어를 중앙에서 일관되게 적용합니다.

마치 도서관의 통합 관리 시스템처럼, 모든 자산의 카탈로그를 만들고 누가 무엇을 볼 수 있는지 체계적으로 관리합니다.

다음 코드를 살펴봅시다.

# Unity Catalog에 카탈로그 생성
spark.sql("""
    CREATE CATALOG IF NOT EXISTS production
    COMMENT '프로덕션 데이터 카탈로그'
""")

# 스키마(데이터베이스) 생성
spark.sql("""
    CREATE SCHEMA IF NOT EXISTS production.customer_data
    COMMENT '고객 데이터 스키마'
""")

# Unity Catalog 관리 테이블 생성
spark.sql("""
    CREATE TABLE IF NOT EXISTS production.customer_data.customers (
        id BIGINT,
        name STRING,
        email STRING,
        phone STRING COMMENT '전화번호 - PII',
        created_at TIMESTAMP
    )
    USING DELTA
    LOCATION '/data/customers'
    COMMENT '고객 마스터 테이블'
""")

# 카탈로그 수준 권한 부여
spark.sql("""
    GRANT USE CATALOG ON CATALOG production
    TO `data_team`
""")

# 스키마 수준 권한 부여
spark.sql("""
    GRANT USE SCHEMA, SELECT ON SCHEMA production.customer_data
    TO `analyst_team`
""")

# 테이블 수준 세밀한 권한 설정
spark.sql("""
    GRANT SELECT ON TABLE production.customer_data.customers
    TO `marketing_team`
""")

# 컬럼 태그 설정 (PII 식별)
spark.sql("""
    ALTER TABLE production.customer_data.customers
    ALTER COLUMN phone SET TAGS ('pii' = 'phone_number')
""")

# 데이터 계보(Lineage) 조회
lineage = spark.sql("""
    SELECT * FROM system.access.table_lineage
    WHERE table_name = 'production.customer_data.customers'
""")

김데이터 씨는 Unity Catalog라는 용어를 들어본 적이 있었지만 정확히 무엇인지 몰랐습니다. "카탈로그가 뭔가요?

쇼핑몰 카탈로그요?" 최주니어가 웃으며 설명했습니다. "비슷해요.

모든 데이터 자산의 목록이에요." 박시니어가 화이트보드에 계층 구조를 그렸습니다. 카탈로그 → 스키마 → 테이블의 3단계 네임스페이스입니다.

Unity Catalog란 정확히 무엇일까요? 쉽게 비유하자면, Unity Catalog는 마치 대학 도서관의 통합 관리 시스템과 같습니다.

도서관에는 수백만 권의 책이 있고, 각 책은 분류 체계에 따라 정리됩니다. 누가 어떤 책을 빌릴 수 있는지, 연체료는 얼마인지 모두 시스템에서 관리됩니다.

Unity Catalog도 모든 데이터 자산을 체계적으로 분류하고, 접근 권한을 중앙에서 관리합니다. 기존 방식의 한계는 무엇이었을까요?

예전에는 각 데이터베이스나 스토리지마다 별도의 권한 시스템이 있었습니다. Delta Lake는 Delta Lake대로, S3는 S3대로, Redshift는 Redshift대로 권한을 설정해야 했습니다.

한 사용자에게 권한을 주려면 여러 시스템을 돌아다니며 설정해야 했습니다. 일관성을 유지하기 어렵고, 실수도 잦았습니다.

Unity Catalog는 모든 것을 통합합니다. 하나의 중앙 카탈로그에서 모든 데이터 자산을 관리합니다.

권한도 한 곳에서 설정하면 모든 곳에 적용됩니다. 데이터 엔지니어, 데이터 사이언티스트, 분석가 모두 동일한 카탈로그를 봅니다.

혼란이 사라지고 협업이 쉬워집니다. 코드를 단계별로 살펴보겠습니다.

먼저 카탈로그를 생성합니다. 카탈로그는 최상위 네임스페이스입니다.

보통 환경별로 나눕니다. production, staging, development 같은 식입니다.

다음은 스키마(데이터베이스)를 만듭니다. 스키마는 관련된 테이블들을 묶는 논리적 그룹입니다.

customer_data, sales_data 같은 도메인별로 구성합니다. 테이블 생성은 기존 Delta Lake와 유사하지만 풀 네임을 사용합니다.

production.customer_data.customers처럼 세 단계 이름으로 테이블을 참조합니다. 이렇게 하면 어느 환경의 어느 도메인 테이블인지 명확합니다.

COMMENT를 적극 활용하세요. 카탈로그에서 검색할 때 메타데이터가 중요합니다.

권한 부여는 계층적으로 이루어집니다. 먼저 카탈로그 접근 권한을 줘야 합니다.

GRANT USE CATALOG는 카탈로그를 볼 수 있는 권한입니다. 그다음 스키마 권한, 마지막으로 테이블 권한을 부여합니다.

상위 레벨 권한 없이 하위 레벨만 주면 작동하지 않습니다. 계층을 이해하는 것이 핵심입니다.

컬럼 태그는 매우 강력한 기능입니다. ALTER COLUMN SET TAGS로 컬럼에 메타데이터를 붙입니다.

pii, sensitive, financial 같은 태그를 달면 자동으로 정책을 적용할 수 있습니다. 예를 들어 pii 태그가 달린 모든 컬럼은 자동으로 마스킹되게 설정할 수 있습니다.

수백 개 테이블의 수천 개 컬럼을 일일이 관리할 필요가 없습니다. 데이터 계보(Lineage) 추적은 거버넌스의 핵심입니다.

어떤 테이블이 어느 테이블에서 파생되었는지 자동으로 추적됩니다. 상류 데이터가 변경되면 하류에 영향을 받는 모든 테이블과 대시보드를 알 수 있습니다.

"이 필드를 바꾸면 어디에 영향이 가나요?"라는 질문에 즉시 답할 수 있습니다. 실무에서는 어떻게 활용할까요?

대기업의 데이터 플랫폼을 상상해봅시다. 수백 명의 데이터 전문가가 수천 개의 테이블을 사용합니다.

Unity Catalog 없이는 관리가 불가능합니다. 카탈로그에서 phone으로 검색하면 전화번호가 포함된 모든 테이블이 나옵니다.

PII 태그로 필터링하면 개인정보가 있는 테이블만 볼 수 있습니다. 거버넌스 팀은 한눈에 보안 현황을 파악합니다.

성능과 비용도 고려해야 합니다. Unity Catalog 자체는 메타데이터만 관리하므로 쿼리 성능에 거의 영향이 없습니다.

하지만 권한 체크는 모든 쿼리마다 일어납니다. 복잡한 권한 구조는 오버헤드를 증가시킬 수 있습니다.

그룹을 적극 활용하여 권한 체크를 최소화하세요. 주의할 점이 있습니다.

Unity Catalog를 도입하면 기존 테이블을 마이그레이션해야 합니다. 테이블 경로를 Unity Catalog 형식으로 바꿔야 하고, 기존 권한도 다시 설정해야 합니다.

치밀한 계획 없이 진행하면 서비스 중단으로 이어질 수 있습니다. 스테이징 환경에서 충분히 테스트한 후 단계적으로 마이그레이션하세요.

김데이터 씨는 Unity Catalog 콘솔을 열어봤습니다. 모든 테이블이 깔끔하게 정리되어 있고, 각 컬럼의 설명과 태그가 한눈에 보였습니다.

"이제야 우리 데이터가 정리된 느낌이에요!" 박시니어가 미소 지었습니다. "거버넌스의 완성입니다." Unity Catalog는 단순한 도구가 아니라 데이터 거버넌스 문화를 만듭니다.

모든 팀원이 동일한 카탈로그를 보고, 동일한 정책을 따르며, 협업의 효율성이 극대화됩니다.

실전 팁

💡 - 카탈로그는 환경별로, 스키마는 도메인별로 구성하세요.

  • 컬럼 태그를 적극 활용하여 정책 자동화를 구현하세요.
  • 데이터 계보를 정기적으로 검토하여 불필요한 파이프라인을 제거하세요.

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Delta Lake#Security#Access Control#Data Masking#Unity Catalog#Data Engineering,Big Data,Delta Lake,Security

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.