지난 게시글에서 API의 설계 원칙 중 하나인 REST API
에 대해서 알아보았고 게시글의 주인공 답게 주인공 버프를 받아 단점에 대해서 명확하게 짚고 넘어가질 않았다.
하지만, 많은 기업들이 REST API
를 사용하게 되면서 문제점들을 인지하게 되었고 이에 2012년 FACEBOOK(현 META)
가 새로운 API 설계 방식인 GraphQL
을 개발하게 되었다.
API를 위한 쿼리 언어이자 데이터베이스내 존재하는 데이터들을 다루기 위한 쿼리들을 실행하는데 도움을 주는 런타임이다. GraphQL은 당신의 API 데이터에 대한 완전하고 이해하기 쉬운 설명을 제공하며, 클라이언트가 필요한 정보를 정확하게 전달하고 시간이 지남에 따라 API를 발전시키는 강력한 개발자 도구이다
2012년 REST API
의 명확한 특정 단점들로 인해 FACEBOOK
에 의해 GraphQL
이 등장하게 되면서 다음과 같은 문제가 해결이 되게 된다.
REST API를 사용할 경우 특정 데이터를 URL로 받아낸다면, 불필요한 부분까지 받아내게 되며 백엔드 데이터 전송에 문제가 될 수 있다.
=> 사실, 불필요한 부분은 백엔드 로직에서 걸러내면 되지만 상황은 다양해지고 그만큼 예측 불가능할 수 도 있기에 신경쓰이는 건 매 한가지로 같다.
GraphQL은 우리가 필요한 데이터만 직접적으로 골라서 받아낼 수 있다.
위의 문제를 직접적인 예제를 통해서 얘기하자면,
GET https://yts.mx/api/v2/list_movies.json
REST API와 GET method를 활용하여 외부에서 영화 목록들을 가져와보자. 그럼 다음과 같은 결과를 얻게 되는데..
사실 여기서, 내가 필요 한 정보는 제목과 년도 그리고 런타임이라고 했을 때, 사실 그 외 정보는 낭비라고 볼 수 있다..
이게 바로 우리가 말하는 Over-fetching
의 예이다.
그럼 GraphQL
은 어떻게 요청을 하며 어떤 식으로 데이터를 가져올까?
{
movie {
title
year
runtime
}
}
GraphQL
은 단순하게 필요한 정보만 요청을 할 수 있게 되며, 빼내오는 정보들도 요청한 정보만 빼내와 Over-fetching
은 막는다.
이거는 사실 직접적으로 GraphQL
을 다뤄봐야 알게되는 기술이기에 추후 다시 한 번 언급할 예정이다.
GraphQL은 위에서 설명한 것처럼 독자적인 쿼리 언어이기에 실행 환경을 설정해 줄 필요가 있는데 이 때 사용되는 것이 바로 Apollo
이다
Apollo란 GraphQL의 클라이언트 라이브러리 중 하나로 GraphQL을 사용한다면 거의 필수적으로 사용하는 상태 관리 플랫폼입니다.
Apollo
를 사용하게 되면 GraphQL
의 쿼리언어를 조금 더 쉽게 설계 할 수 있는 환경을 구축하게 된다. 물론 Apollo
외에도 FACEBOOK
에서 만든 Relay
가 있지만 그에 비해 훨씬 러닝커브가 낮고 기능이 많아 많은 기업에서 Apollo
를 사용한다고 한다.
import { ApolloServer, gql } from "apollo-server";
const server = new ApolloServer();
server.listen().then(({ url }) => {
console.log(`Running on ${url}`);
});
아폴로 서버를 활용하여 GraphQL
을 실행할 수 있는 환경을 위와 같이 세팅해 줄 수 있다.
하지만 위와 같이 설정 후 서버를 실행하게 되면, 오류가 발생하는데
Err Message : Apollo Server requires either an existing schema, modules or typeDefs
그 이유는, GraphQL을 사용하기 위해서는 typeDefs
와 같은 번거로운(?) 작업을 해줘야 한다.
간단한 풀세팅을 보여주자면...
import { ApolloServer, gql } from "apollo-server";
const typeDefs = gql`
type Authors {
id: ID!
lastName: String!
firstName: String!
fullName: String!
}
type Book {
id: ID
title: String
author: Authors
}
type Query {
allBooks: [Book!]!
allAuthors: [Authors!]!
book(id: ID!): Book
ping: String!
}
type Mutation {
postBook(text: String!, authorId: ID!): Book!
deleteBook(id: ID!): Boolean
}
`;
const resolvers = {
Query: {
book(root, { id }) {
return books.find((book) => book.id === id);
},
ping() {
return "pong";
},
allBooks() {
return books;
},
allAuthors() {
return authors;
},
},
Mutation: {
postBook(_, { text, authorId }) {
const newBook = {
id: books.length + 1,
text,
};
books.push(newBook);
return newBook;
},
deleteBook(_, { id }) {
const book = books.find((book) => book.id === id);
if (!book) return false;
books.filter((book) => book.id !== id);
return true;
},
},
Authors: {
fullName({ lastName, firstName }) {
return `${firstName} ${lastName}`;
},
},
Book: {
author({ authorId }) {
return authors.find((author) => author.id === authorId);
},
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server.listen().then(({ url }) => {
console.log(`Running on ${url}`);
});
간단하게 설명을 하자면,
GET 요청
을 쿼리로 전환한 것이다.post
,delete
와 같은 요청들을 쿼리로 전환한 것이다.저런 식으로 초기 세팅을 해줘어야 GraphQL을 제대로 쓸 수 있다.
이러한 GraphQL에도 단점이 있으며 이는 다음과 같다.