Quack 원격 프로토콜

Quack 원격 프로토콜 (Quack Remote Protocol)

Quack 확장은 DuckDB 인스턴스를 서버로 바꿔서, 다른 DuckDB 인스턴스(클라이언트)가 HTTP로 연결할 수 있게 해줘요. 오늘은 Quack 프로토콜의 개요와 서버/클라이언트 양쪽의 기본 사용법을 함께 살펴볼까요?

출처: 문서

본문

Quack은 2026년 5월 12일에 릴리스됐어요. [발표 블로그 포스트]({% post_url 2026-05-12-quack-remote-protocol %})를 읽어 보세요!

Quack 확장은 DuckDB 인스턴스를 서버로 바꿔서, 다른 DuckDB 인스턴스(클라이언트)가 HTTP로 연결할 수 있게 해줘요.

이 페이지는 프로토콜을 한눈에 다루고 양쪽 편(wire)의 기본 사용법을 살펴봐요. 함수, 설정, 로깅 노브의 전체 목록은 [Reference]({% link docs/current/quack/reference.md %})를 참고하세요. TLS와 인증/권한 부여 구성은 [Security]({% link docs/current/quack/security.md %})를 참고하세요. 사용자 가이드는 [Guides]({% link docs/current/quack/setup/overview.md %})를 참고하세요.

경고 Quack은 활발히 개발 중이며 프로토콜, 함수 이름, 설정, 기본값은 여전히 변경될 수 있어요. 이 페이지는 core 저장소를 통해 배포되는 [DuckDB v1.5.3]({% post_url 2026-05-20-announcing-duckdb-153 %})에서 사용할 수 있는 Quack의 베타 릴리스를 문서화해요.

Quack 한눈에 보기 (Quack in a Nutshell)

