Skip to main content
Databricks Apps의 고급 기능과 최신 통합 옵션을 다룹니다: Scale to Zero, Lakebase(PostgreSQL) 연동, Databricks Asset Bundles(DABs) 배포, AppKit 프레임워크.

Scale to Zero — App Spaces (Private Preview)

App Spaces 는 microVM 기반의 새로운 런타임으로, Scale to Zero를 지원합니다. 약 30분간 트래픽이 없으면 자동으로 중지되고, 새 요청이 들어오면 자동 재시작됩니다.
주의 현재 상태: App Spaces는 Private Preview 입니다. 기존 앱을 App Spaces로 변환할 수 없으며, 새 앱을 생성해야 합니다. GA 전까지는 아래 “현재 비용 절감 방법”을 참고하세요.

동작 방식

상태조건과금 여부응답 시간
Active요청 처리 중 또는 최근 트래픽 있음과금즉시
Idle트래픽 없음 (30분 미만)과금즉시
Scaled to Zero30분간 트래픽 없어 자동 중지비과금콜드 스타트 (수 초~수십 초)

App Spaces의 현재 제한사항

항목상태
기존 앱 변환불가 — 새 앱 생성 필요
워크스페이스 Network Policy아직 미적용 (SEG, Private Link 미지원)
템플릿 기반 생성미지원 — API로 직접 생성

현재 비용 절감 방법 (GA 전까지)

App Spaces GA 전까지는 다음 방법으로 비용을 관리할 수 있습니다: 1. Databricks Jobs로 자동 시작/중지 스케줄링:
# 앱 자동 중지/시작 Job 예시 (Python SDK)
from databricks.sdk import WorkspaceClient

w = WorkspaceClient()

# 퇴근 시 앱 중지 (7PM Job으로 등록)
w.apps.stop("my-dashboard-app")

# 출근 시 앱 시작 (7AM Job으로 등록)
w.apps.start("my-dashboard-app")
2. CLI 기반 스크립트:
# cron 또는 Databricks Job으로 실행
# 퇴근 시 (19:00)
databricks apps stop my-dashboard-app

# 출근 시 (07:00)
databricks apps start my-dashboard-app
3. 사용하지 않는 앱 수동 중지:
# 모든 앱 목록 확인
databricks apps list --output json | jq '.[] | select(.status == "RUNNING") | .name'

# 불필요한 앱 중지
databricks apps stop unused-app-1

콜드 스타트 최적화

Scale to Zero에서 앱이 다시 시작될 때 콜드 스타트 시간이 발생합니다. 이를 최소화하는 방법:
최적화 방법효과설명
경량 의존성시작 시간 단축requirements.txt에서 불필요한 패키지 제거
Lazy Loading초기 로드 단축무거운 라이브러리는 필요할 때 import
Serverless Warehouse쿼리 응답 단축Classic Warehouse는 콜드 스타트가 길어 추가 지연 발생
캐싱반복 요청 가속st.cache_data, st.cache_resource 활용
참고 비용 참고: 실제 운영 사례에서 Medium 사이즈 앱의 월 비용은 약 215입니다(24/7실행기준).업무시간만운영(12시간/,5)하면약215** 입니다 (24/7 실행 기준). 업무 시간만 운영(12시간/일, 주 5일)하면 약 **65/월 로 절감할 수 있습니다.

Horizontal Scaling (수평 스케일링, Private Preview)

단일 컨테이너의 성능 한계를 넘기 위해 최대 5개 인스턴스 로 수평 확장할 수 있습니다.
항목설명
상태Private Preview
인스턴스 수1~5개 (수동 설정)
세션 친화성Stateful session affinity 지원 (업계 최초)
무중단 배포Zero-downtime deployments
빌드 캐시Deployment build caching

REST API 설정

{
  "name": "my-scaled-app",
  "description": "Horizontally scaled app",
  "compute_size": "MEDIUM",
  "compute_min_instances": 1,
  "compute_max_instances": 3
}
주의 기존 앱을 Horizontal Scaling으로 업그레이드할 수 없습니다. 새 앱을 생성해야 합니다. 템플릿 기반 생성도 미지원이며, API를 통해 직접 생성해야 합니다.

Git 기반 배포 (Public Preview)

