Snowflake Notebooks 개요

Snowflake Notebooks는 Snowsight의 Workspaces 환경에서 제공되는 Jupyter 호환 .ipynb 기반 개발 환경입니다. Python, SQL, Markdown을 한 파일 안에서 함께 사용할 수 있으며, 데이터 탐색, 머신러닝, 데이터 엔지니어링 워크플로를 Snowflake 데이터 위에서 수행할 수 있습니다.
기존 Snowsight 메뉴의 Notebooks는 Legacy Snowflake Notebooks로 구분되며, 본 실습은 Notebooks in Workspaces 기준으로 진행합니다.

비교

📊 데이터 분석 및 개발

  • 탐색적 데이터 분석 수행
  • 머신러닝 모델 개발
  • 데이터 사이언스 및 데이터 엔지니어링 워크플로 지원
  • 모든 작업을 하나의 통합된 인터페이스에서 처리

🔧 개발 환경

  • Python 3.10~3.12와 Snowflake Container Runtime(최소 2.2)을 지원

    Python 버전과 Runtime 버전은 Notebook Service 생성 시 선택하며, 계정·시점에 따라 선택 가능한 버전이 달라질 수 있습니다.

  • SQL과 Python 코드 셀별 개발 및 실행
  • Markdown 셀을 통한 문서화 및 결과 설명

🚀 런타임 환경

  • Workspace 노트북은 Container Runtime만 지원

    Warehouse Runtime은 Legacy Notebooks에서만 선택할 수 있습니다.

  • 코드 실행을 위해 사용자별 Notebook Service가 필요하며, 이 서비스는 Compute Pool 위에서 동작
  • SQL과 Snowpark 연산은 별도의 Query Warehouse에서 실행

    Compute Pool 크레딧과 Warehouse 크레딧이 각각 발생할 수 있습니다.

🔗 협업 및 버전 관리

  • Workspace에서는 Shared Workspace를 통해 팀 단위 협업이 가능
  • 역할별 View/Edit 권한을 부여 가능
  • Git 연동 Workspace를 사용하여 버전 관리와 브랜치 작업 지원

    Legacy Notebooks처럼 “동일 역할 소유자만 접근”하는 방식과 달리, Workspace는 파일 기반 IDE 환경에 가깝습니다.

⏰ 자동화 및 스케줄링

  • Workspace 노트북을 운영 파이프라인으로 실행하려면, 먼저 Notebook Project Object(NPO) 를 생성한 뒤 EXECUTE NOTEBOOK PROJECT로 실행
  • Snowflake Task와 연동해 스케줄 실행도 가능

💡 활용 사례

  • 데이터 탐색: Snowflake 데이터를 활용한 대화형 분석
  • ML 워크플로: 엔드투엔드 머신러닝 개발 환경
  • 데이터 파이프라인: Snowpark Python을 활용한 대규모 데이터 처리
  • Workspace에서는 Terminal, Scratchpad, Variables Explorer, Jupyter magic(%run, %time 등)을 함께 사용할 수 있어, 단순 노트북 실행을 넘어 IDE 수준의 개발이 가능

Snowflake Notebooks 실습

1. 실습 역할 및 데이터베이스 준비

