DCM 프로젝트 파일과 템플릿

DCM 프로젝트 파일과 템플릿

DCM 프로젝트는 매니페스트 파일과 하나 이상의 SQL 객체 정의 파일이 필요해요. 이 파일들은 보통 Git 리포지토리나 로컬 워크스페이스에 저장되고 관리돼요.

출처: Snowflake User Guide - DCM project files

본문

  • 매니페스트 파일 — 템플릿 변수로 서로 다른 환경에 대한 배포 대상과 구성을 정의해요.
  • 객체 정의 파일 — DCM 프로젝트에서 함께 관리하려는 Snowflake 객체 그룹을 정의해요.

DCM 프로젝트 파일을 만드는 높은 수준의 워크플로는:

  1. 정의 파일을 저장할 DCM 프로젝트 폴더를 만들어요.
  2. 매니페스트 파일을 만들어요.
  3. 객체 정의 파일을 만들어요.

정의 파일을 저장할 DCM 프로젝트 폴더 만들기

새 DCM 프로젝트를 만들려면 매니페스트 파일(manifest.yml)과 SQL 객체 정의 파일을 저장할 폴더를 만들어요.

snow init <project_name> --template dcm_project

dcm_project 템플릿과 함께 snow init 명령은 프로젝트 디렉터리에 예제 정의 파일을 만들어요. 이 파일을 열고 편집해 DCM 프로젝트를 정의할 수 있어요. 웹에서 만들려면 탐색 메뉴에서 Projects » Workspaces, Workspaces 창에서 + Add new, DCM Project를 선택해요.

DCM Projects는 표준화된 폴더 구조를 따라요.

  • DCM Projects 객체 정의 파일은 sources/definitions/ 아래에 배치해야 해요.
  • 선택적 전역 매크로 파일은 sources/macros/ 아래에 배치할 수 있어요.
  • 이 프로젝트 디렉터리 안의 파일 명명·중첩은 유연해요.
  • Snowflake CLI와 Workspaces는 로컬 명령 출력을 out/ 아래에 저장해요. SQL 명령은 인터페이스별 출력 위치를 사용할 수 있어요.
  • DCM 명령과 함께 사용하려는 추가 스크립트가 있다면 sources 아래에 추가할 수 있어요.
  • DCM Projects 명령이 사용하지 않고 로컬에서 업로드되지 않길 원하는 다른 커스텀 스크립트를 프로젝트 폴더 안에 저장하려면 sources 폴더 밖의 폴더에 추가해요.
  • Snowflake CLI는 manifest.yml과 sources 아래 파일을 업로드해요.

참고: Git을 사용한다면 로컬 출력 파일이 Git에 푸시되지 않도록 out/을 .gitignore 파일에 추가해요.

DCM 프로젝트 폴더 구조 예시:

my_dcm_project/
  ├── manifest.yml
  ├── sources/
  │   ├── definitions/
  │   │   ├── bronze.sql
  │   │   └── silver.sql
  │   ├── macros/
  │   │   └── global_macro.sql
  ├── my_post_scripts/
  └── out/
      └── plan/

한 리포지토리에 여러 프로젝트를 유지할 수도 있어요. 각 프로젝트를 자체 manifest.yml과 sources/definitions/ 디렉터리를 가진 별도의 겹치지 않는 폴더에 보관해요.

repository/
  ├── customer_data/
  │   ├── manifest.yml
  │   └── sources/
  │       └── definitions/
  │           └── customer_data.sql
  └── reporting/
      ├── manifest.yml
      └── sources/
          └── definitions/
              └── reporting.sql

디렉터리를 전달해 특정 프로젝트에 대한 명령을 실행해요.

snow dcm plan --from ./customer_data/

각 프로젝트의 DCM 구성은 snowflake.yml이 아니라 manifest.yml 파일에 속해요.

/out/ 하위 폴더

프로젝트 디렉터리의 /out/ 폴더는 다음 중 하나로 PLAN을 실행한 뒤 렌더링된 프로젝트 정의를 포함해요.

  • CLI의 --save-output 플래그
  • Workspace UI
  • EXECUTE DCM PROJECT ... PLAN 문에서 이 폴더를 가리키는 OUTPUT_PATH

