Prisma Korea
Published in

Prisma Korea

프리즈마 스키마에서 CMS를 생성할 수 있을까?

스키마에 꼭 맞는 타입스크립트 클라이언트를 생성해 주는 것은 프리즈마가 다른 ORM보다 돋보이게 해주는 장점입니다. 그래서 프리즈마가 제공하는 인터페이스를 사용할 때는 복잡한 쿼리를 작성해도 타입을 걱정하는 일이 거의 없죠. 놀랍게도 프리즈마의 코드 제너레이터 로직은 확장 가능하게 설계되어서, 프리즈마 클라이언트 이외의 코드도 생성하게 만드는게 가능합니다.

이런 생각의 연장선으로 프리즈마가 CMS를 생성하게 해보자는 시도를 해보았습니다. 아직 프로젝트는 개념 증명 단계지만, 성공한다면 백오피스나 사내 어드민 페이지로 활용하는게 가능할 것으로 기대됩니다.

참고한 프로젝트

이미 시중에 프리즈마를 활용한 CMS들이 존재하기 때문에, 그 중 눈에 띄는 프로젝트들부터 분석해 보았습니다.

KeystoneJS

KeystoneJS는 ORM이 내장된 headless CMS입니다. Keystone schema API로 데이터베이스를 디자인하면 GraphQL 서버와 Admin UI를 전부 생성해주는 굉장한 개발편의성을 자랑합니다. 2021년에는 프리즈마를 데이터베이스 어댑터로 사용하는걸 지원하면서 다른 headless CMS 경쟁자들과는 차원이 다르다는걸 보여줬습니다. Keystone은 이렇게 우수한 프레임워크지만 데이터베이스를 직접 관리하는 이상, 기존에 있던 db에 붙여서 사용하는건 힘들다는 한계가 있었습니다.

PalJS

개인 개발자가 시작한 GraphQL API + Admin UI 생성도구입니다. 풀스택을 한번에 해결해버린다는 점에서 Keystone과 유사하지만, PalJS는 프리즈마 제너레이터라는 점에서 차별점이 있습니다. 프리즈마 스키마만 있다면 어떤 프로젝트에든 붙여서 쓸 수 있고, prisma instrospection과 조합하면 매우 유연하게 적용할 수 있습니다. 하지만 UI코드가 컴포넌트 단위로 생성되기 때문에 커스텀한 UI를 만드는데에 한계가 있었습니다.

prisma-generator-proto

기존 프리즈마 기반 CMS들의 한계점을 극복하기 위해서는 어떻게 설계하는게 좋을지 고민한 결과 prisma-generator-proto 라는 프로토타입 프로젝트를 만들게 되었습니다.

프리즈마 제너레이터 사용

CMS는 그 자체로 하나의 서비스가 되는 경우도 있지만, 다른 서비스의 데이터를 관리하기 위한 목적으로 사용하는 경우가 많습니다. 그렇다면 이미 존재하는 서비스에 쉽게 붙일 수 있게 설계된 CMS가 범용성에서 우위를 차지할 수 있다고 볼 수 있습니다. Keystone처럼 스스로 데이터베이스 스키마와 migration까지 전부 관리하는 도구는 기존 프로젝트에 붙이기 힘들죠. prisma generate 커맨드에서 생성되는 CMS라면 훨씬 도입하는데 부담이 적을것이라고 판단했습니다.

UI는 리액트 훅으로 제공

PalJS와 같이 단번에 admin UI 전체가 생성되는건 확실히 편리하지만, 제품이 성장함에 따라 UI를 커스텀 해야할 필요가 생기는 시점이 옵니다. Material UI 같은 컴포넌트 라이브러리에서 제공하는 UI를 커스텀 해보려고 시도해보신 분들은 공감하시겠지만 패키지로 제공되는 컴포넌트를 입맛대로 수정하는건 꽤나 번거로운 일입니다. 그래서 prisma-generator-proto 는 UI를 만드는데 필요한 데이터와 기능만 리액트 훅으로 제공해서 admin UI 제작이 쉽지만 충분히 유연하도록 만들었습니다.

작동원리