실습을 위해 별도의 역할과 데이터베이스를 생성합니다.

  • Projects > Workspaces 메뉴로 이동합니다

  • + Add new를 클릭하고 sql 파일 이름 지정합니다 (예, setup.sql)

  • 다음 SQL 구문을 복사해서 붙여넣고 파란색 Run 버튼을 눌러 역할, 데이터베이스, Compute Pool 권한을 설정합니다.

    --
    USE ROLE ACCOUNTADMIN ;
    SET my_user = CURRENT_USER();
    -- ============================================
    -- 1. DS_ROLE 생성 및 현재 사용자에게 할당
    -- ============================================
    USE ROLE USERADMIN;
    
    CREATE ROLE IF NOT EXISTS DS_ROLE ;
    GRANT ROLE DS_ROLE TO USER IDENTIFIER($my_user);
    
    -- ============================================
    -- 2. TESTDB 생성 및 권한 부여
    -- ============================================
    USE ROLE SYSADMIN;
    
    CREATE DATABASE IF NOT EXISTS TESTDB;
    
    GRANT USAGE ON DATABASE TESTDB TO ROLE DS_ROLE;
    GRANT CREATE SCHEMA ON DATABASE TESTDB TO ROLE DS_ROLE;
    
    GRANT USAGE ON FUTURE SCHEMAS IN DATABASE TESTDB TO ROLE DS_ROLE;
    GRANT ALL ON FUTURE TABLES IN DATABASE TESTDB TO ROLE DS_ROLE;
    GRANT ALL ON FUTURE VIEWS IN DATABASE TESTDB TO ROLE DS_ROLE;
    
    GRANT CREATE TABLE ON SCHEMA TESTDB.PUBLIC TO ROLE DS_ROLE;
    GRANT CREATE STAGE ON SCHEMA TESTDB.PUBLIC TO ROLE DS_ROLE;
    
    GRANT USAGE ON ALL SCHEMAS IN DATABASE TESTDB TO ROLE DS_ROLE;
    GRANT CREATE NOTEBOOK PROJECT ON ALL SCHEMAS IN DATABASE TESTDB TO ROLE DS_ROLE;
    
    GRANT USAGE ON SCHEMA TESTDB.PUBLIC TO ROLE DS_ROLE;
    GRANT CREATE TASK ON SCHEMA TESTDB.PUBLIC TO ROLE DS_ROLE ;
    GRANT CREATE NOTEBOOK PROJECT ON SCHEMA TESTDB.PUBLIC TO ROLE DS_ROLE;
    GRANT CREATE NOTEBOOK PROJECT ON FUTURE SCHEMAS IN DATABASE TESTDB TO ROLE DS_ROLE;
    GRANT MONITOR ON WAREHOUSE COMPUTE_WH TO ROLE DS_ROLE;
    GRANT USAGE ON WAREHOUSE COMPUTE_WH TO ROLE DS_ROLE;
    
    -- ============================================
    -- 3. Compute Pool 생성 및 권한 부여
    -- ============================================
    USE ROLE ACCOUNTADMIN;
    
    CREATE COMPUTE POOL IF NOT EXISTS DS_NOTEBOOK_POOL
           MIN_NODES = 1
           MAX_NODES = 3
           INSTANCE_FAMILY = CPU_X64_XS
           AUTO_RESUME = TRUE
           AUTO_SUSPEND_SECS = 3600;  
    
    GRANT USAGE ON COMPUTE POOL DS_NOTEBOOK_POOL TO ROLE DS_ROLE;
    GRANT USAGE ON COMPUTE POOL SYSTEM_COMPUTE_POOL_CPU TO ROLE DS_ROLE;
    GRANT EXECUTE TASK ON ACCOUNT TO ROLE DS_ROLE;
  • 스크립트 설명

    • 이 SQL은 실습용 데이터베이스와 역할을 준비하는 스크립트입니다.
    • CREATE NOTEBOOK PROJECT, CREATE TASK 권한은 노트북을 NPO로 배포하거나 Task로 스케줄링할 때 필요합니다.
    • 이번 실습처럼 Workspace에서 .ipynb 파일을 열고 실행하는 단계만 진행한다면 필수는 아닙니다.
    • Compute Pool은 Notebook Service가 사용하는 실행 환경입니다. 본 실습에서는 DS_NOTEBOOK_POOL을 직접 생성하지만, 계정에 기본 제공되는 SYSTEM_COMPUTE_POOL_CPU를 사용해도 됩니다. 이 경우 해당 풀에 대한 USAGE 권한만 있으면 됩니다.
  • SQL 실행이 끝나면, Workspace 상단의 Role/Warehouse picker에서 역할을 DS_ROLE로 변경합니다. 이후 실습에서 사용할 Query Warehouse도 함께 선택합니다.

  • 새로 로그인할 경우 기본 롤을 설정하기 위해 왼쪽 하단의 로그인한 이름을 클릭하여 Switch Role에서 DS_ROLE로 변경하고 Set as default를 설정 합니다

