인프런 강의를 시청하면서 graphQL 에 대해 배운 내용들을 기록하고자 한다.
Int
: A signed 32‐bit integer.Float
: A signed double-precision floating-point value.String
: A UTF‐8 character sequence.Boolean
: true
or false
.ID
: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ID
signifies that it is not intended to be human‐readable.!
기호는 반드시 할당되어야하는 타입이라는 의미이다.apollo-server
apollo-server-express
graphql
$yarn add apollo-server apollo-server-express graphql
server/src/index.js
또는 server/src/index.ts
에 graphQL 사용을 위한 세팅을 한다.
//server/src/index.js
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import schema from './schema/index.js';
import resolvers from './resolvers/index.js';
import { readDB } from './dbController.js';
const server = new ApolloServer({
typeDefs: schema,
resolvers,
context: {
db: {
messages: readDB('messages'),
users: readDB('users')
}
}
});
const app = express();
// graphQL 의 playground 에서 CORS 접근을 허용하기 위함.
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader(
'Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE'
);
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
await server.start();
server.applyMiddleware({
app,
path: '/graphql',
cors: {
origin: ['http://localhost:3000', 'https://studio.apollographql.com/'],
credentials: true
}
});
await app.listen({ port: 8000 });
console.log('server listening on 8000');
server/src/schema/...
경로에 스키마를 정의해준다.
Int, Float
두 가지 키워드를 사용한다.# server/src/schema/index.js
import { gql } from 'apollo-server-express'
import <스키마1> from '<스키마 경로>'
import <스키마2> from '<스키마 경로>'
const linkSchema = gql`
type Query {
_: Boolean # _ : type Query 정의를 index.js 에서 한꺼번에 몰아서 해주기 위해 의미없는 정보를 하나 선언한 것임.
}
type Mutation { # 함수 역할
_: Boolean #
}
`
export default [linkSchema, <스키마1>, <스키마2]
# server/src/schema/index.js
import { gql } from 'apollo-server-express'
import messageSchema from './message.js'
import userSchema from './user.js'
const linkSchema = gql`
type Query {
_: Boolean # _ : type Query 정의를 index.js 에서 한꺼번에 몰아서 해주기 위해 의미없는 정보를 하나 선언한 것임.
}
type Mutation {
_: Boolean # type Query를
}
`
export default [linkSchema, messageSchema, userSchema]
# server/src/schema/message.js
import { gql } from 'apollo-server-express'
const messageSchema = gql`
type Message {
id: ID!
text: String!
user: User!
timestamp: Float #13자리 숫자
}
extend type Query {
messages(cursor: ID): [Message!]!
message(id: ID!): Message!
}
extend type Mutation {
createMessage(text: String!, userId: ID!): Message!
updateMessage(id: ID!, text: String!, userId: ID!): Message!
deleteMessage(id: ID!, userId: ID!): ID!
}
`
export default messageShema;
# server/src/schema/user.js
import { gql } from 'apollo-server-express'
const userSchema = gql`
type User {
id: ID!
nickname: String!
}
extend type Query {
users(): [User!]!
user(id: ID!): User
}
`
export default userShema;
기존 REST API의 routes 를 대체할 resolver
을 정의한다.
보통 /server/src/resolvers/...
경로를 이용한다.
db
파라미터는 서버 루트 파일에서 정의한 context 의 데이터를 뜻한다.# /server/src/index.js
const server = new ApolloServer({
typeDefs: schema,
resolvers,
context: {
db: {
messages: readDB('messages'),
users: readDB('users')
}
}
});
# /server/src/resolvers/index.js
import messageResolver from './message.js';
import userResolver from './users.js';
export default [messageResolver, userResolver];
# /server/src/resolvers/message.js
import { v4 } from 'uuid';
import { writeDB } from '../dbController.js';
const setMsgs = (newMsgs) => writeDB('messages', newMsgs);
/**
* parent: 거의 사용 x
* args: Query에 필요한 필드에 제공되는 인수(parameter)
* context: 로그인한 사용자. DB Access 등의 중요한 정보들
*/
const messageResolver = {
Query: {
// 59 line: GET MESSAGES (전체 메세지 조회) API 기능과 같음
messages: (parent, args, { db }) => {
return db.messages;
},
// 70 line: GET MESSAGE (단일 메세지 조회) API 기능과 같음
message: (parent, { id = '' }, { db }) => {
return db.messages.find((msg) => msg.id === id);
}
},
Mutation: {
// 86 line: POST MESSAGE (단일 메세지 조회) API 기능과 같음
createMessage: (parent, { text, userId }, { db }) => {
const newMsg = {
id: v4(),
text,
userId,
timestamp: Date.now()
};
db.messages.unshift(newMsg);
setMsgs(db.messages);
return newMsg;
},
updateMessage: (parent, { id, text, userId }, { db }) => {
const targetIdx = db.messages.findIndex((msg) => msg.id === id);
if (targetIdx < 0) throw Error('메세지 데이터가 없습니다.');
if (db.messages[targetIdx].userId !== userId)
throw '사용자 정보가 일치하지 않습니다.';
const newMsg = { ...db.messages[targetIdx], text: text };
db.messages.splice(targetIdx, 1, newMsg);
setMsgs(db.messages);
return newMsg;
},
deleteMessage: (parent, { id, userId }, { db }) => {
const targetIdx = db.messages.findIndex((msg) => msg.id === id);
if (targetIdx < 0) throw Error('메세지 데이터가 없습니다.');
if (db.messages[targetIdx].userId !== userId)
throw '사용자 정보가 일치하지 않습니다.';
msgs.splice(targetIdx, 1); // 삭제
setMsgs(db.messages);
return msgs;
}
}
};
export default messageResolver;
# /server/src/resolvers/user.js
const userResolver = {
Query: {
users: (parent, args, { db }) => ({
result_code: 0,
payload: Object.values(db.users)
}),
user: (parent, { id }, { db }) => {
// { db } : /server/src/index.js: 25line에 정의된 key 값
const user = db.users[id];
if (!user) throw Error('Not exist user data');
return { result_code: 0, payload: db.users };
}
}
};
export default userResolver;
설치 패키지 종류
들을 알 수 있었다.schema
, resolver
의 개념과 목적을 알 수 있었다.