Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

Joon's Space

[GraphQL] GraphQL 개념 (Query, Resolver, Schema, Mutation) 본문

Web/JS

[GraphQL] GraphQL 개념 (Query, Resolver, Schema, Mutation)

Happy Joon 2021. 8. 29. 13:35

프로젝트 생성

 

yarn init

yarn init

git init

git init
git remote add origin https://github.com/jyojun/movieql
git commit -m 'setup'
git branch -M main
git push -u origin main

 

 

graphql server setup

 

graphql-yoga 패키지 설치

yarn add graphql-yoga

※graphql-yoga : graphql 서버를 쉽게 셋팅할 수 있게 만들어진 패키지

 

 

graphql을 사용하면서 해결되는 문제들

 

-> graphql을 처음 사용하면서 왜 graphql 을 사용하는지에 대해 물어본다면 알아야 할 다음 두가지 개념은 'over-fetching', 'under-fetching' 이다. 

 

over-fetching 

API를 호출할 때, 필요한 정보 이상을 가져오는 것을 뜻한다. 예를 들면, 내가 필요한 정보는 사용자명인데, 사용자 명의 리스트를 받기위해서 GET요청을 하게되면 users에 딸려오는 불필요한 프로필사진, 이메일, 등등 전달 받게 되어, 불필요한 정보들로 인해 서버와 네트워크가 추가적으로 비효율적으로 더 사용되게 된다. 

 

under-fetching 

하나의 요청으로 충분한 데이터를 받지 못하는 것을 의미한다. 그로 인해 두개 이상의 endpoint에 요청을 보내야한다. 단일 API 요청에 비해서 앱 사용시 더 느린 속도로 불편함을 겪게 된다.

 

-> 위 두가지 개념 모두 graphql을 사용하므로써 해결될 수 있는 문제들이다. 

 

query로 정보 요청을 보내는 방식 (예제)

query {
	feed {
    	comments
        likeNumber
    }
    notifications {
    	isRead
    }
    user {
    	username
        profilePic
    }
}

다음과 같이 query를 보내면 내가 필요한 정보들만이 javascript object로 반환되어 전달받는다. 

 

 

nodemon 설치

yarn global add nodemon

-> nodemon 은 내가 파일을 수정, 생성할 때 마다 서버를 재시작해준다. 

 

nodemon을 사용하기 위해서 실행할 때 nodemon을 실행시키기 위해서, package.json script 에 start:nodemon 추가 해 준다.

 

패키지 설치

yarn add babel-cli babel-preset-env babel-preset-stage-3 --dev

 

babel 환경 설정을 위해서 .babelrc 파일 생성

// .babelrc

{
    "presets": ["env", "stage-3"]
}

 

{
  "scripts": {
    "start": "nodemon --exec babel-node index.js"
  }
}

 

graphql server 시작하기

// index.js

import { GraphQLServer } from "graphql-yoga";

const server = new GraphQLServer({
    
});

server.start(() => console.log("Graphql Server Running"));

index.js 파일에 다음과 같이 작성해주면 다음과 같은 error가 나오게 된다.

 

schema가 정의되지 않음

 

schema 정의

-> 사용자가 무엇을 할지에 대한 설명을 정의한다. 

 

Query : 데이터베이스에서 요청하여 받을 정보들 

Mutation : 서버나 데이터베이스에서 정보를 변경하는 작업 

 

 

# schema.graphql

type Query{
    name: String!
}

다음과 같이 schema를 작성해 주어도, schema는 단지 주고 받는 데이터에 대한 설명이기 때문에, node.js 에서 무언가 작업을 수행시켜줘야한다.

 

이때, query를 내 데이터베이스에서 문제와 같은데, 이걸 resolve(해결) 해줘야 한다. 이 때 필요한 것이 resolver 이다.

(resolvers는 database에 접근할 수 있고, 심지어 다른 database, memory, api 에 접근 할 수도있다.)

 

resolvers.js 는 다음과 같이 작성 해 준다.

 

// resolvers.js

const resolvers = {
    Query: {
        name:() => "hyojun"
    }
};

export default resolvers;

 

