99

최근 30분간 동시 방문자 수를 표시합니다. (~)

최고 동시 방문자 수 -
어제: 0명 / 오늘: 0명

Supabase Database, Prisma로 빠르게 시작하기 w. Next.js

Supabase Database, Prisma로 빠르게 시작하기 w. Next.js

# Supabase 소개

이 글은 Prisma 7.10.0, Next.js 16.3.4 버전을 기준으로 작성되었습니다.
Prisma는 7 버전에서 초기화 결과물, 스키마 구조, 클라이언트 생성 방식이 크게 변경되었습니다.

SupabaseGoogle Firebase와 유사한 오픈소스 백엔드 서비스로, 웹/모바일 애플리케이션을 쉽고 빠르게 개발/배포할 수 있도록 도와주며 직접적인 인프라 관리에 대한 부담을 덜어줍니다.
기본적으로 PostgreSQL을 기반으로 구축된 데이터베이스(Database) 등의 다음과 같은 기능을 제공합니다.

  • Database: Full Postgres 데이터베이스
  • Authentication: 소셜, 전화 등 다양한 방법의 사용자 인증 기능
  • Storage: 이미지, 비디오, 문서 등의 AWS S3와 호환되는 파일 저장소
  • Edge Functions: 사용자와 가까운 엣지에서 배포되는 서버측 TypeScript 함수
  • Realtime: 데이터베이스의 변경 사항과 사용자의 상태를 웹소켓(WebSocket)으로 주고받는 기능
  • Vector: 임베딩(Embedding)을 저장하고 검색해 AI 애플리케이션을 개발하는 기능
  • Cron: 데이터베이스에서 반복 작업을 예약하고 실행하는 기능

# 요금제

무료 플랜에서 제공하는 기본적인 기능 및 특징은 다음과 같습니다.
더 자세한 내용은 Supabase Pricing 페이지에서 확인할 수 있습니다.

  • 500MB 데이터베이스
  • 매월 5GB 전송량(Egress)과 5GB 캐시 전송량
  • 1GB 파일 저장소(Storage)
  • 파일 하나당 50MB 크기 제한
  • 매월 5만 명의 활성 사용자(MAU)
  • 매월 50만 회의 Edge Functions 호출
  • 활성 프로젝트 2개 제한
  • 1주일 동안 활동하지 않으면 프로젝트 일시 중지

활성 프로젝트 개수는 조직이 아니라 계정을 기준으로 계산합니다.
본인이 소유자(Owner)나 관리자(Administrator)로 참여한 모든 조직을 통틀어 2개까지 만들 수 있으며, 일시 중지된 프로젝트는 이 개수에 포함되지 않습니다.

Supabase 요금제

# Prisma 소개

Prisma는 데이터베이스 스키마를 쉽게 정의하고 타입 세이프(Type-Safe)한 쿼리를 작성할 수 있도록 도와주는 차세대 Node.js 및 TypeScript ORM(Object-Relational Mapping)입니다.
Prisma를 사용해 직접 SQL을 작성하지 않고도 데이터베이스를 쉽게 다룰 수 있습니다.

TS
1
2
3
4
5
6
7
await prisma.user.create({ data: { name: 'HEROPY', age: 85, email: 'thesecon@gmail.com' } })
사용자 생성 예시

# Next.js 프로젝트 구성

Supabase 데이터베이스와 Prisma를 사용해 Next.js 프로젝트를 구성해 보겠습니다.
Prisma를 통해 타입 세이프한 쿼리를 작성하려면, TypeScript를 사용하는 것이 좋습니다.

다음과 같이 Next.js 프로젝트를 생성하고 패키지를 설치합니다.

  • prisma: Prisma CLI를 사용해 데이터베이스 스키마를 가져오거나 마이그레이션을 실행하는 코어 패키지입니다.
  • @prisma/client: 클라이언트 라이브러리로, 데이터베이스에 대한 타입 세이프한 쿼리를 요청할 수 있도록 도와줍니다.
  • @prisma/adapter-pgpg: Prisma 7부터 필수인 PostgreSQL 드라이버 어댑터와 드라이버입니다.
  • dotenv: Prisma CLI가 설정 파일에서 .env 파일의 환경변수를 사용할 수 있도록 도와줍니다.
  • @types/pg: pg 패키지의 타입 선언입니다.
BASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
npx create-next-app@latest supabase-test Would you like to use the recommended Next.js defaults? No, customize settings Would you like to use TypeScript? Yes Which linter would you like to use? ESLint Would you like to use React Compiler? No Would you like to use Tailwind CSS? No Would you like your code inside a `src/` directory? No Would you like to use App Router? (recommended) … Yes Would you like to customize the import alias (`@/*` by default)? … No Would you like to include AGENTS.md to guide coding agents to write up-to-date Next.js code? No cd supabase-test npm i -D prisma@7 dotenv @types/pg npm i @prisma/client@7 @prisma/adapter-pg pg

prisma 패키지의 latest 태그는 아직 정식 출시되지 않은 8 버전의 릴리스 후보(RC)를 가리키고, @prisma/client 패키지의 latest 태그는 7 버전을 가리킵니다.
버전을 지정하지 않고 설치하면 두 패키지의 버전이 서로 달라지므로, 위와 같이 @7을 붙여서 버전을 맞춥니다.

src/ 디렉토리를 사용하지 않는 것은 이 글의 경로를 기준으로 따라 하기 위한 선택입니다.
src/ 디렉토리를 사용해도 되지만, 그러면 뒤에서 Prisma가 클라이언트를 생성하는 경로와 가져오기 경로가 달라지므로 해당 부분에서 안내하는 내용을 참고하세요.

# Prisma 초기화

프로젝트에서 Prisma를 사용하기 위해 초기화가 필요합니다.
터미널에서 다음 명령을 실행합니다.

BASH
1
2
3
4
5
6
7
8
9
10
11
12
npx prisma init Initialized Prisma in your project prisma/ schema.prisma prisma7.config.ts .env .claude/skills/ .windsurf/skills/ .agents/skills/ skills-lock.json

prisma/schema.prisma는 데이터베이스 스키마를 정의하는 파일이고, prisma7.config.ts는 스키마 위치와 데이터베이스 주소 등을 지정하는 설정 파일입니다.
Prisma 6까지는 데이터베이스 주소를 schema.prisma 파일에서 관리했지만, 7 버전부터는 설정 파일로 분리되었습니다.
설정 파일의 이름은 prisma7.config.ts로 생성되며, prisma.config.ts 이름도 그대로 인식합니다.

함께 생성되는 .claude/skills/, .windsurf/skills/, .agents/skills/, skills-lock.json은 Claude Code 같은 AI 에이전트가 참고하는 스킬 파일입니다.
Prisma를 사용하는 데 필요한 파일은 아니므로, 사용하지 않으면 삭제해도 됩니다.

초기화로 생성된 schema.prisma 파일은 다음과 같습니다.
datasource 블록에 데이터베이스 주소가 없고, generator 블록에 클라이언트를 생성할 경로(output)가 지정된 것을 확인할 수 있습니다.

/prisma/schema.prisma
1
2
3
4
5
6
7
8
generator client { provider = "prisma-client" output = "../app/generated/prisma" } datasource db { provider = "postgresql" }

Prisma 7부터 클라이언트를 node_modules가 아닌 프로젝트 안의 경로에 생성합니다.
초기화 명령은 프로젝트에 src, lib, app 폴더가 있는지 순서대로 확인해 처음 찾은 폴더 아래 generated/prisma 경로를 output으로 지정하고, 같은 경로를 기존 .gitignore 파일에 추가해 저장소에 포함하지 않도록 합니다.
이 글의 구성에는 app 폴더만 있으므로 app/generated/prisma 경로가 됩니다.

src/ 디렉토리를 사용했거나 lib 폴더를 미리 만들어 두었다면 output.gitignore에 추가된 경로가 src/generated/prisma처럼 달라집니다.
뒤에서 Prisma Client를 가져올 때 경로를 맞춰야 하므로, schema.prisma 파일에서 실제 output 값을 확인해 두세요.

