JS

GraphQL Server 구성

728x90

이 포스팅은 기존의 GraphQL-Yoga 에서 탈피하여

GraphQL, Express, Apollo 를 활용하여 서버 구성을 했던 과정을 기록한 요약한것이다.

 

 

# GraphQL-Yoga

GraphQL 을 처음 배웠을때는, graphql-yoga 를 이용해서 배웠었다.

yoga 는 서버 환경 설정시에 겪게 되는 여러 오류 사항에서 벗어나

편하게 프로그래밍 할 수 있는 환경을 제공해줬다.

 

그러나, 왜 그런지는 모르겠지만,

꽤 오랜 시간 yoga 패키지에 대한 업데이트가 없었고,

yoga 로 만든 프로젝트를 깃헙에 올리면 패키지 보안 취약성 오류를 내고 있었다.

 

 

그리고 단순히 npm audit fix 로는 yoga 패키지 자체가 가지고 있는

여러 의존 패키지들에 대한 보안 취약성 문제를 처리 할 수 없었다.

yoga 패키지 자체를 수정해야 했고, 그렇게 하는것 보단 yoga 대신에 직접 서버를 구축하는게 낫다고 생각했다.

 

 

 

# 패키지 탐색

1. Apollo

출처 : https://www.apollographql.com/docs/apollo-server/

Apollo 는 GraphQL 기반의 플랫폼으로, 위 그림에서도 볼 수 있듯, 웹 뿐 아니라, 다양한 클라이언트 환경에 맞는 플랫폼을 제공하며, GraphQL 서버를 구축함에 있어서 꼭 필요한 요소이다.

Apollo 의 최대 장점은 기존의 Redux 방식보다 SSR 을 간소화 했다는 점이다.

(참조 :  d2.naver.com/helloworld/2838729)

 

 

 

가장 먼저 해야할일은 apollo server 를 구축해야 했다.

그래서 apollo-server 패키지를 설치해줘야하고,

나는 Express 를 기반으로 할 것이기 때문에 

apollo-server-express 를 설치했다.

npm install apollo-server-express
cs

 

(github.com/apollographql/apollo-server 를 보면 Express 대신에 다른 패키지도 여러개 존재한다.)

 

 

설치 후에, 가장 기본적인 객체 생성은 다음과 같이하고,

schema 와 resolver 값은 필수로 객체 안에 들어가야 한다.

 

- server.js

import { ApolloServer, gql } from "apollo-server-express";
 
const typeDefs = gql`
    type Query {
        test: String
    }
`;
 
const resolvers = gql`
    Query: {
        test: () => "good",
    }
`;
 
const server = new ApolloServer({
    typeDefs,
    resolvers,
});
 
server.listen().then(({url}) => {
    console.log(`server listening : ${url}`);
})
cs

 

그러나 이렇게 작성하면, 리졸버와 스키마의 양이 많아지면 가독성이 상당히 떨어진다.

그래서, @graphql-tools 를 사용해서 리졸버와 스키마 파일을 한꺼번에 묶어서 처리하려 했다.

 

먼저, 관련 패키지들을 설치한다.

npm install @graphql-tools/schema @graphql-tools/merge @graphql-tools/load-files
cs

 

그 후, schema.js 파일을 작성하고 아래와 같이 코드를 작성했다.

 

- 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

 

@graphql-tools/schema 는 스키마와 리졸버를 한번에 묶어서 하나의 스키마로 처리하기 위한 패키지이고,

 

@graphql-tools/merge 는 리졸버와 스키마 각각 파일을 묶어서 통합하기 위한 패키지이며,

 

@graphql-tools/load-files 는 파일 로딩을 위한 패키지이다.

 

이렇게 작성하게되면,

"앞으로 모든 스키마와 리졸버 파일을 api 폴더 내에만 작성해야 한다."

(이 경로를 벗어나면 파일을 찾지 못하게 되어서 null 로 리턴된다)

 

기존 server.js 코드에 있는 리졸버와 스키마 코드를 걷어내고 schema.js 를 불러와서 처리한다.

 

- server.js

import { ApolloServer } from "apollo-server-express";
import schema from "./schema";
 
const server = new ApolloServer({
    schema,
});
 
server.listen().then(({url}) => {
    console.log(`server listening : ${url}`);
})
cs

 

2. Express

이전까지는 스키마와 리졸버에 대한 처리를 담당하는 서버를 구성했을뿐

완전한 백엔드 서버를 구성하지는 못했다.

그래서 express 와 관련된 여러 패키지들을 설치해주었다.

npm install express helmet body-parser morgan graphql graphql-playground-middleware-express
cs

 

그리고 나서 server.js 를 다음과 같이 수정했다.

- server.js

import dotenv from "dotenv";
dotenv.config();
import express from "express";
import bodyParser from "body-parser";
import helmet from "helmet";
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(bodyParser.json());
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

 

graphql-playground-middleware-express 는

express 환경에서 GraphQL Playground 를 사용하기 위해 필요한 패키지 이다.

 

그러나 이대로만 코드를 짜서 실행하면

content security policy 에 의해 실행이 되지 않을 것이다.

 

 

그래서 이에 대한 해결책으로 

helmet-csp 패키지를 설치하여

npm install helmet-csp
cs

 

다음과 같은 코드를 server.js 에 추가해줬다.

import csp from "helmet-csp";
 
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"],
    },
  })
);
cs

 

 

 

이후 서버를 실행시키면

아래 그림처럼 정상적으로 Playground 가 나오게 되었다.

 

 

 

- References

1. d2.naver.com/helloworld/2838729

2. github.com/apollographql/apollo-server

3. github.com/prisma-labs/graphql-playground

4. sdy-study.tistory.com/63

 

 

728x90

'JS' 카테고리의 다른 글

schema.prisma 정리  (0) 2020.09.10
prisma 2 query 문제  (0) 2020.09.09
webpack (1)  (0) 2020.09.06
Prisma Migration 관련 오류  (0) 2020.09.03
Prisma 란?  (0) 2020.08.30