그리고 export 한 resolvers를 index.js 에 import 해준다.

 

// index.js

import { GraphQLServer } from "graphql-yoga";
import resolvers from "./graphql/resolvers";

const server = new GraphQLServer({
    typeDefs: "graphql/schema.graphql",
    resolvers  
});

server.start(() => console.log("Graphql Server Running"));

graphql 서버가 성공적으로 돌아가는 것을 볼 수 있다.

 

localhost:4000 에 접속해서, name을 요청하는 query를 작성하면 resolvers.js 에서 작성한대로 "hyojun"을 전달 받을 수 있다.

 

 

error 1 : 이때, resolvers.js 에서 schema 에서 정의되어있지 않는 것을 작성하면, 오류가 난다.

error 2 : 또한 name 의 타입을 schema에서 int로 정의해주고, resolvers.js 에선 query의 name이 String으로 리턴되기 때문에 오류가 발생하게 된다.

 

error 1
error 2

 

Schema 에서 새로운 type 생성

 

// schema.graphql

type hyojun {
    name: String!
    age: Int!
    gender: String!
}

type Query{
    person: hyojun!
}

다음과 같이 String, Int 외에 Object 타입을 선언해서 새로운 type을 생성할 수 있다.

 

그에 따라 resolvers.js 도 다음과 같이 수정 해 준다.

 

// resolvers.js

const hyojun = {
    name: "Hyojun",
    age: 24,
    gender: "male"
}

const resolvers = {
    Query: {
        person: () => hyojun
    }
};

export default resolvers;

 

playground 에서 확인하면 queries 들의 타입들을 확인 할 수 있다.

 

localhost:4000 에서 schema에서 정의한 query 들을 확인 할 수 있다.

 

데이터 베이스에서 내가 원하는 데이터인 person의 age만 원하면 query문을 다음과 같이 보낸다.

 

data가 person의 age만 받아온 것을 확인 할 수 있다.

이외 name과 gender 정보를 얻기를 원한다면 person 괄호속에 name, gender를 요청하면 된다. (optional)

 

 

더 복잡한 Query 생성

- 여러명의 사람 데이터일 경우

 

schema를 바꾸어주는데, person -> people 로 변경

people 에서 특정 인물에 접근하기 위해서,  id가 필요하므로, Person type에 id 항목을 추가.

type Person {
    id: Int!
    name: String!
    age: Int!
    gender: String!
}

type Query{
    people: [Person]!
    person(id: Int!): Person! 
}

 

resolver에 사람들을 추가하는 것 보다, db 파일을 따로 생성해서 사람들 정보를 생성해준다. 

그리고 getById 함수를 만들어, people 배열에서 해당 id에 해당하는 사람을 반환하게 한다. 

 

// db.js

export const people = [
    {
        id: "1",
        name: "Hyojun",
        age: 24,
        gender: "male"
    },
    {
        id: "2",
        name: "Minsu",
        age: 23,
        gender: "male"
    },
    {   id: "3",
        name: "Sujin",
        age: 21,
        gender: "female"
    },
    {   id: "4",
        name: "Cheolsu",
        age: 20,
        gender: "male"
    },
    {   id: "5",
        name: "Yuri",
        age: 19,
        gender: "female"
    }
]

export const getById = id => {
    const filteredPeople = people.filter(person => person.id === String(id));
    return filteredPeople[0];
}

 

playground에서 확인하면 people은 잘 나오는 것을 확인 할 수 있다.

 

여기서 특정인물 person을 getById로 접근하기 위해서 어떻게 해야하나?

 

resolver.js 파일에서, person의 arguments 인 id를 parameter로 받아서, 특정 person을 리턴하는 getById를 리턴 해 준다.

 

import { people, getById } from "./db";

const resolvers = {
    Query: {
        people: () => people,
        person: (_, { id }) => getById(id)
    }
};

export default resolvers;

 

특정인물의 id를 parameter로 받아 이름 정보를 받았다.

 

Mutation

mutation은 Database 상태가 변할 때 사용되는 것. Mutation 을 사용하려면(add, update, delete) schema에 Mutation 을 따로 정의해줘서 사용해야함!

 

