코딩하는 문과생
[Node.js] Apollo Server & GraphQL 본문
[서론]
진행하던 프로젝트는 더 이상의 진행은 무의미로 판단해 그만두기로 결정했다. 가장 큰 문제점은 프로그램 설계와 개발속도를 꼽을 수 있을 것 같다. 실제 업무에서는 기능별로 스프링 코드를 나누고, 본인은 객체 단위로 스프링코드를 나누려 하다보니 어디서부터 어떻게 나눠야 할 지 감이 안 잡힌다는 게 첫번째 문제고, 스프링 자체가 하나의 기능을 위해서 작성해야하는 파일이 많다보니 개발 속도가 점점 더뎌지면서 어느 순간부터 프로젝트를 잘 보지 않게 되었다. (ㅠㅠ)
쨌든 스프링은 잠시접어두고, GraphQL이라는 개념이 요즘 핫하게 뜨길래 궁금하기도 하고 간단한 프로젝트도 해볼 겸 필요한 내용은 블로그에 정리해보려 한다.
[아키텍처]
아직 사실 잘 모르는데, 대충 지금까지 파악한 아키텍처는 다음과 같다.

왜 Apollo Server인가? 단순히 필요한 설정만 몇 개만 하면 백엔드 구성을 쉽게 할 수 있기 때문에, GraphQL과 많이 사용되는 패키지다.
1. 클라인언트와 서버간 통신을 REST API방식이 아닌 GraphQL을 이용해 서로 데이터를 주고받는다.
2. 서버에서 DB에 데이터를 INOUT할 때는 Prisma라는 ORM을 사용한다.
[GraphQL API]
- GraphQL
일반적으로 서버와 클라이언트간 통신을 위해 Rest API통신을 많이 사용한다. 하지만 최근 GraphQL API가 새로운 통신방식으로 떠오르고 있는데, GraphQL의 가장 큰 특징은 바로 클라이언트에게 요청한 만큼의 데이터를 제공하는 데 우선 순위를 둔다는 것이다. 단순히 문장만 읽어보았을 때는 Rest API와 별 차이가 없다고 생각이 들겠지만, 여기서 핵심은 [요청한 만큼] 이라는 단어이다. 가령 post 객체의 제목과 내용을 가져오는 예시를 들어보면 아래와 같다
REST API | GraphQL API |
HTTP 메소드: GET URL(URI): xxx.com/posts/1 |
query{ posts{ title content } } |
즉, Rest는 HTTP Method와 URI를 이용해 클라이언트가 서버에 자원을 요청 또는 제공하는 반면, GraphQL은 schema와 Resolvers 개념을 이용해 서버와 통신한다. 클라이언트가 필요한 자원을 스키마에 작성해 요청하면 서버쪽에서는 필요한 자원만 모아서 응답하는 형식이다.
- GraphQL의 특징
1. over fetching
클라이언트에서 사용자의 메일 정보가 필요하다고 가정할 때, Rest Api통신에서는 일반적으로 메일 정보 이외 프로필사진이나 나이, 성별 등 당장 필요없는 데이터를 포함해 받는다. 실제 여러 api 개발을 하는 방식을 보면 POSTMAN을 통해 필요한 데이터를 Rest API서버로 전송해 JSON을 리턴받아 어떠한 데이터가 있는 지 확인하고, 다시 클라이언트에서 필요한 정보를 추출한다.
이러한 문제를 over fetching 문제라 부른다. GraphQL에서는 클라이언트 쪽에서 원하는 정보만 요청할 수 있기 때문에 over fetching 문제를 해결할 수 있다.
2. under fetching
한 화면에 필요한 데이터를 출력하기 위해 여러 요청을 서버에 해야하는 데 이를 under fetching 문제라 일컫는다. GraphQL은 클라이언트에서 필요한 정보만을 받을 수 있기 때문에 under fetching 문제를 해결할 수 있다.
- GraphQL의 장단점
- 장점: GraphQL은 클라이언트에 필요한 만큼만 요청하고 응답받기 때문에 네트워크 자원을 절약할 수 있고, 또한 Query 또는 Mutation이라는 단어를 이용해 CRUD를 쉽게 할 수 있다는 점이 있다.
- 단점: 그만큼 서버쪽에서 가공해야하는 데이터가 Rest에 비해 많아지고, 아직은 GraphQL에 익숙치 않은 개발자가 많다는 점들이 있다. 하지만 최근 이를 해결해 주는 여러 솔루션이 오픈소스로 개발되고 있고, 대표적인 예로 Apollo가 있다.
- GraphQL에서 필요한 Schema와 Resolver
- Schema: 일반적으로 Query와 Mutaition으로 나뉘어 지고, CRUD에서 R을 Query가 나머지 CUD를 Mutation에서 맡는다고 보면 된다.
- Resolver: 작성한 Query를 이용해 데이터를 요청하는 역할이라 생각하면 된다
- 예시
. Query & Mutation
import { gql } from "apollo-server";
export default gql `
# prisma 스키마와 일치하는 것이 중요
type Post {
id: Int!
title: String!
content: String!
author: String
createdAt: String!
updatedAt: String!
}
type Query {
posts: [Post]
post(id: Int!): Post
}
type Mutation {
createPost(title: String!, content: String!, author: String): Post
deletePost(id: Int!): Post
updatePost(id: Int!, year: Int!): Post
}
`;
. Resolvers
import client from "../client";
export default {
Query: {
posts: () => client.post.findMany(),
post: (_, { id }) => client.post.findUnique({ where: { id } }),
},
};
[Apollo]
Apollo 서버는 GraphQL API를 제공하는 서버를 개발할 수 있게 도와주는 패키지로서 기존에 Node.js에서 사용하는 Express와 역할이 비슷하다.
Apollo 서버는 앞서 이야기했듯이 Node.js진영의 하나의 패키지로 간단한 코드 몇 줄로 GraphQL 서버 구성이 가능하다.
-예시
. server.js
require("dotenv").config();
import { ApolloServer } from "apollo-server";
import schema from "./schema";
const server = new ApolloServer({
schema,
});
const PORT = process.env.PORT;
server
.listen(PORT)
.then(() => console.log(`Server is running on LocalHost:${PORT}`));
ApolloServer 객체 생성 시, 필요한 schema와 resolvers를 선언해 작성하면 GraphQL서버로 사용이 가능하다.
이후 작성된 서버를 구동하면 PostMan처럼 쿼리를 날려볼 수 있는 화면을 볼 수 있다.