소스 코드를 수동으로 업로드하는 대신, Git 레포지토리에서 직접 배포 할 수 있습니다.

주요 기능

기능설명
Git 레퍼런스브랜치, 태그, 커밋 해시 지정 가능
소스 코드 경로레포 내 특정 디렉토리 지정 가능
강제 적용워크스페이스 관리자가 Git-only 배포를 강제할 수 있음
# Git 기반 배포 (CLI)
databricks apps deploy my-app \
  --git-url https://github.com/myorg/myrepo \
  --git-ref main \
  --source-code-path ./app
참고 GitHub Enterprise Managed Users(EMU)의 개인 레포는 OAuth 제한으로 인해 PAT를 대안으로 사용해야 합니다.

앱 텔레메트리 / 모니터링 (Beta)

OpenTelemetry 기반으로 앱의 로그, 스팬, 메트릭을 Unity Catalog에 자동 수집할 수 있습니다.

설정 방법

app.yaml에 텔레메트리 설정을 추가하면 앱 코드 변경 없이 자동으로 계측됩니다:
command: ['streamlit', 'run', 'app.py']
# 텔레메트리 내보내기 설정 (app.yaml 또는 앱 설정 UI에서)
참고 텔레메트리 데이터는 near real-time으로 Unity Catalog 시스템 테이블에 저장됩니다. 앱 성능 모니터링, 오류 추적, 사용 패턴 분석에 활용할 수 있습니다.

uv 의존성 관리 (2026년 3월~)

requirements.txt 대신 uv 를 사용하여 Python 의존성을 관리할 수 있습니다.
# pyproject.toml
[project]
name = "my-databricks-app"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
    "streamlit>=1.32.0",
    "databricks-sdk>=0.20.0",
    "pandas>=2.2.0",
]
# uv.lock 생성
uv lock
참고 uv vs pip: uv는 pip보다 최대 10-100배 빠른 의존성 해결과 설치를 제공합니다. pyproject.toml + uv.lock으로 전환하면 배포 시간이 단축됩니다. 기존 requirements.txt도 계속 지원됩니다.

Lakebase (PostgreSQL) 연동

Lakebase 는 Databricks가 관리하는 PostgreSQL 호환 데이터베이스입니다. Databricks Apps에서 OLTP(트랜잭션) 워크로드 를 처리할 때 적합합니다.

왜 Lakebase인가?

비교 항목Unity Catalog 테이블Lakebase
접근 방식SQL Warehouse를 통한 분석 쿼리직접 PostgreSQL 연결
지연 시간수백 ms~수 초 (Warehouse 오버헤드)수 ms~수십 ms
용도분석, 대시보드, 배치 처리CRUD, 세션 관리, 실시간 데이터
트랜잭션제한적완전한 ACID 트랜잭션
스키마Delta Lake (읽기 최적화)PostgreSQL (쓰기/읽기 균형)

Lakebase 리소스 설정

app.yaml:
command: ['python', 'app.py']
env:
  - name: DB_CONNECTION
    valueFrom: lakebase_db
resources:
  - name: lakebase_db
    type: postgres

필수 패키지

# requirements.txt
databricks-sdk
psycopg2-binary    # PostgreSQL 드라이버

Python 연결 예시

import os
import psycopg2

# Lakebase 연결 정보 가져오기
db_connection = os.getenv("DB_CONNECTION")

# PostgreSQL 호환 연결
conn = psycopg2.connect(db_connection)

# CRUD 작업
with conn.cursor() as cur:
    # 테이블 생성
    cur.execute("""
        CREATE TABLE IF NOT EXISTS app_sessions (
            id SERIAL PRIMARY KEY,
            user_id VARCHAR(255),
            data JSONB,
            created_at TIMESTAMP DEFAULT NOW()
        )
    """)

    # 데이터 삽입
    cur.execute(
        "INSERT INTO app_sessions (user_id, data) VALUES (%s, %s)",
        ("user123", '{"page": "dashboard", "action": "view"}')
    )
    conn.commit()

    # 데이터 조회
    cur.execute("SELECT * FROM app_sessions WHERE user_id = %s", ("user123",))
    rows = cur.fetchall()

conn.close()

Streamlit + Lakebase 예시