prisma-generator-proto 를 포함한 프리즈마 제너레이터들은 모두 비슷한 방식으로 작동합니다.

프리즈마 스키마

시작은 프리즈마의 핵심이라고 할 수 있는 schema.prisma 파일부터입니다.

datasource db {
url = env("DATABASE_URL")
provider = "postgresql"
}
generator client {
provider = "prisma-client-js"
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String @db.VarChar(255)
author User? @relation(fields: [authorId], references: [id])
authorId Int?
}

위의 예시에서 볼 수 있듯이 schema.prisma 파일은 세 부분으로 나누어져 있습니다.

  • datasource: 데이터베이스 연결 설정입니다.
  • model: 데이터 모델 정의입니다.
  • generator: 이 스키마를 활용하는 제너레이터를 설정합니다. 기본적으로 prisma-client-js 라는 제너레이터를 사용해서 프리즈마 클라이언트를 생성하게 되는데, 추가적으로 다른 제너레이터를 실행하도록 설정할 수 있습니다.

프리즈마 사용자는 스키마를 직접 작성할 수도 있고, prisma introspection을 사용해서 기존 db에서 스키마를 뽑아내는 것도 가능합니다.

prisma generate

prisma generate 커맨드는 schema.prisma 파일에서 설정해준 제너레이터들을 차례로 실행합니다. 스키마를 파싱하는 작업은 프리즈마가 담당하며, 파싱된 결과물인 DMMF가 각 제너레이터에 전달됩니다. DMMF에는 다음과 같은 자료가 JS 객체 형태로 들어있습니다:

  • 모든 모델 리스트
  • 각 모델 안에 들어있는 필드 리스트
  • 각 필드의 타입, 초기값, nullable 유무
  • 모델 사이의 association

또한, 프리즈마는 output 디렉토리가 어디로 설정되어있는지도 제너레이터에 전달해주기 때문에 제너레이터를 작성하는 개발자는 DMMF를 이용해서 output 디렉토리에 원하는 파일을 쓰기만 하면 됩니다!

진행상황

prisma-generator-proto 가 현재 생성할 수 있는 코드는 다음과 같습니다.

서버 코드 생성

CRUD용 route handler들을 생성해서 Express 라우터 형태로 제공하며, 서버 자체는 생성해주지 않습니다.

사용 예시:

import express from "express";
import { router } from "../__generated__";
const app = express();app.use(express.json());// 제너레이터가 생성한 route handler을 사용.
app.use("/api", router);
app.listen(3001, () => {
console.log("Listening...");
});

클라이언트 코드 생성

생성된 API handler를 활용하는 리액트 훅을 제공합니다. 서버와 마찬가지로 프론트 엔드 프로젝트 자체를 생성해주지는 않습니다.

사용 예시 (모델 이름이 Country 인 경우):

import Form from "../components/Form";
import React from "react";
import Table from "../components/Table";
import styles from "../styles/DataScreen.module.css";
import { useCountry } from "../__generated__/hooks";
const Countries = () => {
// 생성된 훅을 사용해서 CRUD 가능.
const [countries, addHandler, formState, setFormState] = useCountry();
return (
<div className={styles.screen}>
<Form
name="Country"
keys={["name", "population", "continentId", "isUnMember"]}
onSubmit={addHandler}
state={formState}
setState={setFormState}
/>
<Table
name="Country"
list={countries}
keys={["id", "name", "population", "continentId", "isUnMember"]}
/>
</div>
);
};
export default Countries;

NPM 패키지

prisma-generator-proto 는 NPM에서 다운받으실 수 있습니다.

소스코드는 GitHub 리포 에 공개되어 있습니다.

다른 아이디어

저는 CMS를 만들었지만, 다른 사람들이 만든 신기한 제너레이터들도 구경해보고 싶으시다면 이 리스트도 살펴보세요!

프리즈마 코리아 커뮤니티에서도 다양한 시도를 하고 있으니 직접 참여해보고 싶으시다면 카카오톡 오픈채팅에서 문의해주세요.

Originally published at https://0916dhkim.github.io.

--

--

Prisma community held by dooboolab in Korea

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store