[MUKBO.log] Backend Dev_101 = GraphQL

먹보·2023년 1월 22일
0

MUK_BO's General Info.

목록 보기
5/9

지난 게시글에서 API의 설계 원칙 중 하나인 REST API에 대해서 알아보았고 게시글의 주인공 답게 주인공 버프를 받아 단점에 대해서 명확하게 짚고 넘어가질 않았다.

하지만, 많은 기업들이 REST API를 사용하게 되면서 문제점들을 인지하게 되었고 이에 2012년 FACEBOOK(현 META)가 새로운 API 설계 방식인 GraphQL을 개발하게 되었다.

✍ GraphQL은 무엇인가?

API를 위한 쿼리 언어이자 데이터베이스내 존재하는 데이터들을 다루기 위한 쿼리들을 실행하는데 도움을 주는 런타임이다. GraphQL은 당신의 API 데이터에 대한 완전하고 이해하기 쉬운 설명을 제공하며, 클라이언트가 필요한 정보를 정확하게 전달하고 시간이 지남에 따라 API를 발전시키는 강력한 개발자 도구이다

✍ GraphQL의 등장배경

2012년 REST API의 명확한 특정 단점들로 인해 FACEBOOK에 의해 GraphQL이 등장하게 되면서 다음과 같은 문제가 해결이 되게 된다.

📝 Over-fetching

  • 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은 막는다.

📝 Under-fetching

  • REST API에서 다양한 URL을 사용해서 받아야할 정보를 GraphQL에서는 하나의 리퀘스트로 다양한 데이터를 받아올 수 있다.

이거는 사실 직접적으로 GraphQL을 다뤄봐야 알게되는 기술이기에 추후 다시 한 번 언급할 예정이다.

✍ GraphQL 설계

GraphQL은 위에서 설명한 것처럼 독자적인 쿼리 언어이기에 실행 환경을 설정해 줄 필요가 있는데 이 때 사용되는 것이 바로 Apollo이다

📝 Apollo Server

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}`);
});

간단하게 설명을 하자면,

  1. typeDefs : 데이터베이스에 저장되어있는 데이터의 구조 및 요청 방식을 정의 내린 것으로 생각하면 된다.
    • Query in typeDefs : 반드시 써야하는 부분 중 하나로 GET 요청을 쿼리로 전환한 것이다.
    • Mutation in typeDefs : 데이터 변화를 줄 수 있는 post,delete와 같은 요청들을 쿼리로 전환한 것이다.
    • 그 외 나머지들은 각 데이터의 구조를 정의 한 것이다.
  2. resolvers : Query 또는 Mutation 타입내 특정 필드가 요청되었을 때 실행될 함수들의 모음이다. 이 필드 내부에 서비스 또는 비즈니스 로직이 작성되는 구간이라고 보면 된다.

저런 식으로 초기 세팅을 해줘어야 GraphQL을 제대로 쓸 수 있다.

✍ GraphQL 단점

이러한 GraphQL에도 단점이 있으며 이는 다음과 같다.

  1. 고정된 요청과 응답이 필요할 때에는 query로 인해 요청의 크기가 Restful보다 커질 수 있다.
  2. 캐싱이 REST 보다 복잡하다
  3. 파일 업로드 구현 방법이 정해져 있지 않아 직접 구현해야 한다.
  4. application/json 형식만 받을 수 있다.
  5. Client에서도 스키마 관리가 필요하다.
profile
🍖먹은 만큼 성장하는 개발자👩‍💻

0개의 댓글