--save-output를 사용하는 각 CLI 명령은 로컬 /out/ 폴더를 다시 만들어요. 이 옵션으로 다른 명령을 실행하기 전에 보관해야 하는 로컬 출력을 복사해요. Workspaces와 SQL OUTPUT_PATH 대상은 자신만의 덮어쓰기 동작이 있어요.

PLAN의 경우 로컬 /out/ 폴더는 응답 JSON을 plan_result.json으로, 그리고 렌더링된 정의도 포함해요. Workspace UI는 응답 파일을 사용해 PLAN 체인지셋을 렌더링하며, 에이전트나 자동화로 처리할 수도 있어요. 이 로컬 출력은 DEPLOY 이후 DCM 프로젝트 안에 보존되는 배포 아티팩트와는 구별돼요.

다음을 고려해요.

  • 렌더링된 출력이나 체인지셋이 필요 없다면 /out/ 폴더를 무시할 수 있어요. Git 리포지토리에 커밋되는 것을 막으려면 프로젝트의 .gitignore에 /out/을 추가해요.
  • /out/ 폴더는 언제든 안전하게 삭제할 수 있어요. 출력 저장이 활성화된 PLAN을 다음에 실행할 때 다시 생성돼요.

매니페스트 파일 만들기

각 DCM 프로젝트는 manifest.yml 파일을 요구해요. 이 파일은 프로젝트의 필수 구성 세부 사항을 보유하고 프로젝트 폴더가 DCM 프로젝트로 식별되게 해줘요. 매니페스트 파일로 서로 다른 대상 환경에 배포할 때 사용할 DCM 프로젝트 객체와 역할을 제어하고 템플릿 값 집합을 관리해요.

매니페스트 파일은 다음 속성을 포함하는 YAML 파일이에요.

manifest_version: 2
type: DCM_PROJECT
default_target:
targets:
templating:
속성 필수 설명
manifest_version 필수 매니페스트 스키마의 버전. 현재 버전은 2.
type 필수 프로젝트의 유형. DCM_PROJECT로 설정.
default_target 선택 대상이 여러 개면 기본 대상을 지정. --target 플래그로 대상을 지정하지 않으면 Snowflake CLI와 Workspaces가 기본 대상을 사용.
targets 필수 targets 섹션은 각 배포 대상을 특정 Snowflake 계정, DCM 프로젝트 객체, 소유자 역할, 그리고 선택적으로 템플릿 구성에 매핑. 이 매핑은 모든 CLI 명령에서 정규화된 프로젝트 이름과 구성 플래그를 전달할 필요를 없애줘요.
templating 선택 templating 섹션은 프로젝트에 사용할 템플릿 구성을 정의.

프로젝트 대상(Project targets):

매니페스트 파일의 각 대상은 다음 속성을 포함해요.

targets:
  <target_name>:
    account_identifier:
    project_name:
    project_owner:
    templating_config:
속성 설명
account_identifier 이 대상의 Snowflake 계정 식별자.
project_name DCM 프로젝트 객체의 정규화된 이름(예: DCM_DEMO.PROJECTS.DCM_PROJECT_DEV). SHOW DCM PROJECTS SQL 명령으로 찾아요.
project_owner 이 프로젝트 객체에 대한 OWNERSHIP이 있는 역할. SHOW DCM PROJECTS 또는 DESCRIBE DCM PROJECT SQL 명령으로 찾아요.
templating_config (선택) 이 대상에 사용할, templating 섹션에 정의된 템플릿 구성의 이름.

대상은 인증된 Snowflake CLI 연결이나 활성 역할이 아니라 프로젝트 객체와 템플릿 구성을 선택해요.

프로젝트 정의와 프로젝트 객체의 매핑:

