Prisma 으로 멀티 테넌트(Multi-tenant) 구조를 위한 멀티 스키마(Multiple schemas) , 멀티 데이터베이스 연결(Multi-database connection) 방법을 소개합니다.
개요
프로젝트 진행 중 SaaS 형 DB 설계를 위해서 멀티 테넌트(Multi-tenant) 구조가 필요하여 멀티 데이터베이스 연결(Multi-database connection) 과 함께 멀티 스키마(Multiple schemas) 지원이 필요하였습니다.
여기서 멀티 테넌트(Multi-tenant) 란?
하나의 애플리케이션 또는 소프트웨어 인스턴스 가 여러 사용자 또는 테넌트(tenant) 를 지원하는 아키텍처를 지칭합니다.
이 용어는 주로 클라우드 컴퓨팅 , SaaS(Software as a Service) 모델 또는 대규모 엔터프라이즈 애플리케이션 에서 사용되며, 여러 고객이나 사용자 그룹이 동일한 애플리케이션 인스턴스를 공유하되, 각각의 데이터와 구성이 격리되어 있는 환경을 의미합니다.
아래의 요구 사항을 충족할 수 있어야 했습니다.
커넥션을동적으로 변경할 수 있어야 합니다.커넥션과 관계없이원하는 스키마를 지정해서쿼리할 수 있어야 합니다.
프로젝트에서 Prisma 를 사용하고 있었는데 Prisma 은 선언적으로 정의된 Schema 를 기준으로 Client 가 생성되기 때문에 여러 개의 Schema 와 Connection 을 가질 수 있는지 의문이었고 이에 대해 찾아보게 되었습니다.
멀티 지정 방법을 지원하는가?
찾아보니 3가지 선택지가 있더군요.
Third Party: prisma-multi-tenant
https://github.com/errorname/prisma-multi-tenant 를 찾을 수 있었는데 여러 개의 커넥션 을 가질 수 있긴 한데 스키마 가 고정 되어야 한다고 합니다.
즉, 같은 스키마인데 DB가 여러 개인 경우 사용될 수 있을 것인데 제 요구 상황과 다름으로 배제하였습니다.
Prisma 공식 지원
Prisma with multiple database schemas 라는 이름으로 Prisma 공식 문서에 있더군요.
테스트는 해보지 않았지만 Prisma 에서 멀티 스키마 를 지원하려고 하는 거 같습니다.
근데 문서를 보면 동적으로 커넥션 정보를 바꾸고 싶은데 그런 것이 가능할지 의문이었습니다.
여러 개의 Schema를 정의하고 Client generator 하기
Prisma multiple connections 라고 구글링 하여 https://github.com/prisma/prisma/issues/2443#issuecomment-630679118 방법을 찾을 수 있었습니다.
여러 개의 Schema 를 정의하고 Client generator 하는 방법이었는데 Prisma 의 기본 메커니즘을 따라 하는 것이며 동작 방식이 명확해서 이것을 사용하기로 하였습니다.
멀티 지정 방법
info
구현체는 코드는 parkgang/prisma-multi-schemas-and-multi-connections 에 존재합니다.
README 에 지정 방법을 작성했으니 해당 방법을 보는 것이 도움이 되실 것입니다. 해당 글에서는 핵심 과 주의점 에 대해서 서술하겠습니다.
multiple schemas시나리오이기 때문에*.prisma는 여러 개 생성되어야 합니다.*.prisma단위로 스키마가 정의됩니다.- 스키마가 헷갈리면
DB 단위라고 생각하세요. - 스키마에는
여러 Table이 정의될 것입니다.
스키마에 맞는Client를 호출해야 하는데 공식 문서에는import { PrismaClient } from '@prisma/client'으로Client가 한 개만 존재할 수 있는 것처럼 생겨서 헷갈릴 수 있는데 현재는스키마에 맞게Client를generator하므로 아래의 방법으로 필요한Client를 호출할 수 있습니다.import { PrismaClient as PrismaClientCatalog } from "./prisma/catalog/client";
import { PrismaClient as PrismaClientTenant } from "./prisma/tenant/client";스키마에 맞는커넥션을동적으로 변경할 수 있어야 하는데 아래의 문법으로 변경할 수 있습니다.import { PrismaClient } from "@prisma/client";
const client1 = new PrismaClient({
datasources: { db: { url: "postgres://localhost/db1" } },
});
const client2 = new PrismaClient({
datasources: { db: { url: "postgres://localhost/db2" } },
});
이렇게 스키마 에 맞는 Client 를 호출해서 원하는 커넥션 을 물려서 사용할 수 있는 환경이 마련되었습니다.
Insight
npm i @prisma/client 가 설치된 상태에서 진행해야 한다
스키마 에 맞게 Prisma Client generator 을 하니까 굳이 @prisma/client 패키지가 필요한가? 싶을 수 있습니다.
설치하지 않으면 아래와 같은 에러 메시지와 함께 Prisma Client 가 generator 되지 않고 이상하게 파일이 생성됩니다.
Error: Could not resolve @prisma/client despite the installation that we just tried.
Please try to install it by hand with npm install @prisma/client and rerun prisma generate 🙏.
해당 이유는 공식문서 https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/install-prisma-client-typescript-sqlserver 를 보니까 아래의 다이어그램 으로 잘 설명되어 있습니다.
스키마 는 독립적인 디렉터리에서 관리하는 것이 좋다
아래와 같이 하나의 디렉터리에서 관리하면 좋지 않다는 것을 의미합니다.
이유는 아래와 같은데
migrate시 구분이 어렵습니다. 2개의 스키마에 대해서 동시에 진행되기 때문인데 만약,db1,db2의 스키마 모두init이라고 이름을 지정하면 어떻게 구분할 건가요? 시간 차이로 인하여 아래와 같이 생길 텐데 구분이 어렵습니다. 물론00000000000000_db1_init,00000000000000_db2_init와 같이 구분하여 이름을 지정할 수 있겠지만 이건 원초적인 해결 방법이 아닙니다.db1,db2모두init라고 하니까 구분이 어렵습니다.원초적으로
migrate시 이전 스키마와 비교하여 sql문이 생성되는데 이전 스키마가 같은 스키마를 기준으로 하지 않기 때문에 아래와 같이 이상한 sql문이 생성됩니다. 이전에 생성된migrate의table을drop하는 코드가 들어가 있습니다. 오른쪽sql은 왼쪽sql보다 늦게migrate으로 생성된sql인데DROP TABLE "User";이라는 의도하지 않는 쿼리가 있는 것을 확인할 수 있습니다. 이는 이전migrate와diff하여 생성되었기 때문입니다.큰 문제는 아닌데 스키마 파일이 바뀌기 때문에
CLI에서 파일 이름을 지정해야 하는 귀찮음이 있습니다.npx prisma migrate dev --name init --schema prisma/db1-schema.prisma
npx prisma migrate dev --name init --schema prisma/db2-schema.prisma
고로 아래와 같이 독립적인 디렉터리에서 관리해야지 스키마 파일 이름도 깔끔하게 schema.prisma 으로 떨어져서 기본 값으로 지정되어 찾기가 편하고 migrate 시 차곡차곡 지정됩니다.
마무리
이렇게 Prisma 에서 멀티 스키마(Multiple schemas) , 멀티 데이터베이스 연결(Multi-database connection) 방법에 대해서 알아보았습니다.
Prisma Hello, world 에서는 DB 연결 을 위해 따로 지정 없이 종속된 Schema 정보를 이용하길래 멀티 연결 이 가능한가 걱정이 있었는데 이해하고 해보니까 생각보다 손쉽게 구현할 수 있었습니다.
아쉬운 점으로 Prisma 에서 공식으로 지원하는 것이 아닌 Client generator 방법을 응용한 것이라 업데이트 에 따른 Breaking Changes 가 걱정되는 부분이 있습니다.
읽어주셔서 감사합니다.