Prisma 6까지 사용하던 prisma-client-js 생성기와 datasource 블록의 url, directUrl 속성은 7 버전에서 더는 권장하지 않습니다.
기존 프로젝트를 옮긴다면 providerprisma-client로 변경하고 output을 추가한 후, 데이터베이스 주소를 설정 파일로 옮겨야 합니다.

# Supabase 프로젝트 생성

이제 Supabase에 접속해, 회원가입 및 로그인합니다.
GitHub 계정이나 이메일로 가입할 수 있고, 신용카드는 필요하지 않습니다.

프로젝트는 조직(Organization) 아래에 만들어집니다.
가입 직후에는 조직을 만드는 화면이 바로 나타나며, 'Type'은 'Personal', 'Plan'은 'Free'를 선택하고 'Create organization'을 누르면 곧바로 새 프로젝트를 만드는 화면으로 이동합니다.
이미 조직이 있다면 'Dashboard > Organizations' 페이지에서 조직을 선택하고, 프로젝트 목록의 'New project' 버튼을 선택합니다.

조직의 프로젝트 목록

다음과 같이 조직(Organization), 프로젝트 이름(Project name), 데이터베이스 비밀번호(Database password) 그리고 리전(Region)을 입력/선택합니다.
프로젝트 이름은 자유롭게 정할 수 있으며, 이 글의 화면에서는 supabase-board를 사용합니다.
비밀번호는 'Generate a password'로 생성한 후 'Copy' 버튼으로 복사해 따로 저장해 두고, 리전은 사용자와 가까운 'Northeast Asia (Seoul)'을 선택합니다.
아래쪽 'Security' 항목은 기본값 그대로 두어도 됩니다.

새 프로젝트 생성

데이터베이스 비밀번호는 생성 후에 다시 확인할 수 없으므로 반드시 따로 저장해야 합니다.
잊어버렸다면 프로젝트 상단의 'Connect' 버튼을 선택해 나오는 창의 'Direct' 탭에 있는 'Reset database password' 버튼이나, 'Database > Settings' 페이지에서 재설정할 수 있습니다.
그리고 비밀번호에 특수문자를 포함하면, 연결 문자열에서 퍼센트 인코딩(Percent-encoding)으로 변환해야 합니다.
예를 들어 = 문자는 %3D로 작성해야 하며, 변환하지 않으면 연결에 실패할 수 있습니다.

'Create new project'를 누르면 프로젝트가 준비되기까지 1분에서 2분 정도 걸립니다.
준비가 끝나면 다음과 같이 프로젝트 홈 화면이 나타나며, 왼쪽의 아이콘 메뉴로 각 페이지를 이동합니다.
이 글에서 'Table Editor'처럼 표기한 페이지는 아이콘 메뉴 위에 마우스를 올리면 나오는 이름입니다.

프로젝트 홈

# 데이터베이스 연결

Supabase는 Supavisor라는 커넥션 풀러(Connection Pooler)를 통해 두 종류의 연결을 제공하며, 포트 번호로 구분합니다.

연결 방식 포트 용도
Transaction pooler 6543 서버리스나 엣지처럼 짧은 연결이 반복되는 환경
Session pooler 5432 마이그레이션처럼 하나의 연결을 유지해야 하는 작업

커넥션 풀러는 PostgreSQL 데이터베이스 서버 앞에서 동작하며, 여러 클라이언트의 연결을 미리 확보한 연결로 재사용해 처리 시간을 단축하고 리소스를 최적화합니다.

Next.js는 요청마다 함수가 실행되는 서버리스 환경으로 배포하는 경우가 많으므로, 두 주소를 모두 사용합니다.
앱에서 실행되는 Prisma Client는 Transaction pooler 주소로 연결하고, 스키마를 가져오거나 내보내는 Prisma CLI는 Session pooler 주소로 연결합니다.

