[GraphQL] GraphQL 의 환경세팅 및 기본 구성

BinaryWoo_dev·2023년 4월 23일
0

graphql

목록 보기
1/1

서론


인프런 강의를 시청하면서 graphQL 에 대해 배운 내용들을 기록하고자 한다.

본론


GraphQL의 기본 개념

Scalar types

  • Int: A signed 32‐bit integer.
  • Float: A signed double-precision floating-point value.
  • String: A UTF‐8 character sequence.
  • Booleantrue 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.

! 기호

  • ! 기호는 반드시 할당되어야하는 타입이라는 의미이다.

GraphQL 환경세팅 및 schema 정의

패키지 설치

  • 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/... 경로에 스키마를 정의해준다.

  • graphQL 에서는 숫자 타입을 정의할 경우 Int, Float 두 가지 키워드를 사용한다.
  • 기본 형식 (root)
    # 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]
  • 스키마 1 예시
    # 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;
    
  • 스키마 2 예시
    # 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;
    

resolver 정의

기존 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')
    		}
    	}
    });
  • 루트 resolver 예시
    # /server/src/resolvers/index.js
    
    import messageResolver from './message.js';
    import userResolver from './users.js';
    
    export default [messageResolver, userResolver];
  • reslover 1 예시
    # /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;
  • reslover 2 예시
    # /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;

결론


  • graphQL 을 위해 필요한 설치 패키지 종류들을 알 수 있었다.
  • schema, resolver의 개념과 목적을 알 수 있었다.
profile
매일 0.1%씩 성장하는 Junior Web Front-end Developer 💻🔥

0개의 댓글