People -> Movie api를 만들 것 이기 때문에, schema, resolver, db도 전부 movie 에 맞게 바꾸어 준다.

 

먼저 deleteMovie, addMovie 함수를 db.js에 생성해준다.

 

// db.js

export let movies = [
    {
        id: 0,
        name: "Star Wars - The new one",
        score: 1
    },
    {
        id: 1,
        name: "Avengers - The new one",
        score: 8
    },
    {
        id: 2,
        name: "The Godfather I",
        score: 99
    },
    {
        id: 3,
        name: "Logan",
        score: 2
    }
]
export const getMovies = () => movies;

export const getById = id => {
    const filteredMovie = movies.filter(movie => movie.id === id);
    return filteredMovie[0];
}

export const deleteMovie = id => {
    const cleanedMovies = movies.filter(movie => movie.id !== id);
    if(movies.length > cleanedMovies.length){
        movies = cleanedMovies;
        return true;
    } else {
        return false;
    }
}

export const addMovie = (name, score) => {
    const newMovie = {
        id: `${movies.length + 1}`,
        name: name,
        score: score
    }
    movies.push(newMovie);
    return newMovie
}

 

resolver.js 에 Mutation을 추가하고, mutation에 사용할 함수들을 부른다. schema도 Movie 에 맞게 변경

 

// resolvers.js

import { getMovies, getById, addMovie, deleteMovie } from "./db";

const resolvers = {
    Query: {
        movies: () => getMovies(),
        movie: (_, { id }) => getById(id)
    },
    Mutation: {
        addMovie: (_, {name, score}) => addMovie(name, score),
        deleteMovie: (_, {id}) => deleteMovie(id)
    }
};

export default resolvers;
// schema.graphql

type Movie {
    id: Int!
    name: String!
    score: Int!
}

type Query{
    movies: [Movie]!
    movie(id: Int!): Movie
}

type Mutation {
    addMovie(name: String!, score: Int!): Movie!
    deleteMovie(id: Int!): Boolean!
}

addMovie 했을 때 Movie가 반환
deleteMovie(id:0) (0번 영화를 삭제)
영화 리스트를 출력했을 때, 0번 영화가 삭제된 것, 5번 "hello"가 추가된 것을 확인.

 

Open API 로 Movie list 받아오기

 

패키지 설치 (node.js 에서 fetch 할 때 필요함. )

yarn add node-fetch

 

fetch API 를 사용하기 위해 사용방법은 다음 포스트에서 참고하였다.

 

https://velog.io/@delilah/Node.js-fetch-API

 

[Node.js] fetch API / SPRINT-비동기💯️

goalfetch를 이용해 웹에서 정보 가져오기웹 개발을 할 때, ajax통신을 자주 사용하게 된다. ajax 를 사용할 때, XHR, JQuery, Fetch 를 쓸 수 있는데, 그 중 가장 베스트인 fetch에 대해서 알아본다.비동기 요

velog.io

 

import fetch from "node-fetch";

const API_URL = "https://yts.am/api/v2/list_movies.json"

export const getMovies = (limit, rating) => {
    let REQUEST_URL = API_URL;
    if (limit > 0) {
        REQUEST_URL += `limit=${limit}`;
    }
    if (rating > 0) {
        REQUEST_URL += `&minimum_rating=${rating}`;
    }
    return fetch(REQUEST_URL)
        .then(res => res.json())
        .then(json => json.data.movies);
}

 

fetch API는 JavaScript에서 서버로 네트워크 요청을 보내고 응답을 받을 수 있도록 해주는 매서드이고, 

promise 형식으로 반환되기 때문에, 이 response를 json 메소드를 이용하여 변환 해야 우리가 원하는 data 값이 나온다. 

 

limit, rating 최소 설정을 통해서 다음과 같이 data가 나온 것을 확인할 수 있다.

반응형

'Web > JS' 카테고리의 다른 글

[JavaScript] 생성자 함수  (0) 2022.07.15
[JavaScript] 변수, 호이스팅, TDZ  (0) 2022.07.15