요약하면, Quack 프로토콜과 그 상호작용은 다음과 같이 동작해요:

  • HTTP 기반. Quack 메시지는 리버스 프록시를 통해 일반 HTTP나 HTTPS로 전송돼요 ([Security]({% link docs/current/quack/security.md %}) 참고). 즉 표준 로드 밸런서, 방화벽, 리버스 프록시가 Quack 트래픽을 다른 어떤 HTTP 서비스와 똑같이 처리해요. 운영할 커스텀 와이어 전송이 없어요.
  • 클라이언트 주도의 요청과 응답. 모든 상호작용은 클라이언트가 시작해요. 서버는 푸시로 상호작용을 시작하지 않아요.
  • application/duckdb 직렬화. 요청과 응답은 DuckDB의 내부 직렬화 프리미티브로 인코딩돼요 ([Write-Ahead Log]({% post_url 2024-10-30-analytics-optimized-concurrent-transactions %}#write-ahead-logging-and-checkpointing)가 사용하는 것과 같은 코드 경로). 이는 데이터가 인터체인지 포맷을 오가며 왕복하는 것을 피하고, 복잡한 타입(중첩, decimal, interval, ...)을 와이어에서 손실 없이 유지해 줘요.
  • 쿼리당 단일 왕복(single round-trip). 초기 연결 핸드셰이크 후에는 쿼리에 요청-응답 한 쌍만 필요해요. 큰 결과는 후속 FETCH 요청을 통해 청크로 스트리밍되고, 선택적으로 여러 스레드에서 병렬화돼요.
  • 기본 포트: 9494. 모든 URI는 quack: 스킴을 사용해요, 예: quack:hostname:port이며 포트는 기본적으로 9494예요.

서버 측 사용법 (Server-Side Usage)

서버 시작 (Starting a Server)

서버는 기존 DuckDB 세션에서 시작돼요. 세션이 볼 수 있는 모든 것(인메모리 테이블, 연결된 파일, 스키마)이 원격 프로토콜을 통해 접근 가능해져요.

localhost에서 수신을 시작하려면 다음을 실행하세요:

CALL quack_serve('quack:localhost');

quack_serve는 수신 URI, HTTP URL, 그리고 기본 인증 함수를 사용할 때는 클라이언트가 연결에 필요한 auth_token을 반환해요. 이 토큰은 시작 전에 명시적으로 설정할 수도 있어요 ([Security]({% link docs/current/quack/security.md %}) 참고).

기본적으로 서버는 localhost 호스트네임 외의 것에 바인딩하는 것을 거부해요. 외부에서 연결 가능한 주소에서 수신하려면 allow_other_hostname => true를 전달하세요:

CALL quack_serve('quack:0.0.0.0:9494', allow_other_hostname => true);

이렇게 할 때는 TLS를 종료하는 리버스 프록시로 서버를 앞세워야 해요. [Securing Quack with a Reverse Proxy]({% link docs/current/quack/setup/reverse_proxy.md %})를 참고하세요.

URI 형식 (URI Format)

Quack 엔드포인트는 quack: URI 스킴과 기본 포트 9494를 사용해요. 몇 가지 예:

URI Host Port Comment
quack:localhost localhost 9494
quack://localhost localhost 9494
quack:myhost:9000 myhost 9000
quack:127.0.0.1 127.0.0.1 9494
quack:[::1]:1234 ::1 1234 (IPv6)

URI를 quack_uri_parser(uri, ssl) 스칼라 함수로 파싱하고 검증할 수 있어요.

서버 중지 (Stopping a Server)

서버를 중지하려면 다음을 실행하세요:

CALL quack_stop('quack:localhost');

클라이언트 측 사용법 (Client-Side Usage)

Quack 서버와 통신하는 두 가지 방법이 있어요:

  1. quack_query(uri, query): 무상태 쿼리.
  2. ATTACH 'quack:host' AS name: 원격을 전체 카탈로그로 연결.

두 경우 모두 인증이 필요해요.

클라이언트는 로컬 URI(localhost, 127.0.0.1, ::1)에는 일반 HTTP를 자동으로 선택하고, 그 외에는 HTTPS를 선택해요. 두 기본값 모두 DISABLE_SSL 구성 옵션으로 재정의할 수 있어요.

quack_query로 무상태 쿼리 (Stateless Queries with quack_query)

서버를 연결하지 않고도 서버에 어떤 SQL이든 실행할 수 있어요. 로컬 데이터베이스를 HTTP로 쿼리하려면:

FROM quack_query(
    'quack:localhost',
    'SELECT 42',
    token = '⟨MY_QUACK_TOKEN_01234567890ABCDEF⟩');

원격 데이터베이스는 기본적으로 HTTPS를 사용해요. 일반 HTTP 원격에서 재정의하려면:

FROM quack_query(
    'quack:remote.com',
    'SELECT 42',
    token = '⟨MY_QUACK_TOKEN_01234567890ABCDEF⟩',
    disable_ssl => true
);

쿼리는 원격에서 실행되고 서버가 결과를 다시 스트리밍해요. 서버에서 발생하는 오류(파싱 오류, 테이블 없음 등)는 DuckDB 클라이언트에 로컬로 표시돼요.

원격 데이터베이스 연결 (Attaching a Remote Database)

로컬 데이터베이스를 HTTP로 연결하려면 간단히 실행하세요:

ATTACH 'quack:localhost' AS remote_db (
    TOKEN '⟨MY_QUACK_TOKEN_01234567890ABCDEF⟩'
);

원격 데이터베이스 연결은 기본적으로 HTTPS를 사용해요. 일반 HTTP 원격에서 재정의하려면:

ATTACH 'quack:remote.com' AS remote_db (
    TOKEN '⟨MY_QUACK_TOKEN_01234567890ABCDEF⟩',
    DISABLE_SSL true
);

연결되면 원격 테이블은 로컬 테이블처럼 보이고 동작해요:

CREATE TABLE remote_db.t AS FROM range(10) r(i);  -- DDL on remote
INSERT INTO remote_db.t VALUES (42);              -- remote writes

원격 데이터베이스에 대해 쿼리를 실행할 수 있어요:

FROM remote_db.t;              -- scan remote table
FROM remote_db.t WHERE i = 42; -- run filter remotely
BEGIN; ...; COMMIT;            -- transactions are forwarded
DETACH quack;                  -- detach from the remote database

연결된 카탈로그는 그 연결 범위로 한정된 임시 SQL을 위한 query 테이블 매크로도 노출해요:

FROM remote_db.query('SELECT 42');

인증 (Authentication)

클라이언트는 다음 두 가지 방법 중 하나로 서버에 인증 토큰을 전달해요: 서버 URI 범위의 quack secret, 또는 ATTACH/quack_query의 명시적 TOKEN 옵션. 전체 그림은 [Security]({% link docs/current/quack/security.md %})를 참고하세요.

서버 URI 범위의 [secret]({% link docs/current/configuration/secrets_manager.md %})을 사용하는 것을 권장해요:

CREATE SECRET (
    TYPE quack,
    TOKEN '⟨MY_QUACK_TOKEN_01234567890ABCDEF⟩',
    SCOPE 'quack:localhost'
);

ATTACH 'quack:localhost' AS remote_db (TYPE quack);

또는 토큰을 직접 전달할 수도 있는데, 이는 일치하는 secret을 재정의해요:

ATTACH 'quack:localhost' AS remote_db (
    TOKEN '⟨MY_QUACK_TOKEN_01234567890ABCDEF⟩'
);

HTTP 연결 캐싱 (HTTP Connection Caching)

기본적으로 각 Quack 클라이언트 요청은 서버에 새 연결을 열어요 — 새 TCP와, SSL을 쓰면 비용이 큰 TLS 핸드셰이크. 연결 캐싱은 요청 간에 연결을 재사용해서 반복 요청에서 쿼리당 레이턴시를 줄여 줘요:

SET httpfs_connection_caching = true;

노드 아이덴티티 (whoami)

각 Quack 노드는 기본 아이덴티티와 런타임 정보를 표시하는 whoami() 테이블 매크로를 노출해요. 서버 플릿에 프록시를 걸 때나 로그를 상호 연관시킬 때 유용해요:

FROM remote_db.query('FROM whoami()');
┌─────────┬──────────┬──────────┬─────────┬─────────────────┬───────────────────────────────┬────────────────────────────────────────────────────┐
│  name   │ provider │ hostname │ region  │     uptime      │            ts_now             │                        meta                        │
│ varchar │ varchar  │ varchar  │ varchar │    interval     │   timestamp with time zone    │                        json                        │
├─────────┼──────────┼──────────┼─────────┼─────────────────┼───────────────────────────────┼────────────────────────────────────────────────────┤
│ NULL    │ NULL     │ NULL     │ NULL    │ 00:04:56.832456 │ 2026-05-22 15:59:38.631715+02 │ {"duckdb_version":"v1.5.3","platform":"osx_arm64"} │
└─────────┴──────────┴──────────┴─────────┴─────────────────┴───────────────────────────────┴────────────────────────────────────────────────────┘

아이덴티티 필드는 whoami_* 옵션을 직접 설정하거나 quack_identify 헬퍼를 호출해서 채워져요:

CALL quack_identify(
    name => 'analytics-1',
    provider => 'ec2',
    region => 'eu-west-1',
    meta => '{"role": "worker"}'
);

meta는 자동 계산된 duckdb_versionplatform 키와 병합돼요. 충돌 시 사용자 제공 키가 이겨요. whoami_started_at (ISO 8601 타임스탬프)가 업타임 앵커를 재정의하며, 그렇지 않으면 업타임은 확장 로드부터 측정돼요.

더 알아보기 (Learn more)