Role변경

2. Notebooks 파일 생성

Snowflake Notebook을 생성합니다.
Workspace에서 노트북 파일을 생성합니다. Legacy Notebooks 메뉴의 + Notebook과 달리, Workspace에서는 .ipynb 파일을 직접 만듭니다.

  • Projects > Workspaces 메뉴로 이동합니다
  • + Add new 클릭 > Notebook 선택합니다
  • 다음과 같이 Notebook 이름을 입력
    • Name : Test_notebook.ipynb

3. Notebook Service 란

Workspace Notebooks는 Container Runtime 기반이므로, 코드를 실행할 Notebook Service가 필요합니다. Notebook Service는 Compute Pool 위에서 동작하는 Snowflake-managed 서비스이며, 사용자별로 생성됩니다. 한 서비스에 여러 .ipynb 노트북을 연결해 공유할 수 있습니다.

서비스가 RUNNING 상태인 동안 Compute Pool 크레딧이 발생하며, idle timeout 이후 자동으로 suspend됩니다. SQL 실행에 사용되는 Query Warehouse 크레딧은 별도로 발생합니다.

  • Notebook Service를 생성하고 연결하기
    • 상단의 Connect버튼 옆에 아래 화살표 를 클릭 > Create new service 클릭
      • Service name : 서비스 이름 (예, DS_NOTEBOOK_SERVICE )
      • Artifact repositories : SNOWFLAKE.SNOWPARK.PYPI_SHARED_REPOSITORY
      • Python version : 3.12
      • Runtime version Container Runtime 버전 : (예: 2.5)
      • Compute pool : 앞서 생성한 **DS_NOTEBOOK_POOL** 선택
      • Idle timeout : 미 사용 자동 suspend 시간 (예: 1 hour)
    • Create and connect 클릭하면 해당 노트북이 Notebook Service에 연결됩니다

노트북서비스

4. 마크다운 셀 생성

마크다운 셀을 활용하면 단순한 코드 실행을 넘어 완전한 분석 문서를 만들 수 있어, 데이터 과학자와 엔지니어들이 더 효과적으로 작업을 문서화하고 공유할 수 있습니다.

  • 📝 문서화 및 설명
    • 결과를 맥락화하고 다양한 결과에 대한 메모 작성
    • 코드와 차트에 대한 상세한 설명 및 주석 추가
  • 🔗 통합된 개발 환경
    • Python, SQL과 함께 하나의 인터페이스에서 문서 작성
    • 셀 간 참조가 가능하여 코드와 설명을 seamless하게 연결
  • 👥 협업 및 공유
    • 팀원들과 노트북을 공유할 때 분석 과정과 결과를 명확히 전달
    • 프레젠테이션이나 보고서 용도로 활용 가능
  • 🎯 가독성 향상
    • 복잡한 데이터 분석 과정을 단계별로 설명
    • 헤더, 리스트, 링크 등 마크다운 문법으로 구조화된 문서 작성
  • 📊 분석 스토리텔링
    • 데이터 분석의 논리적 흐름을 설명
    • 결과 해석 및 인사이트를 명확하게 기록

셀은 기존 셀 하단 또는 노트북 하단의 + SQL, + Python, + Markdown 버튼으로 추가할 수 있습니다. Markdown 셀은 편집 후 Done Editing(☑️) 을 눌러 렌더링된 결과를 확인합니다.
코드 셀은 Run cell(▶️) 로 실행합니다.

상단의 PythonMarkdown으로 변경하여 새 마크다운 셀을 사용합니다.

  • 다음 내용을 복사해서 넣어 봅니다
    ## 1. 테스트 테이블 생성 (SQL)
    
    테스트용 테이블을 생성합니다. 이미 존재하는 경우 스킵합니다.
  • 입력이 끝나면 ☑️(Done Editing) 클릭

마크다운입력완료

5. SQL 셀 생성

