게시판에 이슈 관리 기능을 붙이고, 서비스 신청이나 북마크 같은 모듈을 추가하다 보면 DB 모델도 함께 늘어납니다. 그때마다 코어의 schema.prisma에 모든 모델을 넣으면 프로젝트 전용 기능과 공통 기능의 경계가 흐려지고, 코어 업데이트도 점점 어려워집니다.
Plextype는 모델의 원본을 각 확장 모듈에 두고, Prisma가 읽을 때는 하나의 스키마로 합칩니다. 이 글은 현재 scripts/sync-prisma.mjs와 scripts/extension-files.mjs의 구현을 기준으로 파일 구조, 합성 과정, 예제와 운영 시 확인할 경계를 정리한 것입니다. 설치 버전에 따라 경로나 명령이 다를 수 있으므로 적용 전 자신의 코드를 확인하세요.
1. 왜 모듈별로 나누고, 다시 하나로 합칠까?
모델을 나누는 기준은 테이블 개수보다 어느 기능이 이 모델을 책임지는가입니다. 회원·문서·댓글 같은 공통 모델은 코어에, 프로젝트에서만 쓰는 모델은 해당 확장 폴더에 둡니다.
src/core/prisma/schema.prisma 공통 모델과 generator·datasource
src/extensions/bookmarks/prisma/schema.prisma 북마크 확장의 모델 원본
src/extensions/service/prisma/schema/*.prisma 서비스 확장의 모델 원본
↓ npm run prisma:sync
prisma/schema.prisma Prisma가 읽는 최종 생성 파일이렇게 하면 확장 코드를 살펴볼 때 DB 정의도 같은 기능 폴더에서 찾을 수 있습니다. 확장이 없는 기본 배포판에서는 해당 모델 조각을 포함하지 않는 스키마를 만들 수 있습니다. 다만 이것은 소스 구성의 분리이지, 운영 DB에서 해당 테이블을 자동으로 제거하는 기능은 아닙니다.
Prisma 자체도 여러 파일의 스키마를 지원합니다. 여기서 설명하는 것은 그 기능을 직접 설정하는 방법이 아니라, Plextype가 확장 폴더를 탐색해 단일 파일을 생성하는 현재 구현입니다. PostgreSQL의 여러 DB 스키마를 사용하는 multi-schema 기능과도 구분해야 합니다.
2. 어떤 파일을 찾아서 어떤 순서로 합칠까?
탐색은 src/extensions 아래에서 이름이 prisma인 디렉터리를 찾는 방식입니다. 한 단계 아래만 보는 것이 아니므로 src/extensions/posts/issuetracker/prisma처럼 중첩된 기능 폴더도 대상이 됩니다. registry에 모듈을 등록했는지보다 해당 경로에 파일이 존재하는지가 스키마 포함 여부를 결정합니다.
각 prisma 디렉터리에서는 두 형식을 모두 읽습니다.
prisma/schema.prisma: 모델을 한 파일에 정리할 때 사용합니다.prisma/schema/*.prisma: 같은 기능의 모델을 여러 조각으로 나눌 때 사용합니다. 이 schema 폴더 안에서는 바로 아래의 .prisma 파일만 읽습니다.
두 형식을 함께 두면 둘 다 포함합니다. 같은 모델을 양쪽에 중복 작성하지 마세요. 디렉터리는 이름순으로 순회하고, 각 prisma 디렉터리에서는 단일 schema.prisma를 먼저 읽은 뒤 schema 폴더의 .prisma 파일을 이름순으로 읽습니다. 비어 있는 파일은 건너뜁니다.
코어 schema.prisma
→ 탐색된 확장 A의 schema.prisma
→ 확장 A의 schema/*.prisma (파일명순)
→ 다음 확장의 스키마 조각
→ prisma/schema.prisma에 저장탐색 중 숨김 하위 디렉터리와 node_modules·build·dist·storage 같은 폴더는 제외하며, 하위 디렉터리 심볼릭 링크를 따라가 확장 폴더로 확장하지 않습니다. 현재 탐색 테스트에도 중첩 경로와 이 제외 조건을 확인하는 사례가 있습니다.
합성은 모델을 해석해 병합하는 것이 아니라 파일 내용을 연결하는 작업입니다. 생성 파일에는 원본 경로를 나타내는 주석이 붙습니다.
// This file is generated by scripts/sync-prisma.mjs.
// Edit src/core/prisma/schema.prisma for core schema changes.
// Edit src/extensions/<feature>/prisma/schema/*.prisma for extension schema changes.
…코어 스키마…
// Extension schema: src/extensions/bookmarks/prisma/schema.prisma
…북마크 모델…이 주석은 추적을 돕지만 모델 이름 충돌이나 관계 오류를 해결해주지는 않습니다. 문법과 관계가 유효한지는 합성 후 Prisma 검증으로 확인해야 합니다.
3. 실전: 북마크 확장 모델 추가하기
코어 User 모델을 수정하지 않는 작은 예제로 시작하겠습니다. 이 예제는 북마크 데이터를 저장할 모델 정의이며, 사용자별 권한·화면·API까지 구현한 완성 모듈은 아닙니다.
① 기능 폴더에 원본 작성
src/extensions/bookmarks/prisma/schema.prisma를 만들고 다음 모델을 작성합니다. 기존 파일이 있다면 내용을 통째로 덮어쓰지 말고 모델 이름의 중복 여부부터 확인하세요.
model BookmarkEntry {
id Int @id @default(autoincrement())
uuid String @unique @default(cuid())
title String @db.VarChar(200)
url String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([createdAt])
}확장 조각에는 이 모델처럼 필요한 model·enum 정의를 둡니다. 코어에 있는 generator와 datasource를 확장마다 복사하지 않습니다. 예제의 @db.VarChar·@db.Text는 현재 코어가 사용하는 PostgreSQL 기준입니다.
모델이 많아지면 아래처럼 파일을 나눌 수도 있습니다. 이때 앞의 단일 파일에서 옮긴 정의를 그대로 중복해서 남기면 안 됩니다.
src/extensions/bookmarks/prisma/schema/
bookmark-entry.prisma
bookmark-folder.prisma② 합성한 뒤 검증하기
프로젝트 루트에서 다음 순서로 실행합니다.
npm run prisma:sync
npx prisma validateprisma:sync는 원본 파일을 합치고, validate는 생성된 스키마의 유효성을 검사합니다. validate가 실패하면 중복 모델, 누락된 관계 대상, 잘못된 필드 타입을 원본 파일에서 수정하고 다시 합성하세요. prisma/schema.prisma를 직접 고치면 다음 sync에서 수정이 사라집니다.
현재 prisma.config.ts는 env('DATABASE_URL')을 사용하므로 validate나 generate도 설정을 읽는 단계에서 이 환경변수가 필요합니다. 그렇다고 이 명령이 실제 DB 테이블을 생성하거나 데이터를 변경하는 것은 아닙니다. 자신의 개발 환경 설정을 확인하고, 이 글의 예제를 위해 운영 환경변수를 새로 덮어쓰지는 마세요.
③ Prisma Client 생성하기
npm run prisma:generate현재 이 스크립트는 prisma:sync 다음 prisma generate를 실행합니다. 앞서 검증 때문에 sync를 실행했더라도 다시 합성되는 것은 정상입니다. 코어 generator가 지정한 Client 출력 위치는 src/core/utils/db/generated이며, 기준 경로는 최종 스키마 파일입니다.
Client 생성은 타입과 쿼리 API를 준비하는 단계입니다. 여기까지 성공해도 DB에 BookmarkEntry 테이블이 생긴 것은 아닙니다. 실제 조회·저장은 다음 마이그레이션 단계가 끝난 뒤에 연결합니다.
4. 합성·Client 생성·마이그레이션은 서로 다르다
prisma:sync: 코어와 확장 원본을 읽어 최종 스키마 파일을 만듭니다.
prisma validate: 스키마 문법과 모델 구성을 검증합니다.
prisma generate: 애플리케이션에서 사용할 Client를 생성합니다.
prisma migrate dev: 개발 DB에서 변경을 검토하고 마이그레이션을 생성·적용하는 명령입니다.
prisma migrate deploy: 준비된 마이그레이션을 배포 대상 DB에 적용합니다. 모델 파일만 보고 새 마이그레이션을 만드는 명령은 아닙니다.
현재 설정의 중요한 경로는 다음과 같습니다. 연결 비밀번호는 스키마 조각에 작성하지 않습니다.
// prisma.config.ts의 주요 설정
schema: 'prisma/schema.prisma',
migrations: {
path: 'src/core/prisma/migrations',
seed: 'dotenv -e .env -e .env.development -- node src/core/prisma/seed.js',
},
datasource: {
url: env('DATABASE_URL'),
},모델 원본은 확장별로 나뉘지만, 현재 마이그레이션 경로는 하나입니다. 확장마다 독립된 마이그레이션 이력을 자동 수집하거나 적용하는 구조는 아닙니다. 설치할 확장 조합과 대상 DB의 기존 이력을 기준으로 프로젝트 차원에서 검토해야 합니다.
아래는 독립된 개발 DB와 필요한 shadow DB 권한을 갖춘 환경에서만 실행하는 개발 예시입니다. 먼저 연결 대상을 확인하고, 기존 프로젝트가 요구하는 DB 변경 검토 절차를 따르세요.
# 개발 전용: 마이그레이션 생성·적용
npm run migrate:dev -- --name add_bookmark_entry
# 변경된 모델에 맞춰 Client 명시적 생성
npm run prisma:generate현재 의존성은 Prisma 7 계열이며, migrate dev가 Client 생성이나 seed까지 대신 실행한다고 가정하지 않습니다. 운영에서는 개발에서 검토한 마이그레이션 파일을 코드와 함께 배포하고, 배포 절차에 따라 migrate deploy를 실행해야 합니다. db push나 reset을 운영 문제의 일반적인 해결책으로 사용하지 않습니다.
5. 운영 전에 알아둘 경계
합성 스크립트에는 파일 정리 작업도 있습니다. 현재 prisma:sync는 생성 경로의 구형 prisma/seed.js와 prisma/migrations를 제거합니다. 마이그레이션 원본은 설정된 src/core/prisma/migrations에 두며, 생성 폴더에 수작업 이력을 보관하지 마세요. 이전 경로를 사용하던 프로젝트는 실행 전에 내용을 확인해야 합니다.
파일을 나눠도 모델 이름 공간은 하나입니다. 다른 확장과 같은 모델·enum 이름을 쓰면 충돌합니다. 확장 간 이름 규칙을 정하고 필요하면 명확한 기능 접두사를 사용하세요.
기존 모델에 필드를 덧붙이는 기능은 없습니다. 확장 조각에 model User를 다시 선언한다고 코어 User에 병합되지 않습니다. 코어에 역방향 관계 필드가 필요한 설계라면 확장 모델만으로 완결되는지 먼저 검토해야 합니다. scalar userId만 두는 방식은 DB 외래키 관계와 다르다는 점도 구분하세요.
탐색은 활성화 설정과 연결되어 있지 않습니다. 메뉴에서 숨기거나 registry에서 등록을 빼도 prisma 원본이 남아 있으면 합성 대상이 될 수 있습니다. 설치된 소스와 실제 사용할 모델을 함께 관리하세요.
확장 삭제는 DB 삭제가 아닙니다. 폴더를 제거하면 다음 생성 스키마에서 모델이 빠질 수 있지만, 기존 테이블은 자동 정리되지 않습니다. 이후 마이그레이션에서 테이블 삭제가 제안될 수 있으므로 데이터 이전·보관·다른 확장의 참조를 검토해야 합니다.
Client와 DB 버전을 함께 맞춰야 합니다. 새 Client가 오래된 DB를 조회하거나, 오래된 앱이 변경된 DB를 사용하면 런타임 오류가 날 수 있습니다. 현재 서비스와 호환되는 변경인지 확인하고 빌드·배포·DB 적용 순서를 정하세요.
UI에서도 모델에 바로 접근하기보다 기존 Action Layer 규칙을 따릅니다. Page/Component → Action → Query → Prisma Client 순서로 연결하고, 입력값과 사용자 권한을 액션 경계에서 검증합니다. 스키마에 필드가 존재한다고 그 필드의 접근 권한까지 자동으로 생기는 것은 아닙니다.
6. 확인 목록과 참고 자료
확장을 배포하기 전에 다음 항목을 확인하세요.
모델 원본이 해당 기능의 prisma 폴더에 있으며, 생성 파일을 직접 수정하지 않았는가?
단일 schema.prisma와 schema/*.prisma 사이에 중복 정의가 없는가?
다른 확장의 모델·enum·관계 대상과 충돌하지 않는가?
sync, validate, generate가 통과하고 필요한 마이그레이션을 별도로 검토했는가?
기존 데이터와 설치된 확장 조합에서 변경·삭제·재배포를 확인했는가?
Plextype의 스키마 합성은 모델의 소유권을 확장 모듈에 남기면서, Prisma에는 하나의 일관된 입력을 제공하는 장치입니다. 폴더를 나눴다는 사실보다 원본과 생성물, Client와 DB, 확장과 코어의 책임을 구분하는 것이 중요합니다. 작은 독립 모델로 흐름을 확인한 뒤 관계와 운영 절차를 넓혀가세요.