두 주소는 프로젝트 홈 상단의 'Connect' 버튼을 선택해 확인할 수 있습니다.
창이 열리면 'ORM' 탭을 선택하고 'Prisma'를 고르면, 'Configure ORM' 단계의 .env.local 탭에 DATABASE_URL(Transaction pooler)과 DIRECT_URL(Session pooler) 두 값이 채워진 내용이 표시됩니다.

Connect 창의 ORM 탭

초기화 과정에서 생성된 /.env 파일의 기존 내용을 지우고, 복사한 내용을 다음과 같이 작성합니다.
값의 [YOUR-PASSWORD] 부분은 앞서 저장해 둔 데이터베이스 비밀번호로 대체합니다.
DATABASE_URL에는 포트 번호 6543?pgbouncer=true 쿼리스트링이, DIRECT_URL에는 포트 번호 5432가 사용된 것을 확인할 수 있습니다.

/.env
BASH
1
2
DATABASE_URL="postgresql://postgres.rhlkfepaasyyeqydkjkw:abcdefghijk@aws-0-ap-northeast-2.pooler.supabase.com:6543/postgres?pgbouncer=true" DIRECT_URL="postgresql://postgres.rhlkfepaasyyeqydkjkw:abcdefghijk@aws-0-ap-northeast-2.pooler.supabase.com:5432/postgres"

pgbouncer=true는 Transaction pooler에서 준비된 구문(Prepared Statement) 때문에 생기는 충돌을 피하기 위한 옵션으로, Connect 창이 안내하는 값을 그대로 사용합니다.

Connect 창은 파일 이름을 .env.local로 안내하지만, 이 글에서는 초기화 과정에서 생성된 .env 파일에 작성합니다.
prisma7.config.tsdotenv/config로 읽는 파일이 .env이기 때문이며, Next.js는 .env.env.local을 모두 읽으므로 앱에서도 문제없이 사용할 수 있습니다.
같은 창의 prisma/schema.prisma 탭은 Prisma 6 방식(url, directUrl 속성)으로 작성되어 있으므로 사용하지 않습니다.
주소의 aws-0-ap-northeast-2 부분은 프로젝트마다 다르며 생성 시점에 따라 aws-1처럼 다른 번호를 사용하므로, 위 예시를 그대로 사용하지 말고 Connect 창에서 복사한 주소를 사용하세요.

그리고 prisma7.config.ts 파일의 datasource.urlDATABASE_URL에서 DIRECT_URL 환경변수로 변경합니다.
Prisma 7에서 설정 파일의 주소는 스키마를 가져오거나 내보내는 Prisma CLI만 사용하고, 앱에서 실행되는 Prisma Client는 뒤에서 어댑터에 전달하는 주소를 사용합니다.
따라서 설정 파일에는 연결을 유지하는 Session pooler 주소를 지정해야 합니다.

/prisma7.config.ts
TS
1
2
3
4
5
6
7
8
9
10
11
12
import 'dotenv/config' import { defineConfig } from 'prisma/config' export default defineConfig({ schema: 'prisma/schema.prisma', migrations: { path: 'prisma/migrations' }, datasource: { url: process.env['DIRECT_URL'] } })

초기화 명령은 어떤 데이터베이스를 사용할지 모르는 상태에서 실행되므로, 설정 파일에 가장 일반적인 이름인 DATABASE_URL을 넣어 둡니다.
이 값을 그대로 두면 Prisma CLI가 Transaction pooler로 연결하는데, Transaction pooler는 트랜잭션마다 다른 연결을 배정하므로 하나의 연결을 유지해야 하는 스키마 작업과 맞지 않습니다.
이 상태에서 npx prisma db pull을 실행하면 아래 출력에서 더 진행하지 않고 멈추므로, 명령이 멈춘다면 설정 파일의 주소부터 확인하세요.

BASH
1
- Introspecting based on datasource defined in prisma/schema.prisma
Transaction pooler 주소로 실행했을 때 멈추는 지점

# Database 테이블 생성

Supabase 프로젝트 준비가 끝났으니, 이제 'Table Editor' 페이지에서 새로운 데이터베이스 테이블을 생성합니다.
왼쪽 'New table' 버튼을 선택하면 다음과 같이 테이블을 만드는 창이 나타납니다.

