Apollo Server 및 GraphQL 시작하기

GGomBee·2022년 2월 22일
0

GraphQL

목록 보기
1/1
post-thumbnail

brew install yarn

$ mkdir server

$ cd server

$ yarn init -y

Apollo Server 및 GraphQL 설치

$ yarn add apollo-server graphql

Apollo Server는 GraphQL이 적용된 서버를 생성할 수 있는 클래스를 제공한다. 비유하자면 create-react-app 패키지와 비슷한 역할이다.

개발도구 설치

$ yarn add nodemon @babel/core @babel/node @babel/preset-env --dev
  • nodemon

    • 실행중인 자바스크립트 파일 변경을 감지하고, 파일이 변경되면 Node를 재실행해 변경 사항이 자동으로 반영되게 도와준다.. 많이 쓰는 패키지라서 yarn global add nodemon으로 설치하면 좋다.
  • babel

    • 우리가 Javascript 최신 문법을 사용하면 자동으로 각 브라우저가 지원하는 Javascript 버전에 맞게 문법을 바꿔준다.

.babelrc 파일 생성

// .babelrc
{
  "presets": ["@babel/preset-env"]
}

babel 패키지의 설정 파일인 .babelrc 파일을 프로젝트 폴더에 새로 생성한다.

package.json 파일 수정

// package.json
{
  ...
  "scripts": {
    "start": "nodemon --exec babel-node src/index.js"
  }
}

package.json에 script 항목을 추가한다. babel과 nodemon을 설치하지 않았으면 위 스크립트 대신 "start": "node index.js"를 추가한다.

데이터베이스 생성

// src/database/movies.js
const movies = [
  {
    id: 1,
    name: '백두산',
    rating: 7
  },
  {
    id: 2,
    name: '히트맨',
    rating: 7
  },
  {
    id: 3,
    name: '남산의 부장들',
    rating: 9
  },
  {
    id: 4,
    name: '겨울왕국2',
    rating: 7
  }
];

export default movies;

나중에 만들 리졸버 파일 안에서 데이터베이스를 처리해도 되지만 명확한 구분을 위해 따로 폴더를 만들어 json 파일 형태로 간단한 데이터베이스를 만들었다.

데이터베이스는 외부에서 가져와도 되고, MySQL, mongoDB 등 여러가지 형태로 직접 생성해도 된다. GraphQL은 서버와 클라이언트 사이에서 오고 가는 쿼리 언어이기 때문에 데이터베이스 형태에 제약이 없다.

스키마와 리졸버 설정

스키마

// src/graphql/typeDefs.js
import { gql } from 'apollo-server';

const typeDefs = gql`
  type Movie {
    id: Int!
    name: String!
    rating: Int!
  }

  type Query {
    movies: [Movie!]!
    movie(id: Int!): Movie
  }

  type Mutation {
    addMovie(name: String!, rating: Int!): Movie!
  }
`;

export default typeDefs;

스키마는 서버에 어떻게 데이터를 요청할지 정의한 파일이다. 요청 시 어떤 데이터를 얼마나 요청할지, 각각의 데이터의 자료형이 무엇이고, 어떤 데이터를 필수로 요청할지에 대한 정보가 담긴다. 즉, 사용자는 반드시 스키마에 정의된 형태로 서버에 요청해야 한다.

  • Query : 데이터베이스에서 데이터를 읽는 요청
  • Mutation : 데이터베이스를 수정하는 요청

스키마엔 QueryMutation와 같이 2가지 요청이 있다. 이렇게 요청을 구분한 이유는 데이터베이스 읽기 요청은 무한정으로 동시에 수행될 수 있지만, 데이터베이스 수정 요청은 순차적으로 수행되야 하기 때문이다. 성능 향상을 위해 편의상 구분했을 것이라고 생각하지만 정확히는 모른다..

스키마엔 Movie의 구조와 자료형도 정의해야 한다. 그래야 GraphQL 서버에서 데이터베이스 구조를 알고 처리할 수 있다.

위 파일은 다음을 의미한다. 서버에 Query 형태로 movies를 요청하면 Movie의 배열이 반드시 반환된다. 서버에 Mutation 형태로 파라미터와 함께 addMovie를 요청하면 Movie가 반드시 반환된다.

  • ! : Not Nullable. 데이터가 꼭 있어야 한다.
  • [] : 배열

리졸버

// src/graphql/resolvers.js
import movies from '../database/movies';

const resolvers = {
  Query: {
    movies: () => movies,
    movie: (_, { id }) => {
      return movies.filter(movie => movie.id === id)[0];
    }
  },
  Mutation: {
    addMovie: (_, { name, rating }) => {
      // 영화 제목 중복 검사
      if (movies.find(movie => movie.name === name)) return null;
      
      // 데이터베이스에 추가
      const newMovie = {
        id: movies.length + 1,
        name,
        rating
      };
      movies.push(newMovie);
      return newMovie;
    }
  }
};

export default resolvers;

리졸버는 사용자가 쿼리를 요청했을 때 이를 서버가 어떻게 처리할지 정의한 파일이다. 리졸버는 요청에 대해 단순히 데이터를 반환할 수도 있지만, 직접 데이터베이스를 찾거나, 메모리에 접근하거나, 다른 API에 요청해서 데이터를 가져올 수 있다.

위 파일은 다음을 의미한다. 데이터베이스를 읽는 요청(Query) 중 movies가 요청되면 ../database/Movies.js에 있는 movies 데이터를 반환한다. 데이터베이스를 수정하는 요청(Mutation) 중 name과 rating을 파라미터로 가진 addMovie가 요청되면, 데이터베이스에 영화를 추가한다.

각 리졸버 항목(moviesmovieaddMovie)의 매개변수 4개까지 있는데 다음과 같다.

  1. parent : 부모 타입 리졸버에서 반환된 결과를 가진 객체 잘 모름
  2. args : 쿼리 요청 시 전달된 파라미터를 가진 객체
  3. context : GraphQL의 모든 리졸버가 공유하는 객체로서 로그인 인증, 데이터베이스 접근 권한 등에 사용한다.
  4. info : 명령 실행 상태 정보를 가진 객체 잘 모름

프로젝트가 커지다보면 나중에는 리졸버 구현이 복잡해질텐데, 이를 해결하기 위해 리졸버 단계에 prisma나 TypeORM 등 데이터베이스 ORM를 사용하기도 한다.

서버 생성

// src/index.js
import { ApolloServer } from 'apollo-server';
import resolvers from './graphql/resolvers';
import typeDefs from './graphql/typeDefs';

// ApolloServer는 스키마와 리졸버가 반드시 필요함
const server = new ApolloServer({
  typeDefs,
  resolvers
});

// listen 함수로 웹 서버 실행
server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});

프로젝트 src 폴더에 index.js 파일을 새로 생성한다.

profile
Stay Hungry, Stay Foolish! 겸손한 개발자 고은비입니다. 언제나 성장하기 위해 노력하며 유의미한 데이터로 사용자의 경험을 향상시키는 방법에 관심이 많습니다. 성장하고 싶어요!! 피드백은 언제나 환영입니다!

0개의 댓글