DCM Projects 정의 파일은 특정 DCM 프로젝트 객체에 엄격히 묶이지 않아요. 서로 다른 Snowflake 계정에서 또는 서로 다른 구성 프로필을 참조해 여러 프로젝트에 같은 정의 집합을 배포할 수 있어요. 예를 들어 리포지토리 브랜치의 같은 정의 파일을 DEV와 PROD 계정에 모두 배포할 수 있어요. 마찬가지로 서로 다른 경로의 정의 파일을 참조해 DCM Projects 객체를 실행할 수 있어요. 예를 들어 CI/CD 자동화는 main 브랜치의 정의를 배포하고, 로컬 정의 파일에서 같은 프로젝트에 대해 수동으로 PLAN을 실행해 정의가 최신 배포에서 어떻게 벗어났는지 확인할 수 있어요.

프로젝트 템플릿 구성:

매니페스트 파일의 템플릿 구성 높은 수준 구조는 다음과 같아요. 기본값만, 또는 구성만, 또는 둘 다 설정할 수 있어요.

templating:
  defaults:
    <variable_name>: <value>
  configurations:
    <configuration_name>:
      <variable_name>: <value>
속성 설명
defaults 반복을 피하기 위해 모든 구성에 걸쳐 적용되는 공유 변수 값(키-값 쌍).
configurations 프로젝트에 사용할 템플릿 구성. 개별 구성은 구성별 값으로 기본값을 재정의할 수 있어요.
<configuration_name> 템플릿 구성의 이름. 구성 이름은 대소문자를 구분하지 않아요.
<variable_name> 변수의 이름. 변수 이름은 Python 변수 명명 규칙을 따라야 해요. 프로젝트 정의의 모든 변수는 defaults, 선택된 구성, 또는 런타임에서 선언되어야 해요. 문자열 변수를 비워두고 해석되게 하려면 ""로 지정해요.
<value> 변수의 값. 값은 문자열, 숫자, 불리언, 목록, 또는 딕셔너리일 수 있어요. 딕셔너리는 매니페스트에서 정의할 수 있지만 런타임에서 덮어쓸 수 없어요.

예시: manifest.yml:

다음은 템플릿 변수와 그 기본값을 가진 DEV, STAGE, PROD 세 구성을 포함하는 DCM 프로젝트 매니페스트 파일(manifest.yml)의 예시예요.

manifest_version: 2
type: DCM_PROJECT
default_target: DCM_DEV

targets:
  DCM_DEV:
    account_identifier: MYORG-MYACCOUNT_DEV
    project_name: DCM_DEMO.PROJECTS.DCM_PROJECT_DEV
    project_owner: DCM_DEVELOPER
    templating_config: DEV
  DCM_STAGE:
    account_identifier: MYORG-MYACCOUNT_STAGE
    project_name: DCM_DEMO.PROJECTS.DCM_PROJECT_STG
    project_owner: DCM_STAGE_DEPLOYER
    templating_config: STAGE
  DCM_PROD:
    account_identifier: MYORG-MYACCOUNT_PROD
    project_name: DCM_DEMO.PROJECTS.DCM_PROJECT_PROD
    project_owner: DCM_PROD_DEPLOYER
    templating_config: PROD

templating:
  defaults:
    user: "GITHUB_ACTIONS_SERVICE_USER"
    wh_size: "SMALL"
  configurations:
    DEV:
      env_suffix: "_DEV"
      user: "INSERT_YOUR_USER"
      wh_size: "X-SMALL"
      teams:
        - name: "DEV_TEAM"
          write_access: TRUE
    STAGE:
      env_suffix: "_STG"
      teams:
        - name: "TEST_TEAM_A"
          write_access: TRUE
        - name: "TEST_TEAM_B"
          write_access: FALSE
    PROD:
      env_suffix: ""
      teams:
        - name: "Marketing"
          write_access: FALSE
        - name: "Finance"
          write_access: FALSE
          wh_size: "LARGE"
        - name: "HR"
          write_access: FALSE
        - name: "IT"
          write_access: TRUE

객체 정의 파일 만들기