이 과정에서는 테이블 이름(Post)만 입력하고 아래쪽 'Save' 버튼을 선택합니다.
idcreated_at 열(Column)은 자동으로 생성되며, 만약 클라이언트에서 CamelCase로만 속성 이름을 사용하려면 created_at 대신 createdAt 이름으로 미리 변경하는 것이 좋습니다.

새 테이블 생성

'Enable Row Level Security (RLS)'는 기본으로 켜져 있으며, 그대로 두어도 됩니다.
행 수준 보안은 Supabase의 Data API로 접근할 때 정책(Policy)으로 행을 걸러내는 기능인데, Prisma는 테이블의 소유자인 postgres 역할로 직접 연결하므로 정책이 없어도 모든 행을 읽고 쓸 수 있습니다.

생성된 Post 테이블

# DB 스키마 가져오기

이제 Next.js 프로젝트로 돌아와 Prisma를 사용해, 생성한 Supabase 데이터베이스와 연결하겠습니다.
터미널에서 다음 명령으로 Supabase 데이터베이스의 스키마를 가져옵니다.

BASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
npx prisma db pull Loaded Prisma config from prisma7.config.ts. Prisma schema loaded from prisma/schema.prisma. Datasource "db": PostgreSQL database "postgres", schema "public" at "aws-0-ap-northeast-2.pooler.supabase.com:5432" - Introspecting based on datasource defined in prisma/schema.prisma Introspected 1 model and wrote it into prisma/schema.prisma in 424ms *** WARNING *** These tables contain row level security, which is not yet fully supported. Read more: https://pris.ly/d/row-level-security - "Post" Run prisma generate to generate Prisma Client.

행 수준 보안이 켜진 테이블이 있다는 경고가 함께 출력됩니다.
Prisma의 마이그레이션 기능이 정책까지 관리하지는 못한다는 안내이며, 이 글처럼 Table Editor로 테이블을 관리하고 Prisma로 데이터를 다루는 데는 영향이 없습니다.

그러면 다음과 같이 schema.prisma 파일에 Supabase 데이터베이스 Post 테이블의 스키마가 자동으로 추가됩니다.
모델 위의 /// 주석은 같은 경고를 파일에 남긴 것으로, 지워도 다음 db pull에서 다시 추가됩니다.

/prisma/schema.prisma
1
2
3
4
5
6
7
8
9
10
11
12
13
14
generator client { provider = "prisma-client" output = "../app/generated/prisma" } datasource db { provider = "postgresql" } /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model Post { id BigInt @id @default(autoincrement()) created_at DateTime @default(now()) @db.Timestamptz(6) }

그리고 추가된 스키마를 기반으로 Prisma Client를 생성하기 위해, 다음 명령을 실행합니다.
Prisma Client를 생성해야 데이터베이스에 대한 타입 세이프한 쿼리를 작성할 수 있습니다.

BASH
1
2
3
npx prisma generate Generated Prisma Client (7.10.0) to ./app/generated/prisma in 22ms

생성된 /app/generated/prisma 경로는 .gitignore에 포함되므로, Next.js 프로젝트를 배포할 때 서버 빌드 시에도 Prisma Client를 생성해야 합니다.
package.json 파일에 다음과 같이 postinstall 스크립트를 추가합니다.

/package.json
JSON
1
2
3
4
5
{ "scripts": { "postinstall": "prisma generate" } }

# Prisma Client 인스턴스

Prisma 7부터 데이터베이스에 연결할 때 드라이버 어댑터가 필요합니다.
/lib/prisma.ts 파일을 생성하고 다음과 같이 어댑터와 함께 Prisma Client를 생성합니다.

/lib/prisma.ts
TS
1
2
3
4
5
6
7
8
9
10
11
12
import { PrismaClient } from '@/app/generated/prisma/client' import { PrismaPg } from '@prisma/adapter-pg' const adapter = new PrismaPg({ connectionString: process.env.DATABASE_URL! }) const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient } export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter }) if (process.env.NODE_ENV !== 'production') { globalForPrisma.prisma = prisma }

