> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-vortex-format.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# ClickHouse Managed Postgres에서 Bun 사용하기

> Bun에 내장된 Bun.sql 클라이언트와 인증서 검증 TLS를 사용하여 Bun 애플리케이션을 ClickHouse Managed Postgres에 연결합니다

export const BetaBadge = ({link, galaxyTrack, galaxyEvent}) => {
  if (link) {
    return <a href={link} target="_blank" rel="noopener noreferrer" className="betaBadge" onClick={galaxyTrack && galaxyEvent ? galaxyOnClick(galaxyEvent) : undefined}>
                <span>베타</span>
            </a>;
  }
  return <a href="https://clickhouse.com/docs/reference/settings/beta-and-experimental-features#beta-features" className="betaBadge">
            <span>베타 기능</span>
        </a>;
};

<BetaBadge link="https://clickhouse.com/cloud/postgres" galaxyTrack={true} galaxyEvent="docs.managed-postgres.guides-bun-beta" />

[Bun](https://bun.sh)은 Postgres 클라이언트인 `Bun.sql`을 기본으로 내장한 JavaScript 및 TypeScript 런타임으로, 별도의 드라이버를 설치할 필요가 없습니다.
이 가이드에서는 검증된 TLS 연결로 Bun을 ClickHouse Managed Postgres에 연결한 뒤 테이블을 생성하고, 쿼리와 트랜잭션을 실행하고, `Bun.serve`를 사용하여 HTTP로 행을 제공하는 방법을 설명합니다.

<h2 id="prerequisites">
  사전 요구 사항
</h2>

* [Bun](https://bun.sh/docs/installation) 1.4 이상. 이 가이드는 Bun 1.4.2 및 Postgres 18 환경에서 테스트되었습니다.
* ClickHouse Cloud 계정
* 데이터베이스 생성에 사용할 [`psql`](https://www.postgresql.org/download/). [SQL 콘솔](/ko/integrations/connectors/sql-clients/sql-console)에서 `CREATE DATABASE` 문을 실행해도 됩니다.

<h2 id="create-service">
  ClickHouse Managed Postgres 서비스 생성
</h2>

ClickHouse Cloud 콘솔에서 **New service**를 클릭한 후 **Postgres**를 선택하십시오. 몇 분 내에 인스턴스가 준비됩니다. 자세한 단계별 안내는 [빠른 시작 가이드](/ko/products/managed-postgres/quickstart)를 참조하십시오.

<h2 id="connection-details">
  연결 정보 확인하기
</h2>

서비스를 열고 왼쪽 사이드바에서 **Connect**를 클릭하십시오. **Directly**가 선택된 상태를 그대로 두고 **Use SSL**을 켜십시오. 이제 URL 끝에 `sslmode=verify-full&sslrootcert=...`가 붙습니다. **Download CA certificate**를 클릭하여 인스턴스의 CA 인증서를 다운로드하십시오.

이 가이드에서는 포트 `5432`로 **직접** 연결합니다. `Bun.serve` 앱은 장기 실행 프로세스이고 `Bun.sql`이 자체 연결 풀을 관리하므로 서버 측 pooler가 필요하지 않습니다. 수명이 짧은 Bun 프로세스를 다수 실행한다면 [PgBouncer를 통해 연결하기](#pgbouncer)를 참조하십시오.

<h2 id="setup">
  프로젝트 설정
</h2>

프로젝트를 생성하십시오.

```bash theme={null}
mkdir bun-postgres && cd bun-postgres
bun init -y
```

<Tip>
  **기존 앱에 추가하시나요?**

  `Bun.sql`은 Bun에 내장되어 있어 따로 설치할 것이 없으므로 `mkdir`와 `bun init -y` 단계는 건너뛰세요. 프로젝트 루트에서 [연결 구성](#configure)부터 이어서 진행하고, 앱이 서버를 검증할 수 있도록 앱의 시작 스크립트 앞에도 `NODE_EXTRA_CA_CERTS=./ca-certificate.pem`을 붙이세요.
</Tip>

<h2 id="configure">
  연결 구성하기
</h2>

다운로드한 CA 인증서를 프로젝트 디렉터리로 옮기고 파일 이름을 `ca-certificate.pem`으로 변경하세요:

```bash theme={null}
mv ~/Downloads/<service-name>-ca-certificate.pem ca-certificate.pem
```

앱에서 사용할 데이터베이스를 생성하십시오. `<PASSWORD>`와 호스트를 **Connect** 모달에 표시된 값으로 바꾸십시오.

```bash theme={null}
psql "postgresql://postgres:<PASSWORD>@your-instance.pg.clickhouse.cloud:5432/postgres?sslmode=verify-full&sslrootcert=ca-certificate.pem" -c "CREATE DATABASE guide_bun;"
```

```text theme={null}
CREATE DATABASE
```

`guide_bun` 데이터베이스의 연결 URL을 `.env` 파일에 추가하십시오. 파일이 없으면 새로 만드십시오. Bun은 `.env`를 자동으로 로드하며, `bun`에서 export하는 `sql`은 `DATABASE_URL`을 읽습니다:

```bash title=".env" theme={null}
DATABASE_URL=postgresql://postgres:<PASSWORD>@your-instance.pg.clickhouse.cloud:5432/guide_bun?sslmode=verify-full
```

비밀번호에 특수 문자가 포함되어 있으면 URL 인코딩하십시오. `.env` 파일은 버전 관리(version control) 대상에서 제외하십시오.

다음 스크립트를 `package.json`에 추가하십시오. `scripts` 블록이 이미 있다면 해당 블록에 머지하십시오. 이 스크립트는 `NODE_EXTRA_CA_CERTS`를 통해 Bun에 CA 인증서를 전달합니다.

```json title="package.json" theme={null}
"scripts": {
  "setup": "NODE_EXTRA_CA_CERTS=./ca-certificate.pem bun run setup.ts",
  "queries": "NODE_EXTRA_CA_CERTS=./ca-certificate.pem bun run queries.ts",
  "serve": "NODE_EXTRA_CA_CERTS=./ca-certificate.pem bun run server.ts"
}
```

<Note>
  **Bun이 서버 인증서를 검증하는 방식**

  * `sslmode=verify-full`을 지정하면 `Bun.sql`이 인증서 체인과 호스트 이름을 모두 검증합니다.
  * 콘솔 URL에 포함된 `sslrootcert` 매개변수는 복사하지 마십시오. `Bun.sql`은 이 매개변수를 읽지 않고 런타임 설정으로 서버에 그대로 전달하므로, `unrecognized configuration parameter "sslrootcert"` 오류와 함께 연결이 실패합니다.
  * `NODE_EXTRA_CA_CERTS`는 Bun이 시작될 때 환경 변수로 설정되어 있어야 합니다. Bun은 이 값을 `.env`에서 읽지 않으므로 스크립트에서 설정합니다.
  * Bun 1.4.2에서는 다운로드한 인증서를 `SQL`의 `tls: { ca }` 옵션으로 전달하면 `certificate signature failure` 오류가 발생합니다. 대신 `NODE_EXTRA_CA_CERTS`를 사용하십시오.
</Note>

<h2 id="create-table">
  테이블 생성
</h2>

`setup.ts` 파일을 생성하세요. 이 파일은 세션의 TLS 상태를 출력하고 `todos` 테이블을 생성합니다.

```ts title="setup.ts" theme={null}
import { sql } from "bun";

const [tls] = await sql`SELECT ssl, version FROM pg_stat_ssl WHERE pid = pg_backend_pid()`;
console.log("TLS:", tls);

await sql`
  CREATE TABLE IF NOT EXISTS todos (
    id         integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    title      text NOT NULL,
    done       boolean NOT NULL DEFAULT false,
    created_at timestamptz NOT NULL DEFAULT now()
  )
`;
console.log("Table todos is ready");

await sql.close();
```

다음을 실행하십시오:

```bash theme={null}
bun run setup
```

```text theme={null}
$ NODE_EXTRA_CA_CERTS=./ca-certificate.pem bun run setup.ts
TLS: {
  ssl: true,
  version: "TLSv1.3",
}
Table todos is ready
```

인증서가 실제로 검증되는지 확인하려면 CA 인증서 없이 파일을 실행하십시오. 다음과 같이 연결이 거부됩니다:

```bash theme={null}
bun run setup.ts
```

```text theme={null}
error: unable to verify the first certificate
 errno: 0,
  code: "UNABLE_TO_VERIFY_LEAF_SIGNATURE"
```

<h2 id="query">
  데이터베이스 쿼리하기
</h2>

`Bun.sql`은 태그드 템플릿(tagged template)을 사용합니다. 보간되는 모든 값은 바인드 매개변수(bind parameter)로 전송되므로 사용자 입력을 그대로 전달해도 안전합니다. `queries.ts` 파일을 생성하세요:

```ts title="queries.ts" theme={null}
import { sql } from "bun";

// Interpolated values are sent as bind parameters, never spliced into the SQL text
const title = "Download the CA certificate";
const [first] = await sql`INSERT INTO todos (title, done) VALUES (${title}, true) RETURNING *`;
console.log("Inserted:", first);

// Insert several rows at once with the sql(array) helper
const rows = [{ title: "Connect with Bun.sql" }, { title: "Serve rows with Bun.serve" }];
await sql`INSERT INTO todos ${sql(rows)}`;

// Both statements commit together, or neither does
await sql.begin(async (tx) => {
  const [todo] = await tx`INSERT INTO todos (title) VALUES ('Write a transaction') RETURNING id`;
  await tx`UPDATE todos SET done = true WHERE id = ${todo.id}`;
});

const open = await sql`SELECT id, title FROM todos WHERE done = ${false} ORDER BY id`;
console.log("Open todos:", open);

await sql.close();
```

`sql.begin`은 콜백(callback)을 트랜잭션(transaction) 안에서 실행합니다. 콜백이 정상적으로 반환되면 커밋하고, 예외가 발생하면 롤백합니다.

다음과 같이 실행합니다.

```bash theme={null}
bun run queries
```

```text theme={null}
$ NODE_EXTRA_CA_CERTS=./ca-certificate.pem bun run queries.ts
Inserted: {
  id: 1,
  title: "Download the CA certificate",
  done: true,
  created_at: 2026-09-30T16:51:23.040Z,
}
Open todos: [
  {
    id: 2,
    title: "Connect with Bun.sql",
  }, {
    id: 3,
    title: "Serve rows with Bun.serve",
  }, count: 2, command: "SELECT", lastInsertRowid: null,
  affectedRows: null
]
```

<h2 id="serve">
  HTTP로 행 제공하기
</h2>

행을 조회하고 생성하는 `/todos` 엔드포인트를 포함한 `server.ts` 파일을 생성하세요.

```ts title="server.ts" theme={null}
import { sql } from "bun";

const server = Bun.serve({
  port: 3106,
  routes: {
    "/todos": {
      GET: async () => {
        const todos = await sql`SELECT id, title, done, created_at FROM todos ORDER BY id`;
        return Response.json(todos);
      },
      POST: async (req) => {
        const { title } = await req.json();
        const [todo] = await sql`INSERT INTO todos (title) VALUES (${title}) RETURNING *`;
        return Response.json(todo, { status: 201 });
      },
    },
  },
});

console.log(`Listening on ${server.url}`);
```

서버를 시작합니다:

```bash theme={null}
bun run serve
```

```text theme={null}
$ NODE_EXTRA_CA_CERTS=./ca-certificate.pem bun run server.ts
Listening on http://localhost:3106/
```

다른 터미널에서 행을 하나 추가합니다:

```bash theme={null}
curl -X POST http://localhost:3106/todos -H 'Content-Type: application/json' -d '{"title": "Take a screenshot"}'
```

```json theme={null}
{"id":5,"title":"Take a screenshot","done":false,"created_at":"2026-09-30T16:51:30.372Z"}
```

<h2 id="verify">
  확인
</h2>

브라우저에서 [http://localhost:3106/todos](http://localhost:3106/todos)를 열면 모든 행을 확인할 수 있습니다. 응답은 다음과 같습니다(가독성을 위해 서식을 정리함).

```json theme={null}
[
  {"id":1,"title":"Download the CA certificate","done":true,"created_at":"2026-09-30T16:51:23.040Z"},
  {"id":2,"title":"Connect with Bun.sql","done":false,"created_at":"2026-09-30T16:51:23.136Z"},
  {"id":3,"title":"Serve rows with Bun.serve","done":false,"created_at":"2026-09-30T16:51:23.136Z"},
  {"id":4,"title":"Write a transaction","done":true,"created_at":"2026-09-30T16:51:23.184Z"},
  {"id":5,"title":"Take a screenshot","done":false,"created_at":"2026-09-30T16:51:30.372Z"}
]
```

콘솔에서 **SQL 콘솔**을 열고 **guide\_bun**, **public**, **todos**를 차례로 펼치면 동일한 행을 확인할 수 있습니다.

| id | title | done | created\_at |
| - | - | - | - |
| 1 | Download the CA certificate | true | 2026-09-30 16:51:23.04083+00 |
| 2 | Connect with Bun.sql | false | 2026-09-30 16:51:23.136975+00 |
| 3 | Serve rows with Bun.serve | false | 2026-09-30 16:51:23.136975+00 |
| 4 | Write a transaction | true | 2026-09-30 16:51:23.184716+00 |
| 5 | Take a screenshot | false | 2026-09-30 16:51:30.372292+00 |

<h2 id="pgbouncer">
  PgBouncer를 통해 연결하기
</h2>

수명이 짧은 Bun 프로세스를 많이 실행한다면 함께 제공되는 [PgBouncer](/ko/products/managed-postgres/connection#pgbouncer)를 통해 연결하십시오. **Connect** 모달에서 **via PgBouncer**를 선택하고 `DATABASE_URL`의 포트를 `6432`로 변경하십시오. CA 인증서와 `sslmode=verify-full` 설정은 그대로 사용합니다.

PgBouncer는 트랜잭션 풀링(transaction pooling) 모드로 실행됩니다. `Bun.sql`은 기본적으로 이름이 지정된 prepared statement를 사용하며, 자체 테스트에서는 PgBouncer를 거쳐도 오류 없이 동작했습니다. `prepared statement does not exist` 오류가 발생하면 `prepare: false`로 클라이언트를 생성하십시오:

```ts theme={null}
import { SQL } from "bun";

const sql = new SQL(process.env.DATABASE_URL!, { prepare: false });
```

`prepare`는 URL에 포함하지 말고 옵션으로 전달하십시오. `sslrootcert`와 마찬가지로 `prepare=false` URL 매개변수는 서버로 그대로 전달되어 거부됩니다.

<h2 id="next-steps">
  다음 단계
</h2>

* [연결](/ko/products/managed-postgres/connection): 연결 문자열, PgBouncer 및 TLS
* [설정](/ko/products/managed-postgres/settings): Postgres 및 PgBouncer 매개변수 변경
* [읽기 레플리카(Read replicas)](/ko/products/managed-postgres/read-replicas): 읽기 성능 확장
* [Bun SQL 문서](https://bun.sh/docs/runtime/sql): `Bun.sql` API 전체 참조
