Apollo server를 통해 어떤 방식으로 객체가 생성되고 반환되는지를 직접 확인했으니, 이제 API를 직접 작성해보자
지금 작업한 03-06-graphql-api-with-apollo-server 폴더를 복사해서
03-07-graphql-api-with-apollo-server-board로 변경해준다.
(node_modules는 제외)
node_modules는 제외하고 복사했으니 다시 yarn install을 해야한다.
ApolloServer 내에 적용시켜줘야 한다.
// index.js
const app = new ApolloServer({
typeDefs: typeDefs,
resolvers: resolvers,
cors: true, // 모든 사이트 허용하고 싶을 때
// cors: { origin: ["https://naver.com", "https://daum.net"] } // 특정 사이트만 지정하고 싶을 때
});
cors: true 로 설정하면 모든 사이트에 대해 접속을 허용한다는 의미이고,
cors: {origin:"url"} 로 설정하면 특정 origin만 접속을 허용한다는 의미이다.
이전 GraphQL-API를 연습할 때 확인했던 fetchBoards와 createBoard API를 직접 만들어보자
그 전에, 다시 한번 상기하자면 resolvers 부분이 express의 API와 같고, typeDefs는 api-docs를 구성하고 응답으로 돌려줄 타입을 정의해주는 부분이다.
GraphQL을 통해 api 를 생성할 떄는 api를 정의해주는 resolvers와 그 api의 docs를 정의해주는 typeDefs 2개를 정의해줘야 한다.
index.js 파일의 내용을 수정하여 Query API인 fetchBoards를 만들어보자
Rest API에서는 GET 메서드(app.get)를 사용해서 데이터를 조회해서 반환해 주었다.
동일한 데이터를 반환하는 기능을 GraphQL-API로 변경해 보자.
// index.js
const resolvers = {
Query: {
fetchBoards: (_, args) => {
// 게시글 목록조회
// 1. DB에 접속 후 데이터를 조회 => 데이터를 조회했다고 가정
const result = [
{
number: 1,
writer: "tom",
title: "tom and jerry",
contents: "contents",
},
{
number: 2,
writer: "jerry",
title: "tom and jerry",
contents: "this is jerry",
},
{
number: 3,
writer: "glue",
title: "this is title",
contents: "this is glue",
},
];
// 2. DB에서 꺼내온 결과를 브라우저에 응답으로 주기 (return)
return result;
},
},
}
Query 내부에 데이터를 조회하는 로직을 작성해야 한다.
Rest-API에서는 res.send를 이용해서 데이터를 반환했는데 graphql-API는 return을 사용해 함수를 종료하면서 데이터를 반환한다
resolvers 에서 반환되는 값들의 type 또한 typeDefs에서 지정해 주어야 한다.
resolvers 에 작성한 Query의 fetchBoards 함수의 반환값을 한번 확인해보자
# index.js
const typeDefs = `#graphql
type myResultType {
# {number: ~, writer: ~~,,,,} <- 1 개를 의미한다.
number: Int
writer: String
title: String
contents: String
}
type Query {
# fetchBoards: myResultType # 객체 1개를 의미함
fetchBoards: [myResultType] # 배열 안에 객체 1개이상을 의미한다.
}
`;
fetchBoards api의 반환값은 배열 안에 객체의 형태로 이루어져 있다.
먼저 객체의 타입을 지정해주기 위해서 type MyResult {}의 선언해주고 요소의 타입들 지정해야 한다.
객체안의 요소들의 타입을 모두 지정해 주었다면 MyResult을 배열로 감싸서 fetchBoards: [MyResult] 과 같이 지정해 준다.
다시 서버를 실행시켜 주고 localhost:4000/graphql 로 접속하여 기존과 동일하게 API 테스트를 해보자
# index.js
query {
fetchBoards {
number
writer
title
contents
}
}

resolvers 객체 안의 Query 객체에 작성해 놓은 fetchBoards 함수가 실행되면서 type Query에 지정해 놓은 타입과 type MyResult에 지정해 놓은 타입에 맞게 데이터의 결과 값이 반환 된 것을 확인할 수 있다.
이번엔 게시글 등록 API를 만들어 보자. 게시글 등록은resolvers 객체 안에 Mutation 객체를 생성하면 된다.
생성된 함수를 통하여 입력값을 받아 올 수 있는데, 이 함수는 4개의 매개변수(parameter)를 가질 수 있다.
// index.js
const resolvers = {
Mutation: {
createBoard: (parent, args, context, info) => {
},
},
};
4개의 매개변수는 다음과 같은 역할을 한다.
parent: 부모 타입 resolver에서 반환된 결과를 가진 객체args: 쿼리 요청 시 전달된 parameter를 가진 객체context: GraphQL의 모든 resolver가 공유하는 객체로서 로그인 인증, 데이터베이스 접근 권한 등에 사용info: 명령 실행 상태 정보를 가진 객체
Rest-API에서는 요청 데이터를 확인하기 위해 매개변수로 req를 사용했다.
GraphQL-API에서는 4개의 매개변수 중 요청 데이터를 확인 가능한 args를 사용하여 입력값을 가져오겠다. 사용하지 않는 매개변수는 _(언더바)로 선언하는 것을 기억해 두자.
// index.js
const resolvers = {
Query: {
fetchBoards: () => {
// ...
},
},
Mutation: {
// graphql 은 (req, res) 가 아니라 (parent, args, context, info)가 들어온다.
createBoard: (_, args) => {
// 1. 브라우저에서 보내준 데이터를 확인하기
console.log(args.createBoardInput.writer);
console.log(args.createBoardInput.title);
console.log(args.createBoardInput.contents);
// 다른 백엔드에서 요청할때는 parent 로 들어오게 된다.
return "게시글 등록에 성공하였습니다!";
},
},
};
이번에는 요청 데이터의 타입을 지정해 보자.
이 때, 유저가 입력하는 데이터의 타입을 지정하기 위해서는 type이 아니라
다음과 같이 input을 사용해 데이터의 타입을 지정해준다.
# index.js
const typeDefs = gql`
# return 타입엔 type 이라고 쓰지만
# 입력값으로 들어오는 input 에는 type 이 아니라 input 이라고 적어야한다.
input CreateBoardInput {
writer: String
title: String
contents: String
}
type Mutation {
# writer, title, contents 3개가 들어간다면
# 만일 필수입력으로 받고 싶다면 ! 를 붙이면 된다.
# createBoard(writer: String, title: String, contents: String!): String
createBoard(createBoardInput: CreateBoardInput!): String
# ! 를 붙여 필수입력하게 해둠, return 은 string 타입
}
`;
다시 apollo server를 실행시켜 주고 localhost:4000/graphql 로 접속하여 기존과 동일하게 API 테스트를 해보자.
# index.js
mutation {
createBoard(createBoardInput: {
writer: "안녕하세요",
title: "제목 연습용",
contents: "글자 연습용!"
})
}
이 떄, createBoard를 사용할 때는
createBoard

아까 적어놓았듯이, input CreateBoardInput에 지정해 놓은 요소들의 데이터 타입에 맞게 요청을 보내야 한다.
resolvers 라는 객체 안의 Mutation 객체에 작성해 놓은 createBoard함수가 실행된다.
이 때, type Mutation 에 지정해 놓은 타입에 맞게 입력값을 받게 되면 결과 응답 메세지가 잘 반환되는 것을 확인할 수 있다.
입력 요청 값이 잘 들어왔는지 터미널을 통해 출력된 값을 확인해 보자