어댑터에는 Transaction pooler 주소인 DATABASE_URL을 지정합니다.
그리고 개발 서버는 파일을 수정할 때마다 모듈을 다시 실행하므로, 인스턴스를 그대로 생성하면 사용하지 않는 연결이 계속 쌓입니다.
globalThis에 인스턴스를 저장해 하나의 인스턴스만 재사용하도록 작성합니다.

어댑터 없이 new PrismaClient()를 호출하면 'A driver adapter is required to connect to your database' 오류가 발생합니다.
그리고 클라이언트는 @prisma/client가 아니라 schema.prismaoutput에 지정한 경로에서 가져와야 하며, 경로 끝에 /client를 붙입니다.
src/ 디렉토리를 사용했다면 @/generated/prisma/client처럼 경로가 달라집니다.

# 데이터(행) 생성

Prisma Client를 사용할 준비가 끝났습니다.
이제 데이터베이스에 접근해 새로운 데이터(행, Row)를 생성하겠습니다.

Prisma Client로 데이터베이스에 접근할 때는 다음과 같은 형식을 사용합니다.
또한 create() 같은 각 메소드는 'Promise 인스턴스'를 반환하므로, await 키워드나 .then()을 사용해 비동기로 처리할 수 있습니다.

TS
1
2
// Prisma인스턴스.테이블이름.메소드() await prisma.post.create()

/app/api/post/route.ts 파일을 생성하고 다음과 같이 코드를 작성합니다.

/app/api/post/route.ts
TS
1
2
3
4
5
6
7
8
import { prisma } from '@/lib/prisma' export async function GET() { await prisma.post.create({ data: {} }) return Response.json('ok!') }

개발 서버를 실행하고, http://localhost:3000/api/post/로 접속합니다.
그러면 바로 Supabase Post 테이블에서 다음과 같이 새로운 행이 생성된 것을 확인할 수 있습니다.
Table Editor에 이전 화면이 남아 있다면 오른쪽 위 새로 고침 버튼을 선택합니다.

id 열은 BigInt 타입이므로, create()가 반환한 값을 Response.json()으로 그대로 반환하면 'Do not know how to serialize a BigInt' 오류가 발생합니다.
생성한 행을 응답으로 반환하려면 Number(post.id)post.id.toString()처럼 변환해서 사용합니다.

개발 서버를 처음 실행하면 Next.js가 프로젝트에 AGENTS.mdCLAUDE.md 파일을 생성합니다.
프로젝트를 만들 때 AGENTS.md를 포함하지 않았어도 생성되며, 원하지 않으면 next.config.tsagentRules: false를 지정합니다.

# 열 생성

새로운 열(Column)을 추가하려면, 'Table Editor' 페이지에서 머릿열 끝 '+' 버튼을 선택합니다.
열의 이름(Name), 데이터 타입(Type), 기본 값(Default Value) 등을 입력하고 하단의 'Save' 버튼을 선택합니다.
기본 값은 입력 칸 오른쪽의 목록 버튼을 선택해 'Set as empty string'을 고르면 빈 문자로 지정되며, 입력 칸에 'EMPTY'가 표시됩니다.

데이터 타입이 text인 경우, 항상 문자를 반환하도록 기본 값은 빈 문자(Empty String)로 설정하는 것을 추천합니다.

content 열 추가

다음과 같이 추가된 content 열을 확인할 수 있습니다.
기존 행에는 기본 값인 빈 문자가 채워져 'EMPTY'로 표시됩니다.

원격 데이터베이스에서 새로운 열을 추가했으니, 터미널에서 스키마를 다시 가져오고 Prisma Client도 다시 생성합니다.

BASH
1
2
npx prisma db pull npx prisma generate

그러면 다음과 같이 schema.prisma 파일에 content 열이 새로 추가된 것을 확인할 수 있습니다.

/prisma/schema.prisma
1
2
3
4
5
6
7
// ... model Post { id BigInt @id @default(autoincrement()) created_at DateTime @default(now()) @db.Timestamptz(6) content String? @default("") }

