old - prisma schema 분리
서론
글을 작성 하던 도중, prisma 에서 드디어 스키마 분리를 지원해 준다는 이슈가 작성 되었다.
Hey, we just released support for multiple-file Prisma Schema setups! It is behind the prismaSchemaFolder preview feature:
Release Notes: https://github.com/prisma/prisma/releases/tag/5.15.0
Documentation: https://prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema
Announcement blog post: https://www.prisma.io/blog/organize-your-prisma-schema-with-multi-file-support
Please try it out and leave feedback in this discussion: #24413
이에 따라 기존에 스키마를 분리하는 방식은 굳이 필요 없게 되었지만, 위 내용에 따른 추가 내용은 추후 기재 하도록 하고 기존 방식에 대해서 서술을 먼저 작성 해보아야 겠다.
기존 방식은 심플하게, 각 `model/[table-schema].prisma` 파일들을 병합하여 `schema.prisma`에 out 하는 형태로 작업하였다.
스키마 파일을 합치기 위한 코드 작성
import { exec } from 'child_process';
import { promisify } from 'util';
import chalk from 'chalk';
import os from 'os';
import fs from 'fs';
// promisify를 사용하여 exec 함수를 Promise 기반으로 변환
const execAsync = promisify(exec);
const prismaPath = './prisma'
const schemaPath = `${prismaPath}/schema.prisma`
const tempSchemaPrismaFile = `${prismaPath}/temp_schema.prisma`
const defaultSchemaPrismaFile = `${prismaPath}/default_schema.prisma`
// TODO :: window 환경에서도 돌아갈 수 있도록 처리
const checkOs = () => {
if(os.platform() !== 'linux') {
console.error(chalk.red('You can run this script only on Linux operating systems.'))
process.exit(1);
}
}
const getCurrentDateTime = () => {
const now = new Date();
const year = now.getFullYear().toString().slice(-2);
const month = (now.getMonth() + 1).toString().padStart(2, '0');
const date = now.getDate().toString().padStart(2, '0');
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
const seconds = now.getSeconds().toString().padStart(2, '0');
return `${year}${month}${date}${hours}${minutes}${seconds}`;
}
async function checkFolderExists(folderPath) {
try {
const stats = await fs.promises.stat(folderPath);
return stats.isDirectory();
} catch (err) {
if (err.code === 'ENOENT') {
return false;
}
console.log(chalk.red('folder check fail.'))
process.exit(1);
throw err;
}
}
const createFolder = async (folderPath) => {
try {
await fs.promises.mkdir(folderPath);
} catch (e) {
console.log(chalk.red('An error occurred while creating the backup folder.'))
process.exit(1);
}
}
const rollbackSchema = async (message) => {
console.error(message)
await execAsync('rm ./prisma/temp_schema.prisma');
process.exit(1);
}
const removeFile = async (filePath) => {
return await execAsync(`rm ${filePath}`);
}
const getPrismaModelFileList = async () => {
const { stdout, stderr } = await execAsync('find ./prisma/model -type f -name "*.prisma"');
if (stderr) {
await rollbackSchema(`모델 파일을 읽어 오는 도중 오류가 발생하였습니다.: ${stderr}`)
}
return stdout
}
const newLineTempFile = async() => {
return await execAsync(`echo >> ${tempSchemaPrismaFile}`);
}
const convertModelFile = async (fileList) => {
// prisma/model 디렉토리에 있는 모든 .prisma 파일을 temp_schema.prisma 파일에 추가
// await execAsync('find ./prisma/model -type f -name "*.prisma" -exec cat {} \\; -exec echo \\; >> ./prisma/temp_schema.prisma');
for (const fileName of fileList) {
console.log(chalk.yellow(`READ ${fileName}`))
try {
await execAsync(`cat ${fileName} >> ${tempSchemaPrismaFile}`)
await newLineTempFile()
await newLineTempFile()
} catch (e) {
console.error(chalk.red(e))
await rollbackSchema(`${fileName} MERGE FAILED. ROLLBACK THIS JOB.`)
}
console.log(chalk.green(`MERGE ${fileName} SUCCEED.`))
}
console.log(chalk.green('ALL MODEL FILE CONVERT SUCCEED.'))
}
async function updateSchema() {
checkOs()
try {
// 백업 폴더 없으면 생성
if(!await checkFolderExists(`${prismaPath}/backup`)) { await createFolder(`${prismaPath}/backup`) }
// 현재 스키마 정보 백업
await execAsync(`cat ${schemaPath} >> ${prismaPath}/backup/${getCurrentDateTime()}_schema.prisma`)
// default_schema.prisma 파일을 temp_schema.prisma 파일에 추가
await execAsync(`cat ${defaultSchemaPrismaFile} >> ${tempSchemaPrismaFile}`);
// DB 접속정보와 model을 구분하기 위하여 개행 두번 처리
await newLineTempFile()
await newLineTempFile()
// 현재 동기화된 상태를 확인하기 위하여, 각 파일에 따로 접근하도록 처리
const getSchemaFileStdout = await getPrismaModelFileList()
const fileNames = getSchemaFileStdout.trim().split('\n');
await convertModelFile(fileNames)
// temp_schema.prisma 파일을 schema.prisma 파일로 복사
await execAsync(`cat ${tempSchemaPrismaFile} > ${schemaPath}`);
await removeFile(tempSchemaPrismaFile)
} catch (error) {
await rollbackSchema(`ERROR: ${error.message}`)
}
}
// 함수 호출
updateSchema().then(() => {
console.log(chalk.green('😊PRISMA SCHEMA FILE MERGE SUCCEED!😊'))
});
호출 명령어 추가
"scripts": {
"prisma:model": "node prisma-model-merge.mjs",
//...
}
pakage.json 파일에 명령어를 추가하여 위에서 작성한 실행 코드를 해당 명령어로 쉽게 호출 하기 위함이다.
default-schema.json 파일 작성
위에서 작성한 코드로 schema 파일을 산출하게 되면, schema.prisma 파일에 계속 되어 동일 코드가 추가되는 문제가 발생한다.
따라서, 기본 스키마를 작성하고 해당 파일을 토대로 schems.prisma 파일을 산출 할 수 있도록 추가해 준다.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
sample model file 작성
model Test {
creator String
creator_program_id String
creator_ip String
updator String?
updator_program_id String?
updator_ip String?
created_at DateTime @default(now())
updated_at DateTime @default(now())
deleted_at DateTime?
}
테스트를 위하여, sample model 파일을 작성한다.
병합 실행
npm run prisma:model
병합 명령어를 실행한다.
다만 위 코드는 linux 기반에서만 동작하게 되어 있기 때문에 window 환경으로는 wsl을 이용하여 구동시켜보자.
아래와 같이 정상적으로 구동 된다면 모든 스키마 파일이 병합되어 schema.prisma 파일이 생성된다.
NEW - 새로운 방식의 schema 분리 방법 (^5.15.0)
서론
위에서 언급 하였듯 드디어 prisma 에서 schema를 분리 작성 할 수 있게 지원 해 준다.
기존에 위에 언급한 방식으로 작업 하셨던 분들은 크게 추가적으로 작업을 해 줄 필요는 없이 일부만 수정하면 바로 적용이 가능하였다.
지금부터 prisma 5.15.0에서 추가된 schema 분리 작성 방법에 대해 알아보자.
필수 필요 항목
필요 항목
- VS Code
- prisma : ^5.15.0
- @prisma/client: ^5.15.0
INFO
현재 prisma의 schema 분리 방식을 지원해 주는 plugin을 제공해주는 에디터가 VSCode 뿐이다. Intellij 쪽 계열은 아직 지원해 주지 않으니, 작성시에 문법 오류라고 판단 할 수 있다.
기존에 primsa로 작업하고 있던 분들은 package.json 에서 prisma, @prisma/client의 버전을 올려서 작업해야 한다.
구현
Schema.prisma 파일 변경
먼저 `schema.prisma` 파일에 generator client 부분을 다음과 같이 수정한다.
generator client {
provider = "prisma-client-js"
previewFeatures = ["prismaSchemaFolder"] // 추가
}
디렉토리 구조 변경
디렉토리 구조를 아래와 같이 변경한다.
각 schemaFolder 안에는 모델을 정의한 .prisma 파일이 존재한다.
기존 문서 에서 작성한 model을 기준으로 보면 다음과 같은 디렉토리 구조를 가지고 있을 것이다.
prisma
└── schema
├── post
│ └── post.prisma
├── schema.prisma
└── user
└── user.prisma
각 모델 파일들의 코드는 다음과 같다.
model User {
id Int @default(autoincrement()) @id
email String @unique
name String?
posts Post[]
}
model Post {
id Int @default(autoincrement()) @id
title String
content String?
published Boolean? @default(false)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}
TIP
schema 폴더 명은 반드시 schema일 필요는 없다.
다만, schema.prisma 파일은 해당 폴더 하위에 반드시 존재 하여야 한다.
db push 실행
다음 명령어로 db push 를 실행한다.
npx prisma db push --schema prisma/schema
일반적으로 아래와 비슷한 내용을 확인할 수 있다.
실제 DB 스키마에 접근해 보면, 테이블들이 생성 되었음을 확인 할 수 있다.
PS C:\workspace> npx prisma db push --schema prisma/schema
Environment variables loaded from .env
Prisma schema loaded from prisma\schema
Datasource "db": PostgreSQL database "DB", schema "SCHEMA" at "localhost:5432"
Your database is now in sync with your Prisma schema. Done in 117ms
✔ Generated Prisma Client (v5.15.0) to .\node_modules\@prisma\client in 146ms
'JAVASCRIPT > nest.js' 카테고리의 다른 글
[NestJS] NestJS + prisma CRUD (0) | 2024.06.11 |
---|---|
[nest.js] nest.js + prisma - setting (0) | 2024.05.10 |
[nest.js] module reference (2) | 2023.10.02 |
[Nest.js] Circular dependency (0) | 2023.10.02 |