Python 코드만으로 SQL구문을 작성하려면 SQL문자열이 길어질 수록 가독성이 떨어집니다. SQL 문법에 최적화된 하이라이팅을 지원하여 쿼리 구조를 한눈에 파악하기 쉽습니다. Snowflake Notebooks의 가장 큰 강점 중 하나는 SQL 셀의 결과를 Python에서 즉시 사용하거나, 반대의 경우도 가능하다는 점입니다.

Workspace 노트북은 database/schema 컨텍스트가 자동으로 설정되지 않으므로, SQL 셀에서 반드시 USE DATABASE, USE SCHEMA를 실행합니다. 상단 picker에서 선택한 Role과 Warehouse도 함께 확인하세요.

하단의 + SQL을 클릭하여 새 SQL 셀을 생성합니다.

  • 셀 이름 : S1
  • 다음 내용을 복사해서 넣어 봅니다
    
    USE DATABASE TESTDB ;
    USE SCHEMA PUBLIC ;
    
    CREATE TABLE IF NOT EXISTS test_table (
        id INTEGER AUTOINCREMENT,
        timestamp TIMESTAMP,
        value FLOAT,
        category VARCHAR(50)
    );
  • 입력이 끝나면 ▶️(Run cell) 클릭하여 실행합니다

S1

6. Python 셀 생성

위와 같은 방법으로 Markdown 셀과 Python 셀을 생성합니다.

S1 하단의 + Markdown을 클릭하여 새 마크다운 셀을 생성합니다.

  • 다음 내용을 복사해서 넣어 봅니다
    ## 2. 랜덤 데이터 생성 (Python)
    
    현재 시간과 랜덤한 값, 카테고리를 생성합니다.
  • 입력이 끝나면 ☑️(Done Editing) 클릭

하단의 + Python을 클릭하여 새 Python 셀을 생성합니다.

  • 셀 이름 : P1
  • 다음 내용을 복사해서 넣어 봅니다
    
    import random
    from datetime import datetime
    
    v_current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    random_value = random.uniform(0, 100)
    categories = ['A', 'B', 'C', 'D']
    random_category = random.choice(categories)
    
    print(f"Time: {v_current_time}")
    print(f"Value: {random_value}")
    print(f"Category: {random_category}")
  • 입력이 끝나면 ▶️(Run cell) 클릭하여 실행합니다

P1

7. Python 변수 참조

Jinja 형식 {{변수명}}을 사용하면 SQL 셀에서 Python 변수를 참조할 수 있습니다.
Python 셀 P1에서 생성한 v_current_time, random_value, random_category를 SQL 셀 S2에서 사용해 데이터를 삽입합니다.

P1셀 하단의 + Markdown을 클릭하여 새 마크다운 셀을 생성합니다.

  • 다음 내용을 복사해서 넣어 봅니다
    ## 3. 데이터 삽입 (SQL)
    
    이전 셀에서 생성한 Python 변수를 Jinja 템플릿으로 참조하여 테이블에 데이터를 삽입합니다.
  • 입력이 끝나면 ☑️(Done Editing) 클릭

+ SQL을 클릭하여 새 SQL 셀을 생성합니다.

  • 셀 이름 : S2
  • 다음 내용을 복사해서 넣어 봅니다
    
    INSERT INTO test_table (timestamp, value, category)
    VALUES ('{{v_current_time}}', {{random_value}}, '{{random_category}}')
  • 숫자형 변수 random_value는 따옴표 없이, 문자열 변수는 SQL 값으로 사용할 때 적절히 quoting합니다.
  • 입력이 끝나면 ▶️(Run cell) 클릭하여 해당 SQL을 실행합니다

S2

S2셀 하단의 + Markdown을 클릭하여 새 마크다운 셀을 생성합니다.

  • 다음 내용을 복사해서 넣어 봅니다
    ## 4. 데이터 조회 (SQL)
    
    입력된 데이터를 SQL 쿼리로 조회합니다.
  • 입력이 끝나면 ☑️(Done Editing) 클릭

+ SQL을 클릭하여 새 SQL 셀을 생성합니다.

  • 셀 이름 : S3
  • 다음 내용을 복사해서 넣어 봅니다
    
    SELECT * FROM test_table ORDER BY timestamp DESC
  • 입력이 끝나면 ▶️(Run cell) 클릭하여 실행합니다