Prisma Client를 다시 생성한 후에는 개발 서버를 다시 시작해야 합니다.
실행 중인 개발 서버는 이전에 생성한 클라이언트 인스턴스를 그대로 사용하므로, 새로 추가한 열을 사용하면 'Unknown argument content' 오류가 발생합니다.

다음과 같이 새롭게 추가된 content 열에, 정보를 추가해 행을 생성해 봅시다.

/app/api/post/route.ts
TS
1
2
3
4
5
6
7
8
9
10
// ... export async function GET() { await prisma.post.create({ data: { content: '포스트 내용 입력!' } }) return Response.json('ok!') }

위와 같이 수정한 내용으로 http://localhost:3000/api/post/로 접속한 후 바로 Supabase 테이블을 확인하면, 다음과 같이 새로운 행이 생성된 것을 확인할 수 있습니다.

Prisma Client는 스키마(schema.prisma)를 기반으로 타입 세이프한 쿼리를 작성할 수 있기 때문에, contents와 같이 존재하지 않는 열을 사용하려고 하면 다음과 같이 타입 에러가 발생합니다.

/app/api/post/route.ts
TS
1
2
3
4
5
6
7
8
9
10
// ... export async function GET() { await prisma.post.create({ data: { contents: '포스트 내용 입력!' // Error - ... 형식에 'contents'이(가) 없습니다. 'content'을(를) 쓰려고 했습니까? ts(2561) } }) return Response.json('ok!') }

# 스키마 내보내기

원격의 'Table Editor'를 대신해, 로컬에서 수정한 스키마를 반영할 수도 있습니다.
'Table Editor'가 편하긴 하지만, 지원하지 않는 기능이나 복잡한 작업을 할 때는 로컬에서 직접 스키마를 수정해 내보내는 과정이 필요할 수 있습니다.

여기에서는 행을 수정할 때의 시간을 저장하도록 updated_at 열을 추가해 보겠습니다.
/prisma/schema.prisma 파일을 다음과 같이 직접 수정합니다.

타입은 선택적(Optional) DateTime?이고 @updatedAt@db.Timestamptz(6) 데코레이터를 사용해 Timezone으로 업데이트 시간을 자동으로 저장하도록 설정합니다.

DateTime?? 기호가 있는 것을 주의하세요!
지금까지의 과정을 통해서 기존에 추가한 2개의 행이 있기 때문에, 새로운 updated_at 열을 필수적(Required)으로 설정하고 스키마를 내보내면, 기존 테이블 구조와의 충돌로 테이블 데이터를 모두 잃을 수 있습니다.
이런 경우, 필요하다면 선택적(Optional) 타입으로 열을 추가하고 이후 다른 행의 해당 열에 데이터를 모두 채운 후 필수적 타입으로 변경해야 합니다.

/prisma/schema.prisma
1
2
3
4
5
6
7
8
// ... model Post { id BigInt @id @default(autoincrement()) created_at DateTime @default(now()) @db.Timestamptz(6) updated_at DateTime? @updatedAt @db.Timestamptz(6) content String? @default("") }

로컬에서 스키마를 수정했으니, 원격 데이터베이스에 적용(내보내기)하기 위해 터미널에서 다음 명령을 실행합니다.

BASH
1
2
3
npx prisma db push 🚀 Your database is now in sync with your Prisma schema. Done in 471ms

Prisma 6까지는 db push 명령이 Prisma Client를 자동으로 생성했지만, 7 버전부터는 생성하지 않습니다.
따라서 스키마를 내보낸 후에는 npx prisma generate 명령을 직접 실행하고, 개발 서버도 다시 시작해야 합니다.

완료되면, 다음과 같이 updated_at 열이 추가된 것을 확인할 수 있습니다.

그리고 이번에는 새롭게 추가된 열에 데이터가 잘 저장되는지 확인하기 위해 기존의 행을 수정해 봅시다.
다음과 같이 update 메소드의 where 속성으로 특정 id의 행을 찾아 수정할 수 있습니다.