import os
import streamlit as st
import psycopg2

@st.cache_resource
def get_db_connection():
    """Lakebase 연결을 캐싱하여 재사용합니다."""
    return psycopg2.connect(os.getenv("DB_CONNECTION"))

conn = get_db_connection()

st.title("피드백 수집 앱")

# 피드백 입력 폼
with st.form("feedback_form"):
    name = st.text_input("이름")
    feedback = st.text_area("피드백")
    rating = st.slider("만족도", 1, 5, 3)
    submitted = st.form_submit_button("제출")

    if submitted:
        with conn.cursor() as cur:
            cur.execute(
                "INSERT INTO feedback (name, feedback, rating) VALUES (%s, %s, %s)",
                (name, feedback, rating)
            )
            conn.commit()
        st.success("피드백이 저장되었습니다!")

# 피드백 목록 조회
with conn.cursor() as cur:
    cur.execute("SELECT name, feedback, rating, created_at FROM feedback ORDER BY created_at DESC LIMIT 20")
    rows = cur.fetchall()

if rows:
    import pandas as pd
    df = pd.DataFrame(rows, columns=["이름", "피드백", "만족도", "작성일"])
    st.dataframe(df)
주의 OAuth 토큰 로테이션: Lakebase 연결은 OAuth 역할 인증을 사용하며, 토큰은 1시간 후 만료 됩니다. 장기 실행 앱에서는 자동 토큰 갱신을 구현하거나, 연결 풀에서 주기적으로 연결을 재생성해야 합니다. 토큰이 만료되면 연결이 끊어집니다.

Lakebase 사용 시나리오

시나리오왜 Lakebase가 적합한가
사용자 설정 저장빠른 읽기/쓰기, 세션 간 영속성
피드백/설문 수집ACID 트랜잭션으로 데이터 무결성 보장
채팅 이력 저장실시간 쓰기, 사용자별 격리
앱 메타데이터설정, 상태, 캐시 등 앱 내부 데이터
큐/태스크 관리작업 큐, 상태 추적, 워크플로우
참고 Lakebase vs UC 테이블 선택 기준: “앱 내부에서만 사용하는 운영 데이터”는 Lakebase, “분석이나 ML에 활용할 데이터”는 UC 테이블에 저장하세요. 물론 Lakebase의 데이터를 Delta Live Tables(DLT)로 UC 테이블에 동기화하는 것도 가능합니다.

Databricks Asset Bundles (DABs) 배포

Databricks Asset Bundles (DABs) 를 사용하면 앱을 Infrastructure as Code로 관리하고, CI/CD 파이프라인과 통합할 수 있습니다.

DABs 프로젝트 구조

my-app-bundle/
├── databricks.yml          # Bundle 설정 파일
├── src/
   ├── app.py              # 앱 코드
   ├── app.yaml            # 앱 런타임 설정
   └── requirements.txt    # 의존성
└── resources/
    └── my_app.yml           # 앱 리소스 정의

databricks.yml 예시

bundle:
  name: my-dashboard-app

workspace:
  host: https://<workspace-url>

targets:
  dev:
    mode: development
    default: true
    workspace:
      host: https://<dev-workspace-url>

  prod:
    mode: production
    workspace:
      host: https://<prod-workspace-url>