S3

8. 쿼리 결과를 Python에서 사용

Workspace 노트북에서는 SQL 셀 결과가 dataframe_x 형태의 pandas DataFrame pointer 로 노출됩니다. 결과 영역의 tooltip을 확인하면 Python에서 참조할 정확한 이름을 볼 수 있습니다.

S3 하단의 + Markdown을 클릭하여 새 마크다운 셀을 생성합니다.

  • 다음 내용을 복사해서 넣어 봅니다
    ## 5. 데이터 시각화 (Python)
    
    조회한 데이터에 대해 차트로 시각화합니다.
    
  • 입력이 끝나면 ☑️(Done Editing) 클릭

+ Python을 클릭하여 새 Python 셀을 생성합니다.

  • 셀 이름 : P2
  • 다음 내용을 복사해서 넣어 봅니다
    
    import matplotlib.pyplot as plt
    import pandas as pd
    import seaborn as sns
    
    df = dataframe_3
    
    fig, axes = plt.subplots(2, 2, figsize=(16, 10))
    fig.subplots_adjust(hspace=0.4, wspace=0.3)
    
    # Color palette
    colors = sns.color_palette('Set2', len(df['CATEGORY'].unique()))
    
    # 1. Count by Category
    category_counts = df.groupby('CATEGORY').size().sort_index()
    bars1 = axes[0, 0].bar(category_counts.index, category_counts.values, 
                           color=colors, edgecolor='black', linewidth=2, width=0.6)
    for bar in bars1:
        height = bar.get_height()
        axes[0, 0].text(bar.get_x() + bar.get_width()/2., height,
                        f'{int(height)}', ha='center', va='bottom', 
                        fontweight='bold', fontsize=11)
    axes[0, 0].set_xlabel('Category', fontsize=13, fontweight='bold')
    axes[0, 0].set_ylabel('Count', fontsize=13, fontweight='bold')
    axes[0, 0].set_title('Count by Category', fontsize=15, fontweight='bold', pad=20)
    axes[0, 0].grid(True, alpha=0.3, axis='y', linestyle='--')
    
    # 2. Average by Category
    category_avg = df.groupby('CATEGORY')['VALUE'].mean().sort_index()
    bars2 = axes[0, 1].bar(category_avg.index, category_avg.values,
                           color=colors, edgecolor='black', linewidth=2, width=0.6)
    for bar in bars2:
        height = bar.get_height()
        axes[0, 1].text(bar.get_x() + bar.get_width()/2., height,
                        f'{height:.1f}', ha='center', va='bottom', 
                        fontweight='bold', fontsize=11)
    axes[0, 1].set_xlabel('Category', fontsize=13, fontweight='bold')
    axes[0, 1].set_ylabel('Average Value', fontsize=13, fontweight='bold')
    axes[0, 1].set_title('Average by Category', fontsize=15, fontweight='bold', pad=20)
    axes[0, 1].grid(True, alpha=0.3, axis='y', linestyle='--')
    
    # 3. Minimum by Category
    category_min = df.groupby('CATEGORY')['VALUE'].min().sort_index()
    bars3 = axes[1, 0].bar(category_min.index, category_min.values,
                           color=colors, edgecolor='black', linewidth=2, width=0.6)
    for bar in bars3:
        height = bar.get_height()
        axes[1, 0].text(bar.get_x() + bar.get_width()/2., height,
                        f'{height:.1f}', ha='center', va='bottom', 
                        fontweight='bold', fontsize=11)
    axes[1, 0].set_xlabel('Category', fontsize=13, fontweight='bold')
    axes[1, 0].set_ylabel('Min Value', fontsize=13, fontweight='bold')
    axes[1, 0].set_title('Minimum by Category', fontsize=15, fontweight='bold', pad=20)
    axes[1, 0].grid(True, alpha=0.3, axis='y', linestyle='--')
    
    # 4. Maximum by Category
    category_max = df.groupby('CATEGORY')['VALUE'].max().sort_index()
    bars4 = axes[1, 1].bar(category_max.index, category_max.values,
                           color=colors, edgecolor='black', linewidth=2, width=0.6)
    for bar in bars4:
        height = bar.get_height()
        axes[1, 1].text(bar.get_x() + bar.get_width()/2., height,
                        f'{height:.1f}', ha='center', va='bottom', 
                        fontweight='bold', fontsize=11)
    axes[1, 1].set_xlabel('Category', fontsize=13, fontweight='bold')
    axes[1, 1].set_ylabel('Max Value', fontsize=13, fontweight='bold')
    axes[1, 1].set_title('Maximum by Category', fontsize=15, fontweight='bold', pad=20)
    axes[1, 1].grid(True, alpha=0.3, axis='y', linestyle='--')
    
    plt.show()
    
    # Summary statistics
    print("=" * 60)
    print("Data Summary".center(60))
    print("=" * 60)
    print(f"Total Records: {len(df):,}")
    print(f"Value Range: {df['VALUE'].min():.2f} ~ {df['VALUE'].max():.2f}")
    print(f"Mean Value: {df['VALUE'].mean():.2f}")
    print(f"Standard Deviation: {df['VALUE'].std():.2f}")
    
    print("\n" + "=" * 60)
    print("Category Statistics".center(60))
    print("=" * 60)
    category_stats = df.groupby('CATEGORY')['VALUE'].agg(['count', 'mean', 'min', 'max']).round(2)
    category_stats.columns = ['Count', 'Average', 'Minimum', 'Maximum']
    print(category_stats.to_string())
  • SQL 결과를 pandas DataFrame으로 받을 때 컬럼명 대소문자가 환경마다 다를 수 있습니다. 시각화 전에 print(df.columns)로 실제 컬럼명을 확인한 뒤 CATEGORY, VALUE 등에 맞게 코드를 조정하세요.
  • 입력이 끝나면 ▶️(Run cell) 클릭하여 실행합니다