DCM 프로젝트 정의 파일은 Snowflake 객체를 관리하기 위한 유효한 SQL 문으로 해석되는 템플릿이에요. 각 DCM 프로젝트는 정의 파일을 최소 하나 필요로 해요. 객체 정의와 권한을 여러 파일과 폴더에 걸쳐 구성할 수 있어요. Snowflake는 객체 유형별로 그룹화하는 것보다 프로젝트의 비즈니스 로직(예: bronze, silver, gold)을 나타내는 구조를 선택할 것을 권장해요.

정의 파일은 DEFINE, GRANT, 또는 ATTACH 문만 포함할 수 있어요. 다른 SQL 명령은 지원되지 않아요. DCM Projects로 빠르게 시작하려면 지원되는 객체 유형에 대해 기존 DDL에 DEFINE 키워드를 사용해 기존 SQL 배포 스크립트를 변환할 수 있어요.

DEFINE 문은 CREATE OR ALTER <object> 명령처럼 작동하지만 다음 핵심 차이점이 있어요.

  • DEFINE 문의 순서와 위치는 중요하지 않아요. Snowflake가 프로젝트 실행 중 모든 정의 파일의 모든 문을 수집하고 정렬해요.
  • DEFINE 문을 제거하면 다음에 프로젝트를 배포할 때 Snowflake가 해당 객체를 삭제해요.
  • 지원되는 Snowflake 객체의 부분집합만 지원돼요. 지원되는 엔터티를 참고해요.
  • 객체의 범위에 따라 객체 이름을 한정해요. 계정 수준 객체는 계정 수준 식별자, 데이터베이스 수준 객체는 적절한 두 부분 형식, 스키마 수준 객체는 database.schema.object_name 형식의 정규화된 세 부분 이름을 사용해요.

정의 파일은 다양한 Jinja2 템플릿 옵션을 포함할 수 있고 고급 템플릿 기능을 지원해요.

  • 템플릿 변수로 런타임에서 파일 내용을 커스터마이즈할 수 있어요.
  • 루프와 조건 같은 논리에 Jinja2 구문을 사용할 수 있어요.
  • 정의 파일을 다른 시나리오에 재사용·적응시킬 수 있어요.

객체 정의 템플릿:

DCM Projects는 SQL 문 템플릿을 위해 Jinja2 프레임워크를 지원해요. Jinja2 구문으로 구성 프로필에서, EXECUTE DCM PROJECT 명령에서, 또는 Jinja 안에서 변수를 선언하고 값을 할당할 수 있어요. 값 목록을 통한 루프, case 문, 재사용 가능한 함수 등을 구성할 수도 있어요.

지원되는 Jinja2 기능:

  • 문자열 치환
  • 목록
  • 딕셔너리와 중첩 딕셔너리
  • 조건(IF 문)
  • 루프
  • 전역 및 파일 내 매크로

sources/macros 폴더에 정의된 매크로는 모든 정의 파일에서 사용할 수 있어요. 파일 안에 정의된 매크로는 파일 안에서 사용할 수 있어요.

지원되지 않는 Jinja2 기능:

  • 다음 태그 사용: import, extends, include

참고: _snow 식별자는 향후 사용을 위해 예약되어 있으며 변수나 매크로 이름으로 사용할 수 없어요.

중요: 민감한 정보나 자격 증명을 포함한 객체 정의에 DCM Projects 템플릿 변수를 사용하지 마세요. 렌더링된 SQL 정의는 환경 변수가 삽입한 값을 읽어내지 않아요. 마찬가지로 Snowflake 서비스를 사용할 때 파일 이름, 구성·변수 이름 같은 메타데이터로 개인 데이터, 민감한 데이터, 수출 통제 데이터, 또는 기타 규제 대상 데이터를 입력하지 마세요.

Jinja2 템플릿을 사용하는 DCM 프로젝트 정의 파일 예시:

DEFINE WAREHOUSE DCM_PROJECT_WH_{{db}}
  WITH
    warehouse_size = '{{wh_size}}'
    auto_suspend = 300;

DEV와 PROD 두 구성을 정의하는 manifest.yml 예시:

templating:
  configurations:
    DEV:
      db: "DEV_2"
      wh_size: "X-SMALL"
    PROD:
      db: "PROD"
      wh_size: "LARGE"

이 웨어하우스 정의를 DEV 구성으로 렌더링하면 다음으로 해석돼요.

DEFINE WAREHOUSE DCM_PROJECT_WH_DEV_2
  WITH
    warehouse_size = 'X-SMALL'
    auto_suspend = 300;

매크로:

매크로 파일은 macros 폴더와 그 하위 폴더에 있는 모든 SQL 파일이에요. 매크로만 포함할 수 있어요. 일반 프로그래밍 언어의 함수와 비슷하게 매크로는 자주 사용되는 코드 조각을 재사용 가능한 함수로 정리해 반복을 피하고 DRY(Don't Repeat Yourself) 원칙을 지켜요. DCM Projects의 매크로는 다음 예외를 제외하고 Jinja2 매크로와 같은 방식으로 작동해요.

  • 매크로 파일을 위한 전용 위치가 macros 폴더예요.
  • 매크로 파일에 정의된 매크로는 다른 소스 파일에서 자동으로 보여요.
  • import Jinja 태그는 허용되지 않아요.
  • 같은 이름의 매크로 중복 정의는 감지되어 거부돼요.

정의 파일 렌더링 과정에서 소스 파일이 잠재적인 매크로 호출을 위해 스캔돼요. 호출된 매크로가 매크로 파일에 정의되어 있다면 암시적 from [...] import 태그가 자동으로 추가되므로 명시적인 import가 필요 없어요. Jinja2 매크로와 비슷하게 밑줄로 접두사 처리해 로컬 매크로를 정의할 수 있어요. 로컬 매크로는 선언된 파일에서만 사용할 수 있고 다른 파일에서는 보이지 않아요.

템플릿 주석:

SQL 명령에서 코드 앞에 --를 추가해 줄을 주석 처리할 수 있어요. Jinja는 SQL 코드 안의 변수를 여전히 처리하지만 SQL 주석은 남겨요. 예를 들어 다음 Jinja 코드:

-- hello {{ project_owner_role }}

다음으로 렌더링돼요:

-- hello DCM_DEVELOPER

주석 처리된 명령은 SQL에서 실행되지 않아요. 템플릿 주석을 사용해 SQL 코드에 영향을 주지 않고 Jinja 템플릿을 디버깅할 수 있어요. 렌더링 중 Jinja 코드를 무시하려면 다음 예시처럼 여는 괄호와 닫는 괄호 안에 #을 추가해요.

{# This Jinja comment will not appear in the rendered output. #}

구성:

객체 정의에서 템플릿을 사용할 때 다음 옵션이 있어요.

  • 런타임에서 변수에 값을 할당해요.
  • manifest.yml의 templating: configurations: 아래에 서로 다른 구성 프로필을 정의해요. 각 대상은 templating_config로 구성을 참조할 수 있어요.

구성 프로필이 정의되고 대상이 하나를 templating_config로 참조하면, 그 대상을 사용할 때 구성이 자동으로 적용돼요. DCM Projects에서 구성 프로필의 주요 사용 사례는 서로 다른 환경을 대상으로 하는 것이에요. 구성 프로필로 다음을 할 수 있어요.

  • 같은 코드를 여러 환경에 배포.
  • 비프로덕션 환경에서 축소된 규모로 프로덕션 코드를 테스트.
  • 같은 계정에서 여러 격리된 환경을 유지 관리.

모든 템플릿 구성이 대상 프로필에 참조될 필요는 없어요. 대상의 템플릿 구성을 한 구성에서 다른 구성으로 전환하려고 사용하지 않는 구성을 유지할 수 있어요.

경고: DCM 프로젝트는 한 번에 하나의 배포된 구성만 가져요. 대상을 다른 구성으로 전환하면 새로 렌더링된 정의에 없는 이전 구성의 객체를 삭제할 수 있어요. 공존 배포를 위해서는 서로 다른 DCM 프로젝트 객체와 겹치지 않는 관리 객체 이름을 사용해요.

  • templating: defaults: 아래에 공유 기본값을 정의해 구성에 걸쳐 공통 변수를 반복하는 것을 피해요.
  • CLI의 --variable 플래그로 런타임에서 일회성 값으로 특정 변수를 덮어써요.

변수는 전역 기본값 < 구성 변수 < 런타임 실행 변수의 3단계 계층으로 해석돼요.

딕셔너리:

DCM Projects 템플릿은 복잡한 멀티테넌트 또는 멀티리소스 배포를 위한 구조적 구성을 가능하게 하는 딕셔너리를 변수 값으로 지원해요. 관련 구성 세부 사항을 딕셔너리로 그룹화하면 다음을 얻을 수 있어요.

  • 세분화된 제어: 웨어하우스 크기, 보존 정책, 권한 같은 특정 설정을 개별 리소스에 적용해요.
  • 더 깨끗한 코드 베이스: 반복되는 하드코딩된 스크립트를 구성에 적응하는 동적 루프로 대체해요.
  • 확장성: 배포 파이프라인을 리팩터링하는 대신 구성에 항목을 추가해 새 팀이나 리소스를 온보딩해요.

참고: 딕셔너리는 매니페스트에서 정의할 수 있지만 런타임에서 --variable 플래그나 SQL USING CONFIGURATION (...) 재정의로 덮어쓸 수 없어요. 스칼라 값과 목록만 런타임에서 덮어쓸 수 있어요.

예를 들어 마케팅, 파이낸스, HR처럼 각각 다른 규정 준수·컴퓨팅 요건을 가진 여러 부서가 공유하는 플랫폼을 고려해요. 딕셔너리로 각 팀의 요구를 담는 단일 구성을 정의할 수 있어요.

매니페스트 예시:

templating:
  defaults:
    user: "GITHUB_ACTIONS_SERVICE_USER"
    wh_size: "X-SMALL"
  configurations:
    PROD:
      env_suffix: ""
      project_owner_role: "DCM_PROD_DEPLOYER"
      teams:
        - name: "Marketing"
          wh_size: "MEDIUM"
          data_retention_days: 14
          needs_sandbox_schema: true
        - name: "Finance"
          wh_size: "X-LARGE"
          data_retention_days: 90
          needs_sandbox_schema: false
        - name: "HR"
          data_retention_days: 30
          needs_sandbox_schema: false

정의 예시 — SQL 템플릿은 이 딕셔너리를 순회하며 스키마를 자동으로 만들고, 올바른 보존 정책을 할당하고, 요청한 팀에 대해서만 조건부로 추가 리소스를 만들어요.

-- team 딕셔너리 순회
{% for team in teams %}
  {% set team_name = team.name | upper %}

  -- 딕셔너리 값을 객체 속성에 직접 주입
  define schema DCM_DEMO_1{{env_suffix}}.{{team_name}}
    comment = 'using JINJA dictionary values'
    data_retention_time_in_days = {{ team.data_retention_days }};

  -- 이름을 매크로에 전달
  {{ create_team_roles(team_name) }}

  define table DCM_DEMO_1{{env_suffix}}.{{team_name}}.PRODUCTS (
      ITEM_NAME varchar,
      ITEM_ID varchar,
      ITEM_CATEGORY array
  )
  data_metric_schedule = 'TRIGGER_ON_CHANGES';

  {% if team_name == 'HR' %}
    define table DCM_DEMO_1{{env_suffix}}.{{team_name}}.EMPLOYEES (
        NAME varchar,
        ID int
    )
    comment = 'This table is only created in HR';
  {% endif %}

  -- 딕셔너리 불리언으로 선택적 인프라 배포
  {% if team.needs_sandbox_schema | default(false) %}
    define schema DCM_DEMO_1{{env_suffix}}.{{team_name}}_SANDBOX
      comment = 'Sandbox schema defined via dictionary flag'
      data_retention_time_in_days = 1;
  {% endif %}
{% endfor %}

더 알아보기