# Prisma 2
Prisma 의 정의를 보면 아래와 같이 서술 되어 있다.
"Prisma is an open-source database toolkit. It replaces traditional ORMs and makes database access easy with an auto-generated query builder for TypeScript & Node.js."
-> 말이 거창하게 써있는것 처럼 보이는데 그냥 JS, TS 에서 쓰는 ORM 이다.
* Prisma 의 연산 과정
Prisma 가 작업을 수행하는 과정을 대략적으로 요약하면 아래 그림과 같다
Node.js 에서 (백엔드 서버 단) Prisma Client 를 생성하고,
Query Engine 을 이용해서 DB 와 연결한다
그리고 백엔드 서버에서 어떤 요청을 DB 로 보내면
Query Engine 이 이에 맞춰서 SQL 로 전환하여 DB 에서 연산을 수행하도록 하게 하고,
이에 대한 결과를 JS 의 Object 값으로 받아온다
(보통 Prisma 를 사용하면 GraphQL 을 이용해서 DB 를 이용하는것이 일반적이다)
그리고 Prisma 와 DB 간에 연산을 위한 대표적인 두 명령어가 있는데
prisma migrate 와 prisma introspect 이다.
* Prisma Introspection
Introspection 은 한마디로 말하면 DB 에 있는 스키마를 그대로 받아와서 prisma 의 schema 로 전환하는 것이다.
DB 에 이미 스키마가 정의되어 있거나, 어떤 변화가 DB 내부에서 생긴경우,
이 변화된 값을 Prisma 에서도 알아야 맞춰갈 수 있기 때문에,
이 명령어를 사용해서 Prisma 에도 변경된 사항을 알려줘야 한다.
* Prisma Migration
이 명령어는 위의 introspection 과는 반대로,
Prisma 에 변화가 생겼을 때, DB 에 알려주는 방식이다.
* Prisma Studio
원래 Prisma 1 에서는 Prisma 사이트 안에다가 서버 올리고, 관리도 모두 사이트 내에서 했었다.
그러나 Prisma 2 는 개발자 각자가 관리를 하는 방식으로 바뀌었고,
별도의 GUI 환경이 없었다.
아직은 완전히 개발된 단계는 아닌것 같긴한데
prisma studio 를 실행하면 아래 같은 모습으로 나오게 된다.
아직까지는, prisma migrate 와 prisma studio 가 "experimental" 키워드가 붙은것으로 봐서는
완전히 개발된 단계는 아닌것 같다.
그러면, 직접 Prisma 코드를 작성하여 어떤지 알아보자.
# 코드 작성 - 1. DB 연결 및 Prisma 설정
여기서 DB 는 MySQL 을 사용할 것이고,
Prisma 가 지원하는 DB 는 SQLite, PostgreSQL, MySQL, MariaDB, AWS Aurora 가 있으나,
실질적으로는, PostgreSQL, SQLite, MySQL 만 지원한다고 본다.
개인적인 생각으론 그냥 PostgreSQL 쓰는게 제일 나은 것 같다.
(아래의 내용들은 이미 node.js 와 MySQL 이 설치 되어 있고, javascript 와 GraphQL 에 대한 이해도가 이미 어느 정도 있다고 가정한다
또한 이 내용은 "Prisma 2" 를 기준으로 하기 때문에 Prisma 1 에서의 실행방법은 다소 차이가 있다.)
먼저, @prisma/cli 를 설치한다.
npm install @prisma/cli --save-dev
|
cs |
그 다음 초기 Prisma Schema 파일을 생성하기 위해 다음과 같은 명령어를 입력한다
npx prisma init
|
cs |
이 명령을 수행하면 현재 폴더에 prisma/schema.prisma 라는 파일이 생성된다.
그리고 schema.prisma 파일에 다음과 같이 작성하여
DB 와 연결을 시도한다.
- schema.prisma
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
|
cs |
이는 DB 를 MySQL 을 사용하겠다는 의미이고,
env("DATABASE_URL") 은
prisma 폴더에 .env 파일이 있어야 하며,
.env 파일에는 아래처럼 정의되어 있어야 한다.
- .env
DATABASE_URL="mysql://username:password@localhost:3306/dbname"
|
cs |
이 부분은 어떤 DB 를 쓰는 가에 따라서 값이 달라진다.
(이에 대한 좀 더 자세한 사용법은 https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/prisma-schema-reference/#datasource 참조)
그리고 prisma client 를 백엔드 서버에 이용하기 위해서
@prisma/client 를 설치해준다.
npm install @prisma/client
|
cs |
그리고 prisma client 에 대한 설정값이 schema.prisma 파일에 설정되어야 한다.
schema.prisma 파일에 아래의 내용을 추가한다
- schema.prisma
generator client {
provider = "prisma-client-js"
}
|
cs |
실제 테스트를 해보기 위해서
schema.prisma 파일에 model 을 정의해준다
(model 이란 관계형 데이터베이스에서 테이블과 같은 개념이다)
- schema.prisma
model User {
id Int @default(autoincrement()) @id
avatarUrl String? @default("")
username String
email String @unique
password String
phoneNum String @default("") @unique
bio String? @default("")
createdAt DateTime? @default(now())
}
|
cs |
최대한 간단하게 테스트만 해보고자 model 을 하나만 정의했다.
단순히 유저 생성만 테스트 해볼 것이다.
그리고, MySQL 에도 이 모델이 테이블 형태로 들어가야 하기 때문에
prisma migrate 명령어를 넣어줘서 테이블을 만들어야 한다.
npx prisma migrate save --name init --experimental && npx prisma migrate up --experimental
|
cs |
이 명령어를 입력해서 migration 을 시도한다.
(prisma migration 에 대한 더 자세한 내용은 https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-migrate 참조)
# 코드 작성 - 2. GraphQL, JS
백엔드 서버를 REST 기반의 Express 를 써도 되고 다른 프레임워크를 써도 상관은 없다.
REST 기반의 프레임워크를 이용해서 작성하고 싶다면 https://www.prisma.io/docs/understand-prisma/prisma-in-your-stack/rest 를 참조
여기서 필자는 GraphQL 을 사용할 것이다.
(GraphQL 을 모른다면 GraphQL 에 대한 자세한 설명은 https://tech.kakao.com/2019/08/01/graphql-basic/ 를 참조)
GraphQL 로 백엔드 서버를 구성할때, 다양한 방식이 있지만
여기서는 graphql 과 apollo-server-express 를 이용해서 구성할 것이다.
먼저 관련된 패키지들을 설치를 해주고,
npm install graphql express apollo-server-express body-parser helmet helmet-csp dotenv morgan graphql-playground-middleware-express
|
cs |
서버 파일을 다음과 같이 구성한다
- server.js
import dotenv from "dotenv";
dotenv.config();
import express from "express";
import bodyParser from "body-parser";
import helmet from "helmet";
import csp from "helmet-csp";
import expressPlayground from "graphql-playground-middleware-express";
import { ApolloServer } from "apollo-server-express";
import morgan from "morgan";
import schema from "./schema";
const PORT = process.env.PORT;
const server = new ApolloServer({
schema,
});
const app = express();
app.use(helmet());
app.use(
csp({
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
styleSrcElem: [
"'self'",
"fonts.googleapis.com",
"cdn.jsdelivr.net",
"'unsafe-inline'",
],
imgSrc: ["'self'", "cdn.jsdelivr.net"],
scriptSrcElem: ["'self'", "cdn.jsdelivr.net", "'unsafe-inline'"],
fontSrc: ["'self'", "'unsafe-inline'", "fonts.gstatic.com"],
},
})
);
app.use(bodyParser.json(), cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(morgan("dev"));
app.get("/", expressPlayground({ endpoint: "/graphql" }));
server.applyMiddleware({ app });
const handleListening = () => {
console.log(`Server ready at http://localhost:${PORT}`);
};
app.listen(PORT, handleListening);
|
cs |
위 코드에서 csp 설정은 꼭 이렇게 맞출 필요는 없고, 그냥 각자 원하는 대로 맞추면 된다.
(csp 에 대한 자세한 설명은 https://sdy-study.tistory.com/63 를 참조)
다음으로,
GraphQL 에서 Resolver 와 TypeDefs 설정을 위해서 다음의 패키지들을 설치한다
(Resolver 와 TypeDefs 에 대한 내용을 모르면 https://www.apollographql.com/docs/apollo-server/api/graphql-tools/#makeexecutableschemaoptions 를 참조)
npm install @graphql-tools/schema @graphql-tools/merge @graphql-tools/load-files
|
cs |
- schema.js
import path from "path";
import { makeExecutableSchema } from "@graphql-tools/schema";
import { mergeTypeDefs, mergeResolvers } from "@graphql-tools/merge";
import { loadFilesSync } from "@graphql-tools/load-files";
const allTypes = loadFilesSync(path.join(__dirname, "api/**/*.graphql"));
const allResolvers = loadFilesSync(path.join(__dirname, "api/**/*.js"));
const schema = makeExecutableSchema({
typeDefs: mergeTypeDefs(allTypes),
resolvers: mergeResolvers(allResolvers),
});
export default schema;
|
cs |
다음으로, Resolver 와 TypeDefs 파일을 만들것인데
주의할 점이 한가지 있다면 schema.js 에서 설정한 Resolver 와 TypeDefs 에 대한 위치가 설정 되었기 때문에
path.join(__dirname, "api/**/*.graph"); 이 값에 맞도록 파일 구조를 만들어야 한다
벗어날 경우 graphql 이 제대로 실행되지 않고 [object Object] 만 리턴한다.
그래서 파일 구조를 아래와 같이 설정해야 한다.
|- prisma
| |- .env
| |- schema.prisma
|- src
| |- api
| | |- User
| | | |- createAccount
| | | | |- createAccount.js
| | | | |- createAccount.graphql
| | | |- User.js
| | |- models.graphql
| |- schema.js
| |- server.js
| |- utils.js
|
cs |
아래처럼 파일들을 생성한다.
- utils.js
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient();
|
cs |
- models.graphql
type User {
id: ID!
avatarUrl: String
username: String!
email: String!
password: String!
phoneNum: String!
bio: String
createdAt: String
}
|
cs |
아래처럼 Resolver 에 해당하는 createAccount.js 를 만든다.
- createAccount.js
import { prisma } from "../../../utils";
export default {
Mutation: {
createAccount: async (_, args) => {
const { username, password, email, phoneNum, bio, avatarUrl } = args;
const user = await prisma.user.create({
data: {
username,
email,
password,
phoneNum,
bio,
avatarUrl,
},
});
return user;
},
},
};
|
cs |
그 다음으로 TypeDefs 파일인 createAccount.graphql 파일을 만든다.
- createAccount.graphql
type Mutation {
createAccount(
username: String!
email: String!
password: String!
phoneNum: String!
bio: String
avatarUrl: String
): User!
}
|
cs |
# Test
이제 직접 코드를 실행해보고 어떤지 파악해보자
백엔드 서버를 실행시키면, graphql playground 가 나오고
여기에 다음과 같이 입력을 해서 유저 계정 하나를 생성하면
'abc' 라는 이름의 유저가 하나 생성되었다.
prisma 2 에서 제공하는 prisma studio 를 실행시켜서 확인해보면
아래 그림처럼 레코드가 하나 생성되었음을 알 수 있고,
(password 값이 다르게 나오는 것은 나는 bcrypt 를 사용해서 비밀번호를 해시로 암호화해서 넣었기 때문이다)
MySQL 의 테이블에도 실제로 들어갔는지 확인해보면
레코드를 실제로 만들어서 넣은 것을 볼 수 있다.
- Reference
1. https://en.wikipedia.org/wiki/Object-relational_impedance_mismatch
2. https://www.quora.com/What-is-the-object-relational-impedance-mismatch
4. https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-migrate
5. https://www.prisma.io/docs/understand-prisma/prisma-in-your-stack/rest
6. https://tech.kakao.com/2019/08/01/graphql-basic/
7. https://sdy-study.tistory.com/63
8. https://www.apollographql.com/docs/apollo-server/api/graphql-tools/#makeexecutableschemaoptions
'JS' 카테고리의 다른 글
webpack (1) (0) | 2020.09.06 |
---|---|
Prisma Migration 관련 오류 (0) | 2020.09.03 |
CSP (Content Security Policy) 란? (0) | 2020.08.30 |
#33 가지 Javascript 필수 개념 - 5. Equals (0) | 2020.08.22 |
CORS (Cross Origin Resource Sharing) (0) | 2020.08.19 |