P2

9. 전체 실행

상단의 Run를 클릭하여 모든 셀을 순서대로 실행할 수 있습니다.
CREATE TABLE IF NOT EXISTS를 사용했기 때문에 Run all을 여러 번 실행하면 데이터가 누적됩니다.

  • 여러번 클릭해서 데이터가 정상적으로 입력되는지 확인해 봅니다
  • 테이블을 생성하는 S1 셀에서 CREATE TABLE IF NOT EXISTS 옵션을 사용했기 때문에 기존 데이터가 삭제되지 않고 계속 누적됩니다.

ALL

결과를 HTML로 저장하기
Workspace 노트북에서는 셀 실행 결과가 .ipynb 파일에 저장되지 않습니다. 분석 결과와 출력을 그대로 공유하려면 HTML로 내보내는 것이 편합니다. Export as HTML로 저장하면 셀 코드, 실행 결과, 표·차트 출력을 하나의 파일로 묶을 수 있습니다. Workspace에서 바로 확인한 결과를 그대로 전달할 때 유용합니다

  • 노트북 편집 화면 오른쪽 상단의 ...(더보기) 메뉴를 클릭합니다.
  • 드롭다운에서 Export as HTML을 선택합니다.
  • 생성된 HTML 파일을 다운로드해 팀원과 공유하거나 보고 자료로 활용합니다.

html결과


본 글은 Snowflake의 차세대 노트북 환경인 Notebooks in Workspaces 기준으로 작성되었습니다. Legacy Notebooks와의 주요 차이는 다음과 같습니다.

  • 노트북이 database/schema 객체가 아니라 Workspace의 .ipynb 파일로 관리됨
  • Container Runtime만 지원
  • SQL 결과 참조 시 셀 이름 대신 dataframe_x 사용
  • 운영 실행은 NPO + EXECUTE NOTEBOOK PROJECT 흐름 사용
  • Legacy Notebooks에서 Workspace로 이전할 때는 Snowsight의 Bulk Migration 기능과 Cortex Code 리팩터링 기능을 함께 활용할 수 있습니다.
profile
Snowflake Korea SE

0개의 댓글