include:
  - resources/*.yml

앱 리소스 정의 (resources/my_app.yml)

resources:
  apps:
    my_dashboard_app:
      name: "my-dashboard-app"
      description: "매출 분석 대시보드"
      source_code_path: ../src
      config:
        command:
          - streamlit
          - run
          - app.py
        env:
          - name: DATABRICKS_WAREHOUSE_ID
            value: ${var.warehouse_id}
      resources:
        - name: sql_warehouse
          sql_warehouse:
            id: ${var.warehouse_id}
            permission: CAN_USE
      permissions:
        - level: CAN_MANAGE
          user_name: admin@company.com
        - level: CAN_USE
          group_name: data-team

DABs 배포 명령

# Bundle 유효성 검사
databricks bundle validate -t dev

# 개발 환경에 배포
databricks bundle deploy -t dev

# 프로덕션 환경에 배포
databricks bundle deploy -t prod

# 배포 상태 확인
databricks bundle summary -t dev

DABs의 장점

장점설명
환경 분리targets로 dev/staging/prod 환경을 코드로 관리
변수 치환${var.warehouse_id}로 환경별 설정 자동 적용
CI/CD 통합GitHub Actions, Azure DevOps 등과 자연스러운 연동
버전 관리Git으로 인프라 설정 포함 전체 이력 관리
권한 관리permissions로 앱 접근 권한을 코드로 선언
참고 DABs vs CLI 직접 배포: 소규모 프로젝트나 프로토타이핑에서는 databricks apps deploy CLI가 더 빠릅니다. 프로덕션 환경이나 여러 환경에 배포하는 경우에는 DABs가 훨씬 효율적입니다.

AppKit (React + TypeScript 프레임워크)

AppKit 은 Databricks가 공식으로 제공하는 React + TypeScript 기반 풀스택 프레임워크입니다. 타입 안전한 SQL 쿼리, 내장 차트/테이블 컴포넌트, tRPC 기반 API를 제공합니다.

AppKit vs 다른 프레임워크

비교 항목StreamlitAppKit (React)
언어PythonTypeScript/React
UI 커스터마이징제한적 (위젯 기반)완전한 자유도 (컴포넌트 기반)
타입 안전성없음SQL 쿼리 결과까지 타입 자동 생성
차트Streamlit 내장ECharts 기반 (고성능)
API 레이어없음tRPC (타입 안전 RPC)
빌드불필요Vite 기반 빌드
학습 곡선낮음중간~높음
적합 대상빠른 프로토타입, 데이터 앱프로덕션 SPA, 복잡한 UI

AppKit 프로젝트 생성

# 1. 매니페스트 확인 (사용 가능한 플러그인과 리소스 확인)
databricks apps manifest --profile <PROFILE>

# 2. 프로젝트 생성
databricks apps init \
  --name my-analytics-app \
  --features analytics \
  --set analytics.sql-warehouse.id=<WAREHOUSE_ID> \
  --description "매출 분석 대시보드" \
  --run none \
  --profile <PROFILE>

# 3. 의존성 설치 및 개발 서버 실행
cd my-analytics-app
npm install
npm run dev

AppKit 프로젝트 구조

my-analytics-app/
├── client/
│   ├── src/
│   │   ├── App.tsx              # 메인 React 컴포넌트
│   │   ├── appKitTypes.d.ts     # 자동 생성된 SQL 쿼리 타입
│   │   └── main.tsx
│   └── vite.config.ts
├── server/
│   └── server.ts                # tRPC 라우터 (백엔드)
├── config/
│   └── queries/
│       └── sales_by_region.sql  # SQL 쿼리 파일
├── tests/
│   └── smoke.spec.ts            # 스모크 테스트
├── app.yaml                     # 배포 설정
└── package.json

SQL 쿼리 + 차트 연동

1. SQL 쿼리 파일 작성:
-- config/queries/sales_by_region.sql
SELECT
  region,
  SUM(revenue) as total_revenue,
  COUNT(*) as order_count
FROM catalog.schema.sales
WHERE order_date >= :start_date
  AND order_date <= :end_date
GROUP BY region
ORDER BY total_revenue DESC
2. 타입 생성:
npm run typegen
# ✓ sales_by_region — 타입 생성 완료
3. React 컴포넌트에서 사용:
// client/src/App.tsx
import { BarChart, DataTable } from '@databricks/appkit-ui/react';
import { useAnalyticsQuery } from '@databricks/appkit-ui/react';
import { sql } from '@databricks/appkit-ui/js';
import { useState } from 'react';

function App() {
  const [startDate, setStartDate] = useState('2024-01-01');
  const [endDate, setEndDate] = useState('2024-12-31');

  const params = {
    start_date: sql.date(startDate),
    end_date: sql.date(endDate),
  };

  // KPI 카드용 데이터 조회
  const { data } = useAnalyticsQuery('sales_by_region', params);

  const totalRevenue = data
    ? data.reduce((sum, row) => sum + Number(row.total_revenue), 0)
    : 0;

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">매출 분석 대시보드</h1>

      {/* KPI 카드 */}
      <div className="grid grid-cols-3 gap-4 mb-8">
        <div className="p-4 border rounded">
          <p className="text-sm text-gray-500">총 매출</p>
          <p className="text-2xl font-bold">
            ${Number(totalRevenue).toLocaleString()}
          </p>
        </div>
      </div>

      {/* 지역별 매출 차트 */}
      <BarChart
        queryKey="sales_by_region"
        parameters={params}
        xKey="region"
        yKey="total_revenue"
        colors={['#FF3621']}
        height={400}
      />

      {/* 데이터 테이블 */}
      <DataTable
        queryKey="sales_by_region"
        parameters={params}
      />
    </div>
  );
}