update 메소드는 단일 행을 수정하기 때문에 고유 열(Unique)의 제약 조건이 필요하고, 따라서 id와 같은 고유 열의 정보를 조건으로 제공해야 합니다.
반대로 고유 열 없이, 제공된 조건으로 찾은 모든 행을 수정하려면 updateMany 메소드를 사용하면 됩니다.

/app/api/post/route.ts
TS
1
2
3
4
5
6
7
8
9
10
11
12
13
// ... export async function GET() { await prisma.post.update({ where: { id: 2 }, data: { content: '포스트 내용 수정~' } }) return Response.json('ok!') }

http://localhost:3000/api/post/로 접속한 후 바로 Supabase 테이블을 확인하면, 다음과 같이 기존 행이 수정되면서 updated_at 열에 시간이 자동으로 추가된 것을 확인할 수 있습니다.

@updatedAt은 Prisma가 쿼리를 실행할 때 값을 채웁니다.
따라서 Supabase 'Table Editor'에서 직접 행을 수정하면 updated_at 열의 값은 변경되지 않습니다.

# Prisma Studio

Prisma Studio는 데이터베이스를 시각적으로 확인하고 수정할 수 있는 GUI(Graphical User Interface) 도구입니다.
우리는 Supabase의 'Table Editor'를 사용하고 있기 때문에, Prisma Studio를 사용하지 않아도 무방합니다.
단지, 원격이 아닌 로컬에서 데이터베이스를 시각적으로 관리하고 싶다면 Prisma Studio를 사용할 수 있습니다.

터미널에서 다음 명령을 실행하면 브라우저가 자동으로 열리며, 출력된 주소로 직접 접속할 수도 있습니다.
포트 번호는 --port 옵션으로 지정할 수 있습니다.

BASH
1
2
3
4
5
npx prisma studio Loaded Prisma config from prisma7.config.ts. Prisma Studio is running at: http://localhost:51212

왼쪽 'Tables' 목록에서 테이블을 선택하면 데이터를 확인할 수 있습니다.

셀을 더블 클릭해 값을 수정하면 위쪽에 'Save' 버튼이 나타나며, 이 버튼을 선택해야 원격의 Supabase 데이터베이스에 반영됩니다.

# Prisma Client API

Prisma Client에서 제공하는 API 중 기본적인 모델 쿼리(Model queries) 및 옵션은 다음과 같습니다.
더 자세한 내용은 Prisma Client API 문서를 참고하세요.

TS
1
2
3
4
5
6
7
8
prisma.post.update({ where: { id: 2 }, data: { content: '포스트 내용 수정~' } })
메소드 설명
findUnique 고유(Unique) 열을 기반으로 단일 행을 찾습니다.
findFirst 조건의 첫 번째 행을 찾습니다.
findMany 조건의 여러 행을 찾습니다.
create 새로운 행을 생성합니다.
createMany 여러 행을 생성합니다.
createManyAndReturn 여러 행을 생성하고 생성된 행을 반환합니다.
upsert 행을 찾아 수정하거나, 없으면 새로 생성합니다.
update 조건의 행을 수정합니다.
updateMany 조건의 여러 행을 수정합니다.
updateManyAndReturn 조건의 여러 행을 수정하고 수정된 행을 반환합니다.
delete 조건의 행을 삭제합니다.
deleteMany 조건의 여러 행을 삭제합니다.
count 조건의 행 개수를 반환합니다.
aggregate 조건의 행 집계 정보를 반환합니다.
groupBy 열을 기준으로 그룹화된 정보를 반환합니다.
옵션 설명
select 열을 선택합니다.
omit 선택에서 제외할 열을 지정합니다.
include 관계(Relation) 열을 선택합니다.
where 조건 행을 찾습니다.
orderBy 열을 기준으로 정렬합니다.
distinct 중복 행을 제거합니다.
take 가져올 행의 개수를 제한합니다.
skip 지정한 개수만큼 행을 건너뜁니다.
cursor 특정 행을 기준으로 페이지를 나눕니다.