> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# ClickHouse Managed Postgres에서 Prisma 사용하기

> Prisma ORM으로 TypeScript 앱을 ClickHouse Managed Postgres에 연결하고, Prisma Migrate로 마이그레이션을 실행하며, 인증서 검증을 거친 TLS 연결로 PgBouncer를 통해 쿼리를 수행하는 방법을 설명합니다

[베타](https://clickhouse.com/cloud/postgres)

[Prisma ORM](https://www.prisma.io/orm)은 선언적 스키마, 마이그레이션 도구(Prisma Migrate), 자동 생성되는 타입 안전(type-safe) 클라이언트를 제공하는 TypeScript ORM입니다. 이 가이드에서는 Prisma를 ClickHouse Managed Postgres에 연결하고, 마이그레이션으로 테이블을 생성한 뒤, 스크립트와 간단한 HTTP 엔드포인트에서 해당 테이블을 쿼리합니다.

이 가이드에서는 현재 안정 릴리스인 Prisma ORM 7(`7.10.0`에서 테스트됨)을 사용합니다.

## 사전 요구 사항

* Node.js 20.19, 22.12 또는 24 이상
* ClickHouse Managed Postgres 서비스
* 데이터베이스 생성에 사용할 [`psql`](https://www.postgresql.org/download/). [SQL 콘솔](https://clickhouse.com/docs/ko/integrations/connectors/sql-clients/sql-console.md)에서 `CREATE DATABASE` SQL 문을 실행해도 됩니다.
* macOS 사용 시: Prisma Migrate 실행에 필요한 [Docker](https://docs.docker.com/get-started/get-docker/) ([마이그레이션 실행](#migrate) 참조)

## ClickHouse Managed Postgres 서비스 생성

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

## 연결 정보 확인

서비스의 왼쪽 사이드바에서 **Connect**를 클릭합니다. 모달에 사용자 이름, 비밀번호, 서버, 포트와 함께 바로 사용할 수 있는 연결 문자열(connection string)이 표시됩니다.

Prisma에는 데이터베이스와 통신하는 구성 요소가 두 가지 있으므로, 이 가이드에서는 연결을 두 개 사용합니다.

* **애플리케이션(Prisma Client)** 은 포트 `6432`에서 **PgBouncer를 통해** 연결합니다. PgBouncer가 연결을 풀링하므로 여러 앱 프로세스나 서버리스 인스턴스가 소수의 Postgres 백엔드를 공유할 수 있습니다.
* **Prisma CLI(Prisma Migrate)** 는 포트 `5432`에서 **직접** 연결합니다. Prisma Migrate는 실행되는 동안 세션 수준의 권고 잠금(advisory lock)을 획득합니다. PgBouncer의 트랜잭션 풀링 모드에서는 이 잠금이 풀링된 백엔드에 그대로 남은 채 해당 백엔드가 다른 클라이언트의 요청을 처리할 수 있으며, 그 결과 이후의 마이그레이션이 이 잠금에 막혀 진행되지 않습니다.

모달 상단에서 **Directly**와 **via PgBouncer**를 전환하면 각 포트를 확인할 수 있습니다. 서버와 자격 증명은 두 연결 모두 동일하며, 포트만 다릅니다.

**via PgBouncer**를 선택한 상태에서 **Download CA certificate**를 클릭합니다. **Directly**를 선택한 경우에는 먼저 **Use SSL**을 켜야 버튼이 표시됩니다. 파일 이름은 `<service-name>-ca-certificate.pem`이며, **Settings → CA Certificate**에서도 찾을 수 있습니다. 이 인증서는 인스턴스마다 고유하며, `sslmode=verify-full`로 연결할 때 사용합니다. 이 모드에서는 통신 중인 서버가 실제로 자신의 서버인지 검증합니다.

## 프로젝트 설정

TypeScript 프로젝트를 생성하고 Prisma CLI, Prisma Client, [node-postgres](https://node-postgres.com/) 드라이버 어댑터 및 `dotenv`를 설치하십시오.

```bash theme={null}
mkdir hello-prisma
cd hello-prisma
npm init -y
npm pkg set type=module
npm install typescript tsx @types/node @types/pg --save-dev
npm install prisma@7 --save-dev
npm install @prisma/client@7 @prisma/adapter-pg pg dotenv
```

> [!NOTE]
>
> `prisma@7`과 `@prisma/client@7`을 명시적으로 설치하십시오. 이 문서의 작성 시점 기준으로 `prisma`의 npm `latest` 태그는 Prisma 8 릴리스 후보(release candidate)를 가리키며, 이 버전은 본 가이드와 설정 방식이 다릅니다.

`tsconfig.json` 파일을 생성하세요:

```json theme={null}
{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "bundler",
    "target": "ES2023",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "types": ["node"]
  }
}
```

> [!TIP]
>
> **기존 앱에 추가하는 경우**
>
> `mkdir`, `npm init`, `tsconfig.json` 단계는 건너뛰세요. 프로젝트에서 `npm install tsx @types/pg prisma@7 --save-dev`와 `npm install @prisma/client@7 @prisma/adapter-pg pg dotenv`를 실행한 다음 [Prisma 초기화](#init) 단계부터 진행하세요.

## Prisma 초기화

**1. prisma init 실행**

```bash theme={null}
npx prisma init --datasource-provider postgresql --output ../generated/prisma
```

이 명령어는 `prisma/schema.prisma`와 Prisma 구성 파일인 `prisma7.config.ts`를 생성합니다. 또한 `.env`에는 플레이스홀더 `DATABASE_URL`을, `.gitignore`에는 생성된 클라이언트를 추가하고(해당 파일이 없으면 새로 생성합니다), AI 코딩 에이전트용 Prisma 스킬도 추가합니다. Prisma 7.10 이상에서는 Prisma 8 포맷과 충돌하지 않도록 구성 파일 이름을 `prisma7.config.ts`로 지정합니다.

**2. CA 인증서 추가**

다운로드한 CA 인증서를 프로젝트 루트로 옮긴 후 이름을 `ca-certificate.pem`으로 변경하세요.

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

인증서의 상대 경로는 명령어를 실행하는 디렉터리를 기준으로 해석됩니다. 따라서 이 가이드의 모든 명령어는 프로젝트 루트에서 실행하세요.

## 연결 구성

**1. 데이터베이스 생성**

앱에서 사용할 데이터베이스를 생성합니다. `<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_prisma;"
```

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

**2. 연결 URL 설정**

`.env`에서 `prisma init`이 추가한 플레이스홀더 `DATABASE_URL`을 아래의 연결 URL 2개로 바꾸세요. 두 URL 모두 `guide_prisma` 데이터베이스를 가리킵니다. 비밀번호는 Connect 모달에 표시된 값을 사용하세요:

```bash theme={null}
# Prisma Client(애플리케이션): PgBouncer 경유, 포트 6432
DATABASE_URL="postgresql://postgres:<PASSWORD>@your-instance.pg.clickhouse.cloud:6432/guide_prisma?sslmode=verify-full&sslrootcert=ca-certificate.pem"

# Prisma CLI(마이그레이션): 직접 연결, 포트 5432
DIRECT_URL="postgresql://postgres:<PASSWORD>@your-instance.pg.clickhouse.cloud:5432/guide_prisma?sslmode=require&sslaccept=strict&sslcert=ca-certificate.pem"
```

두 URL을 읽는 드라이버가 서로 다르기 때문에 TLS 매개변수도 서로 다릅니다:

* \*\*`DATABASE_URL`\*\*은 `@prisma/adapter-pg`를 통해 node-postgres가 읽습니다. Connect 모달에 표시된 것과 동일한 표준 매개변수인 `sslmode=verify-full&sslrootcert=...`를 지원합니다.
* \*\*`DIRECT_URL`\*\*은 Prisma CLI의 스키마 엔진이 읽으며, 이 엔진은 자체 매개변수를 사용합니다. `sslcert`는 CA 인증서의 경로를 지정하고, `sslaccept=strict`는 인증서 및 호스트명 검증을 활성화합니다.

> [!WARNING]
>
> Prisma CLI용 URL에는 `sslmode=verify-full&sslrootcert=...`를 사용하지 마십시오. 스키마 엔진은 `sslrootcert`를 무시하며, `sslaccept=strict`가 없으면 서버 인증서를 전혀 검증하지 않습니다. 따라서 연결은 암호화되지만 인증은 이루어지지 않습니다.

**3. Prisma CLI가 직접 연결을 사용하도록 설정**

`prisma7.config.ts`의 내용을 다음과 같이 바꾸세요:

```typescript theme={null}
import "dotenv/config";
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
  },
  datasource: {
    url: env("DIRECT_URL"),
  },
});
```

Prisma 7에서 구성 파일의 URL은 CLI에서만 사용됩니다. Prisma Client는 드라이버 어댑터를 통해 연결하며, 드라이버 어댑터는 [데이터베이스 쿼리](#query)에서 구성합니다.

## 스키마 정의 및 마이그레이션 실행

**1. 모델 추가**

`prisma/schema.prisma` 파일 끝에 `Todo` 모델을 추가합니다.

```prisma theme={null}
model Todo {
  id        Int      @id @default(autoincrement())
  title     String
  done      Boolean  @default(false)
  createdAt DateTime @default(now())
}
```

**2. 마이그레이션 실행**

Linux에서는 Prisma Migrate를 직접 실행합니다.

```bash theme={null}
npx prisma migrate dev --name init
```

macOS에서는 동일한 명령어를 Linux 컨테이너에서 실행합니다.

```bash theme={null}
docker run --rm -it -v "$PWD":/app -w /app node:24 npx prisma migrate dev --name init
```

> [!WARNING]
>
> ClickHouse Managed Postgres는 TLS 1.3만 허용합니다. macOS에서는 Prisma CLI의 스키마 엔진이 시스템 TLS 라이브러리를 사용하는데, 이 라이브러리로는 해당 설정에서 연결할 수 없습니다. 이 경우 `P1011: Error opening a TLS connection: One or more parameters passed to a function were not valid` 오류가 발생하고, `sslcert`를 제거하면 `bad protocol version` 오류가 발생합니다([prisma/orm#29600](https://github.com/prisma/orm/issues/29600)). 이 문제는 `prisma migrate`, `prisma db pull`처럼 데이터베이스에 연결하는 CLI 명령어에만 해당합니다. Prisma Client는 node-postgres를 통해 연결하므로 영향을 받지 않습니다. CLI를 Linux 컨테이너나 Linux 기반 CI 파이프라인에서 실행하면 이 문제를 우회할 수 있습니다.

Prisma는 `prisma/migrations/`에 SQL 마이그레이션을 생성한 뒤 적용합니다.

```text theme={null}
Applying migration `20260930165244_init`

The following migration(s) have been created and applied from new schema changes:

prisma/migrations/
  └─ 20260930165244_init/
    └─ migration.sql

Your database is now in sync with your schema.
```

**3. Prisma Client 생성**

```bash theme={null}
npx prisma generate
```

클라이언트는 `generated/prisma`에 생성됩니다. 이 명령어는 데이터베이스에 연결하지 않으므로 어떤 플랫폼에서든 실행할 수 있습니다.

## 데이터베이스 쿼리

**1. Prisma Client 생성**

`lib/prisma.ts`를 생성하십시오. `PrismaPg` 어댑터는 PgBouncer를 통해 `DATABASE_URL`로 연결합니다:

```typescript theme={null}
import "dotenv/config";
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../generated/prisma/client";

const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL });

export const prisma = new PrismaClient({ adapter });
```

> [!TIP]
>
> **PgBouncer 설정 불필요**
>
> `@prisma/adapter-pg`는 각 쿼리를 이름 없는 prepared statement로 전송하고 `DEALLOCATE`를 실행하지 않으므로 PgBouncer의 트랜잭션 풀링 모드에서도 정상적으로 동작합니다. 풀링된 여러 백엔드에서 서로 다른 쿼리 수백 개가 동시에 실행되더라도 오류 없이 처리합니다. 이전 Prisma 버전에서 사용하던 `pgbouncer=true` URL 매개변수는 필요하지 않으며, 드라이버 어댑터를 사용할 때는 아무런 효과가 없습니다.

**2. CRUD 쿼리 실행**

`script.ts`를 생성하십시오:

```typescript theme={null}
import { prisma } from "./lib/prisma";

async function main() {
  // 생성
  const todo = await prisma.todo.create({
    data: { title: "Connect Prisma to ClickHouse Managed Postgres" },
  });
  console.log("Created:", todo);

  // 조회
  const open = await prisma.todo.findMany({ where: { done: false } });
  console.log("Open todos:", open.length);

  // 수정
  const updated = await prisma.todo.update({
    where: { id: todo.id },
    data: { done: true },
  });
  console.log("Updated:", updated);

  // 삭제
  await prisma.todo.delete({ where: { id: todo.id } });
  console.log("Deleted todo", todo.id);
}

main().finally(() => prisma.$disconnect());
```

스크립트를 실행하십시오:

```bash theme={null}
npx tsx script.ts
```

```text theme={null}
Created: {
  id: 1,
  title: 'Connect Prisma to ClickHouse Managed Postgres',
  done: false,
  createdAt: 2026-09-30T16:52:57.116Z
}
Open todos: 1
Updated: {
  id: 1,
  title: 'Connect Prisma to ClickHouse Managed Postgres',
  done: true,
  createdAt: 2026-09-30T16:52:57.116Z
}
Deleted todo 1
```

**3. HTTP로 데이터 제공**

`server.ts`를 생성하십시오. 이 파일은 `POST /todos` 요청으로 할 일을 생성하고, 모든 `GET` 요청에 대해 할 일 목록을 반환하는 최소한의 HTTP 서버입니다:

```typescript theme={null}
import { createServer } from "node:http";
import { prisma } from "./lib/prisma";

const port = Number(process.env.PORT ?? 3000);

createServer(async (req, res) => {
  if (req.method === "POST" && req.url === "/todos") {
    let body = "";
    for await (const chunk of req) body += chunk;
    const todo = await prisma.todo.create({ data: { title: JSON.parse(body).title } });
    res.writeHead(201, { "Content-Type": "application/json" });
    res.end(JSON.stringify(todo));
    return;
  }

  const todos = await prisma.todo.findMany({ orderBy: { id: "asc" } });
  res.writeHead(200, { "Content-Type": "application/json" });
  res.end(JSON.stringify(todos, null, 2));
}).listen(port, () => console.log(`Listening on http://localhost:${port}`));
```

서버를 시작하십시오:

```bash theme={null}
npx tsx server.ts
```

다른 터미널을 열고 할 일 2개를 추가하십시오:

```bash theme={null}
curl -X POST localhost:3000/todos -H "Content-Type: application/json" -d '{"title": "Try Prisma with ClickHouse Managed Postgres"}'
curl -X POST localhost:3000/todos -H "Content-Type: application/json" -d '{"title": "Add a read replica"}'
```

브라우저에서 [http://localhost:3000/todos](http://localhost:3000/todos)를 열어 추가된 항목을 확인하십시오. 응답은 다음과 같습니다:

```json theme={null}
[
  {
    "id": 2,
    "title": "Try Prisma with ClickHouse Managed Postgres",
    "done": false,
    "createdAt": "2026-09-30T16:53:06.098Z"
  },
  {
    "id": 3,
    "title": "Add a read replica",
    "done": false,
    "createdAt": "2026-09-30T16:53:06.184Z"
  }
]
```

## SQL 콘솔에서 확인하기

ClickHouse Cloud 콘솔에서 해당 서비스의 **SQL 콘솔**을 열고, 데이터베이스와 `public` 스키마를 펼친 다음 `Todo` 테이블을 선택하십시오. Prisma Migrate가 적용된 마이그레이션을 기록하는 `_prisma_migrations` 테이블도 함께 표시됩니다.

`Todo` 테이블에는 HTTP 엔드포인트를 통해 추가한 2개의 행이 저장되어 있습니다.

| id | title | done | createdAt |
| - | - | - | - |
| 2 | Try Prisma with ClickHouse Managed Postgres | false | 2026-09-30 16:53:06.098 |
| 3 | Add a read replica | false | 2026-09-30 16:53:06.184 |

## 문제 해결

| 오류 | 원인 및 해결 방법 |
| - | - |
| `P1011: Error opening a TLS connection: One or more parameters passed to a function were not valid` 또는 `bad protocol version` | Prisma CLI가 macOS에서 실행되고 있습니다. [마이그레이션 실행](#migrate)에 나온 대로 Linux 컨테이너에서 실행하십시오. |
| `P1011: ... certificate verify failed` | Prisma CLI가 서버를 검증하지 못했습니다. `DIRECT_URL`의 `sslcert`가 인스턴스의 CA 인증서를 가리키는지 확인하십시오. |
| `P1011: ... cert file not found` | `DIRECT_URL`의 `sslcert` 경로가 잘못되었습니다. 프로젝트 루트에서 CLI를 실행하거나 절대 경로를 사용하십시오. |
| `unable to verify the first certificate` | Prisma Client가 서버를 검증하지 못했습니다. `DATABASE_URL`의 `sslrootcert`가 인스턴스의 CA 인증서를 가리키는지 확인하십시오. |
| `ENOENT: no such file or directory` | `DATABASE_URL`의 `sslrootcert` 경로가 잘못되었습니다. 프로젝트 루트에서 애플리케이션을 실행하거나 절대 경로를 사용하십시오. |

## 다음 단계

* [연결](https://clickhouse.com/docs/ko/products/managed-postgres/connection.md): 연결 문자열(connection string), PgBouncer 및 TLS
* [설정](https://clickhouse.com/docs/ko/products/managed-postgres/settings.md): Postgres 및 PgBouncer 매개변수 조정
* [읽기 레플리카(Read replica)](https://clickhouse.com/docs/ko/products/managed-postgres/read-replicas.md): 읽기 작업이 많은 워크로드 확장
* [로컬 개발](https://clickhouse.com/docs/ko/products/managed-postgres/local-development.md): Docker에서 실행되는 로컬 Postgres로 개발
* [프로덕션 환경의 Prisma Migrate](https://www.prisma.io/docs/orm/prisma-migrate/workflows/development-and-production): CI 파이프라인에서 `prisma migrate deploy` 사용
