Joon's Space
[GraphQL] GraphQL 개념 (Query, Resolver, Schema, Mutation) 본문
프로젝트 생성
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 정의
-> 사용자가 무엇을 할지에 대한 설명을 정의한다.
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"));
localhost:4000 에 접속해서, name을 요청하는 query를 작성하면 resolvers.js 에서 작성한대로 "hyojun"을 전달 받을 수 있다.
error 1 : 이때, resolvers.js 에서 schema 에서 정의되어있지 않는 것을 작성하면, 오류가 난다.
error 2 : 또한 name 의 타입을 schema에서 int로 정의해주고, resolvers.js 에선 query의 name이 String으로 리턴되기 때문에 오류가 발생하게 된다.
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 들의 타입들을 확인 할 수 있다.
데이터 베이스에서 내가 원하는 데이터인 person의 age만 원하면 query문을 다음과 같이 보낸다.
이외 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];
}
여기서 특정인물 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;
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!
}
Open API 로 Movie list 받아오기
패키지 설치 (node.js 에서 fetch 할 때 필요함. )
yarn add node-fetch
fetch API 를 사용하기 위해 사용방법은 다음 포스트에서 참고하였다.
https://velog.io/@delilah/Node.js-fetch-API
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 값이 나온다.
'Web > JS' 카테고리의 다른 글
[JavaScript] 생성자 함수 (0) | 2022.07.15 |
---|---|
[JavaScript] 변수, 호이스팅, TDZ (0) | 2022.07.15 |