[Prisma]
마지막으로 Prisma에 대해 간단히 정리해보겠다. Prisma는 Django ORM이나 TypeORM처럼 객체와 DB를 맵핑시켜주는 ORM이다.
- 특징
1. Prisma client객체를 이용해 쉽게 CRUD가 가능하다. client를 생성하고, post라는 객체가 있다면, client.post.findMany()를 사용해 원하는 데이터를 가져올 수 있다.
2. 다른 ORM도 지원하는 기능이지만, prisma migrate를 이용해 쉽게 DB 테이블을 구성할 수 있다.
3. 추가로 프로젝트를 진행해봐야 느끼는 바가 있을 것 같다... 아직은 사실 타 ORM과 큰 차이점을 잘 모르겠다.
- 사용법
1. prisma 설치후 DB와 연결시킨다. 처음 prisma 설치 시 .env파일이 생성되며 gitignore에 자동 추가된다. .env에 DB정보를 입력한다.
2. schema.prisma 파일에 생성하고자 하는 객체 스키마를 작성한다.
3. 스키마를 작성하고 $ prisma migrate dev 명령어 작업을 수행하면, 객체를 관리할 수 있는 prisma-client가 함께 생성된다.
4. PrismaClient를 사용해 client객체를 생성한다.
5. Query와 Mutation에서 client객체를 이용해 CRUD를 작성한다.
. schema.prisma
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
model Post {
id Int @id @default(autoincrement())
title String
content String
author String? //Required가 아님
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
. client.js
import { PrismaClient } from "@prisma/client";
const client = new PrismaClient();
export default client;
. post.queries.js(단순 READ 용)
import client from "../client";
export default {
Query: {
posts: () => client.post.findMany(),
post: (_, { id }) => client.post.findUnique({ where: { id } }),
},
};
. post.mutations.js(Create, Update, Delete용)
import client from "../client";
export default {
Mutation: {
createPost: (_, { title, content, author }) =>
client.post.create({
data: {
title,
content,
author,
},
}),
deletePost: (_, { id }) => client.post.delete({ where: { id } }),
updatePost: (_, { id, year }) =>
client.post.update({ where: { id }, data: { year } }),
},
};
'웹 프로그래밍 > Node.js' 카테고리의 다른 글
[Node.js] NestJS & GraphQL (0) | 2021.09.09 |
---|---|
[Node.js] Prisma ORM (0) | 2021.07.20 |