export default App;

tRPC로 Model Serving 호출

// server/server.ts
import { initTRPC } from '@trpc/server';
import { getExecutionContext } from '@databricks/appkit';
import { z } from 'zod';

const t = initTRPC.create();

export const appRouter = t.router({
  askAgent: t.procedure
    .input(z.object({ question: z.string() }))
    .mutation(async ({ input }) => {
      const { serviceDatabricksClient: client } = getExecutionContext();

      const response = await client.servingEndpoints.query({
        name: 'my-agent-endpoint',
        messages: [{ role: 'user', content: input.question }],
      });

      return {
        answer: response.choices[0].message.content,
      };
    }),
});

AppKit 배포

# 유효성 검사
databricks apps validate --profile <PROFILE>

# 배포
databricks apps deploy --profile <PROFILE>
주의 AppKit 개발 워크플로우: 반드시 SQL 파일 작성 → npm run typegen → App.tsx 작성 순서를 지켜야 합니다. 타입 생성 전에 UI 코드를 작성하면 컴파일 에러가 발생합니다.

Node.js/React 앱 (수동 구성)

AppKit을 사용하지 않고 Node.js/React 앱을 직접 구성할 수도 있습니다.

프로젝트 구조

my-react-app/
├── app.yaml
├── package.json
├── server.js                 # Express/Node.js 서버
├── build/                    # React 빌드 결과물
   ├── index.html
   └── static/
└── src/                      # React 소스 (빌드 전)
    ├── App.jsx
    └── index.js

app.yaml (Node.js)

command: ['npm', 'run', 'start']
env:
  - name: DATABRICKS_WAREHOUSE_ID
    valueFrom: sql_warehouse
  - name: NODE_ENV
    value: 'production'
resources:
  - name: sql_warehouse
    type: sql-warehouse

package.json

{
  "name": "my-react-app",
  "version": "1.0.0",
  "scripts": {
    "start": "node server.js",
    "build": "react-scripts build"
  },
  "dependencies": {
    "@databricks/sql": "^1.8.0",
    "express": "^4.18.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  }
}

server.js (Express)

const express = require('express');
const path = require('path');
const { DBSQLClient } = require('@databricks/sql');

const app = express();
const port = process.env.DATABRICKS_APP_PORT || 3000;

// React 빌드 결과물 서빙
app.use(express.static(path.join(__dirname, 'build')));
app.use(express.json());

// API 엔드포인트
app.get('/api/data', async (req, res) => {
  try {
    const client = new DBSQLClient();
    const connection = await client.connect({
      authType: 'databricks-oauth',
      host: process.env.DATABRICKS_HOST,
      path: `/sql/1.0/warehouses/${process.env.DATABRICKS_WAREHOUSE_ID}`,
      oauthClientId: process.env.DATABRICKS_CLIENT_ID,
      oauthClientSecret: process.env.DATABRICKS_CLIENT_SECRET,
    });

    const session = await connection.openSession();
    const result = await session.executeStatement(
      'SELECT * FROM catalog.schema.table LIMIT 100'
    );
    const rows = await result.fetchAll();

    await result.close();
    await session.close();
    await connection.close();

    res.json({ data: rows });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// SPA fallback
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.listen(port, '0.0.0.0', () => {
  console.log(`Server running on port ${port}`);
});
참고 AppKit vs 수동 구성: AppKit은 SQL 타입 생성, 내장 차트, tRPC 등을 제공하므로 새 프로젝트에서는 AppKit을 권장합니다. 기존 React 앱을 마이그레이션하거나 특수한 요구사항이 있는 경우에만 수동 구성을 사용하세요.

참고 자료