본 시리즈는 정재남님의 풀스택 리액트 라이브코딩 - 간단한 쇼핑몰 만들기 강의 내용을 기반으로, 추가적인 학습을 통해 습득한 지식 또는 강의 코드를 다른 방법으로 구현한 경험을 작성하고 있습니다. 강의 코드(GitHub)를 확인하세요.
@apollo/client
는 React와의 기본 integration을 제공한다.참고 문서: Migrate from apollo-server-express
Apollo Server 3까지는 express를 사용하려면 apollo-server-express 패키지를 설치해야 했지만, Apollo Server 4에서는 expressMiddleware
함수로 GraphQL 서버를 설정하여 apollo-server-express 패키지 대신에 사용할 수 있다.
cors
를 TypeScript 환경에서 사용하려면 @types/cors
도 함께 설치해야 한다.ApolloServer
)을 가져와야 한다. (apollo-server-express, apollo-server-core에서 가져오는 것이 아님)cors
와 bodyParser.json()
을 추가해야 한다./graphql
URL 경로를 사용하는 경우 (즉, path 옵션을 사용하여 다른 URL을 지정하지 않는 경우), expressMiddleware
를 /graphql
에 마운트하여 동작을 유지할 수 있다. 다른 URL 경로를 사용하려면 app.use
를 사용하여 지정된 경로에 서버를 마운트한다.// npm install @apollo/server express graphql cors body-parser
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import express from "express";
import http from "http";
import cors from "cors";
import bodyParser from "body-parser";
import { typeDefs, resolvers } from "./schema";
interface MyContext {
token?: String;
}
// Express와의 통합에 필요한 로직
const app = express();
// httpServer는 Express 앱으로 들어오는 요청을 처리합니다.
// 아래에서는 Apollo 서버에 이 httpServer를 drain하도록 지시하여 서버를 정상적으로 종료할 수 있도록 한다.
const httpServer = http.createServer(app);
// 이전과 동일한 ApolloServer 초기화와 httpServer용 drain 플러그인.
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
// 서버가 시작될 때까지 기다리도록 await를 사용한다.
await server.start();
// CORS, body parsing, `expressMiddleware` 기능을 처리하도록 Express 미들웨어를 설정한다.
app.use(
"/",
cors<cors.CorsRequest>(),
// 50mb는 `startStandaloneServer`가 사용하는 제한이지만 필요에 맞게 구성할 수 있다.
bodyParser.json({ limit: "50mb" }),
// `expressMiddleware`는 `ApolloServer` 인스턴스와 optional configuration options: { context: asynchronousFunction }과 같은 동일한 인수를 허용한다.
expressMiddleware(server, {
context: async ({ req }) => ({ token: req.headers.token }),
})
);
// 수정된 서버 시작
await new Promise<void>((resolve) =>
httpServer.listen({ port: 4000 }, resolve)
);
console.log(`🚀 Server ready at http://localhost:4000/`);
typeDefs
)이며, 함께 실행되는 쿼리의 모양을 정의한다.ApolloServer
인스턴스 생성const server = new ApolloServer({
typeDefs,
resolvers,
});
new
연산자와 함께 ApolloServer
를 호출할 때 typeDefs
속성과 resolvers
속성이 정의된 객체를 인수로 전달한다.context
속성 또한 ApolloServer
를 호출할 때 전달했으나, 4버전에서는 expressMiddleware
또는 startStandaloneServer
에 전달하는 것으로 변경되었다.expressMiddleware
expressMiddleware(ApolloServer Instance, { context: context function })
expressMiddleware
는 Apollo Server를 Express 서버에 연결할 수 있도록 하는 함수이다.expressMiddleware
를 사용하기 위해서는 웹 프레임워크에 대한 HTTP body parsing 및 CORS 헤더를 설정해야 한다. (cors
, body-parser
패키지 설치 필요)ApolloServer Instance
: expressMiddleware
의 첫 번째 인수로, ApolloServer
의 Instance를 전달한다.
expressMiddleware
의 두 번째 인수로, context
속성에 context
function을 값으로 가지는 객체를 전달한다.
context
function는 비동기 함수로 객체를 반환해야 한다.
operation 실행 중에 서버의 모든 resolver가 공유하는 객체를 반환한다. 이를 통해 resolver는 데이터베이스 연결과 같은 유용한 context value를 공유할 수 있다.
TypeScript를 사용하는 경우, ApolloServer
에 contextValue
에 대한 타입이 정의된 context를 전달해야 한다.
interface MyContext {
// You can optionally create a TS interface to set up types for your contextValue
authScope?: String;
}
const server = new ApolloServer<MyContext>({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
// async context function should async and return an object
context: async ({ req, res }) => ({
authScope: getScope(req.headers.authorization),
}),
});
```
context
함수는 express.Request 및 express.Response 객체인 req 및 res 옵션을 받는다.
미들웨어는 HTTP 요청과 응답 사이(middle)에서 단계별 동작을 수행하는 함수이다.
const middelware = (req, res, next) => {
// ...
};
req
)과 응답 객체(res
)를 처리하거나 다음 미들웨어를 실행(next()
)할 수 있다.next
함수를 호출하지 않으면 미들웨어 동작 사이클이 멈춘다.오류 처리 미들웨어는 다른 미들웨어와 달리 (err, req, res, next)
, 4개를 인수로 받는다.
모든 매개변수를 사용하지 않아도 4가지 모두 선언해주어야 Express가 오류 처리 미들웨어로 식별한다.
동일한 경로(path
)에 요청되는 미들웨어를 처리하는 메서드를 모두 작성한 뒤, 오류 처리 미들웨어는 마지막에 메서드와 함께 정의해야 한다.
var bodyParser = require("body-parser");
var methodOverride = require("method-override");
app.use(bodyParser());
app.use(methodOverride());
app.use(function (err, req, res, next) {
// 생략
});
app.use([path,] callback [, callback...])
지정된 미들웨어의 기능을 지정된 경로에 마운트하는 메서드로, 요청된 path
경로의 기준이 일치할 때 미들웨어가 실행된다. 즉, path
로 들어오는 요청에 대한 공통 미들웨어를 적용하기 위해 사용되는 메서드이다.
[path,]
/
)이다.path
와 매칭할 때, path
의 하위 path
들 또한 함께 매칭된다./fruits
을 path
로 등록하면 /fruits/apple
또한 매칭된다./
)를 path
로 매칭하기 때문에 미들웨어는 애플리케이션에 접근하는 모든 요청에 마운트된다.callback
callback
에는 미들웨어 function이나 미들웨어 function이 쉼표(,
)를 기준으로 나열된 list, 미들웨어 function의 배열, 이들의 조합을 전달할 수 있다.app.listen()
listen(port: number, hostname: string, backlog: number, callback?: () => void): http.Server;
listen(port: number, hostname: string, callback?: () => void): http.Server;
listen(port: number, callback?: () => void): http.Server;
listen(callback?: () => void): http.Server;
listen(path: string, callback?: () => void): http.Server;
listen(handle: any, listeningListener?: () => void): http.Server;
createServer()
메소드를 이용하여 서버를 생성하는 것과 동일한 역할을 하나, express에서는 http 모듈과 달리 미들웨어, 라우팅, 세션 관리, 에러 핸들링 등을 미리 구현해둔 서버를 제공하여 사용이 간단하다.cors package는 CORS와 관련된 옵션들을 설정할 수 있는 Express 미들웨어를 제공하는 패키지로, npm에서 설치 가능하다.
cors()
cors(options)
메서드는 CORS와 관련된 설정을 할 수 있도록 만들어진 미들웨어로, 아래 속성을 포함한 options
객체를 인수를 전달할 수 있다.
origin
: Access-Control-Allow-Origin CORS 헤더를 구성할 수 있다. 작성 가능한 값은 아래와 같다.Boolean
: req.header('Origin')
에 정의된 대로 request origin(요청 출처)를 반영하려면 origin을 true로 설정하고, CORS를 비활성화하려면 false로 설정한다.String
: origin을 하나의 특정 origin으로 반영한다.RegExp
: origin 요청을 테스트하는 데 사용할 정규식 패턴으로 origin을 설정한다. 일치할 경우 request origin이 반영된다.Array
: origin을 유효한 origin의 배열, 즉 여러개의 origin을 설정한다. 각 origin은 String 또는 RegExp일 수 있다.credentials
: Access-Control-Allow-Credentials CORS 헤더를 구성한다. 헤더를 전달하려면 true로 설정하고 그렇지 않으면 생략한다.