Develop/React & React Native

Facebook도 쓰는 GraphQL 정복하기

안다희 2019. 12. 4. 03:08
728x90

graphql-yoga: create-react-app 명령어랑 비슷. GraphQL 프로젝트를 빠르게 시작할 수 있음

https://github.com/prisma-labs/graphql-yoga

 

prisma-labs/graphql-yoga

🧘 Fully-featured GraphQL Server with focus on easy setup, performance & great developer experience - prisma-labs/graphql-yoga

github.com

 

easy installation

 

1. yarn init

2. make github repository 

3. git init / git remote add / git pull origin master

 

graphql-yoga: 쉽게 설치하는데 중점을 둔 완전한 기능을 갖춘 GraphQL 서버

 

4. yarn add graphql-yoga     <--- that's it!

 

 

 

 

<GraphQL이 해결하는 2가지 문제>

- over-fetching: 필요 이상의 정보를 받음.

- under-fetching: 어떤 하나를 완성하기 위해 그 아래 것들을 받는 것? - url 개념이 없어서 개 쿨해

  3개의 api를 요청할 필요가 없는거지.

 

5. yarn global add nodemon

파일 수정할 때마다 서버 재시작

 

index.js 만들고 콘솔 찍어보기.

package.json에 "scripts" 추가

{
  "name": "191204_movieql",
  "version": "1.0.0",
  "description": "Movie API with Graphql",
  "main": "index.js", // nodemon은 얘를 주시할거임.
  "repository": "https://github.com/daheeahn/191204_movieql",
  "author": "daheeahn<deg9810@likelion.org>",
  "license": "MIT",
  "dependencies": {
    "graphql-yoga": "^1.18.3"
  },
  "scripts": {
    "start": "nodemon"
  }
}

6. yarn start 하면 index.js 바뀔 때마다 콘솔 찍힘. 서버 재시작 된다는 것!

7. 이제 서버 만들거야. Quick Start (graphql-yoga 참고)

 

8. yarn add babel-node --dev

babel-node 좀 더 좋은 코드 작성하게 해줌. const graphqlYoga = require 이딴거 필요x. import 같은거 쓰게 해줘

const a = require 쓰기 싫고 import 쓰고 싶어서 깔았던데.

근데 이거 말고 babel-cli를 까는거야.. 흠 나는 이거 까는데 성공했어.

 

9. yarn global add babel-cli 이것도 깔아야하나봐

yarn global add babel-cli --ignore-engines ?? 7번 하더니 갑자기 error나서 이걸로 다시함. 근데 나는 위에거 했더니 에러 안남

 

10. package-json scripts를 

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

이렇게 변경!

 

그리고 yarn start 해봐~

SyntaxError: Unexpected token 나면 잘되고 있다는 뜻.

 

11. 이제 .babelrc 환경을 설정할거야 파일 저 이름으로 하나 만들어

babel을 위한 모든 환경설정을 넣을 수 있어!

{ // in .babelrc
    "presets": ["env", "stage-3"] // for node
}

 

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

언어 명세 다운

그리고 yarn start~

그럼 잘될거임

 

서버라는 새로운 변수 만들고, 새로운 서버 만들어서 환경설정만 넣으면 됨.

서버 시작하는게 넘 쉬운거야

13. schema: 사용자에게 보내거나 받을 data에 대한 설명.

 

위 설정하고 yarn start하면 

no schema defined라고 뜬다.

 

이제 배울거여!

 

14. 아 근데 babel 설치해야해

 

15. 처음 발견한 에러가 No schema defined였지? 

무엇을 받을지, 무엇을 줄 지에 대한 description

 

16. graphql 폴더 만들어서

그 안에 schema.graphql 파일 만들게

 

1) Query는 정보 받을 때만 쓰임

2) Mutation(변형): 정보를 바꿀 때

 

이제 우리가 GraphQL 서버에 할 건, 어떤 Mutations, 어떤 Query 들을 우리가 가졌는지 알려주는거야!

 

17. graphql/schema.graphql

type Query {
    name: String!
}

! 는 required

 

18. graphql/resolvers.js 만들기

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

export default resolvers

19. 그리고 index.js에서

import { GraphQLServer } from 'graphql-yoga'
import resolvers from './graphql/resolvers'  // add this code

const server = new GraphQLServer({
    typeDefs: "graphql/schema.graphql", // add this code
    resolvers // add this code
})
server.start(() => console.log("GraphQL Server Running"))

 

그리고 yarn start하면 잘 돌아가고, 

 

20. Graphql playground

localhost:4000 가면 있음 (graphql-yoga 깃헙 참고)

 

database 잘 테스트 되는지 보는거. Postman과 비슷해.

query {

  name

}

 

타입도 int인데 string으로 하면 다 잡아줌

localhost:4000/graphql 하면 종점은 playground야 근데 이건 GET인데.. 암튼 안됨

그냥 localhost:4000 해야됨

 

이런건 다 POST다. 개발자 도구에서 네트워크 보면 알 수 있다.

 

21. 타입 정의

graphql/schema.graphql

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

type Query {
    person: Person!
}

 

graphql/resolvers.js

const nicolas = {
    name: 'Nicloas',
    age: 18,
    gender: 'female'
}

const resolvers = {
    Query: {
        person: () => nicolas
    }
}

export default resolvers

 

playground

query {
  person {
    name
    gender
  }
}

 

22. 더 복잡한 Query

graphql/schema.graphql

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

type Query {
    people: [Person]!
    person(id: Int!): Person  // ! 안넣었어. 해당하는 person을 못찾을 수도 있거든.
}

근데 여기서 Person! 이렇게 null을 반환하지 못하도록 하면 만약에 없는 id 요청했을 때 null 반환이 아니라, 에러를 뿜겠지!

 

graphql/resolvers.js

import { people } from './db'

const resolvers = {
    Query: {
        people: () => people,
    }
}

export default resolvers

 

graphql/db.js

export const people = [
    {
        id: 1,
        name: 'Dahee Ahn',
        age: 23,
        gender: 'female'
    },
    {
        id: 2,
        name: 'Sungah Oh',
        age: 24,
        gender: 'female'
    },
    {
        id: 3,
        name: 'Eunju Kim',
        age: 25,
        gender: 'female'
    },
    {
        id: 4,
        name: 'Sukyung Choi',
        age: 26,
        gender: 'female'
    },
]

 

23. 유저가 우리에게 준 id는 어떻게 받을까?

GraphQL 서버가 쿼리나 뮤테이션의 정의를 발견하면, 리솔버를 찾고, 해당 함수를 실행할거야

 

people: (_, {}) => people

=

people: () => people

 

오~

 

person: (_, args) => console.log(args) 

해보자.

 

query {
person(id:2) { // id 꼭 써줘야 함.
    name
    id
  }

이렇게!

args를 { id } 로 사용도 가능하곘다!

 

 

근데 graphql 저장할 땐 nodemon 작동안해서 다시 yarn start 해야되는듯?

특별한 종류의 db backend가 필요없어

 

24. movie로 좀 바꿀거야

어ㄷ떠한. 그 어떠한 db도 다 가져다 쓸 수 있어.

 

mutation의 response를 조절할 수 있는건 참 멋진일이야.

request와 함께 response를 받을 수 있어!

 

Boolean 반환할 때는 굳이 값을 물어보지 않아도 돼.

addMovie(name: "name", score: 1) {

    name

}

왜냐? addMovie(~): Movie! 이거든.

 

근데 deleteMovie(id: 0) {

없어도됨.

}

그냥 deleteMovie(id: 0) 만 해도 돼~

 

 

 

graphql/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!
}

 

graphql/resolvers.js

import { getMoives, getById, addMovie, deleteById } from './db'

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

export default resolvers

 

graphql/db.js

export let movies = [
    {
        id: 1,
        name: '어바웃타임',
        score: 100,
    },
    {
        id: 2,
        name: '업',
        score: 200,
    },
    {
        id: 3,
        name: '코코',
        score: 90,
    },
    {
        id: 4,
        name: '하트시그널2',
        score: 1000,
    },
]

export const getMoives = () => movies

export const getById = id => {
    const foundMovie = movies.find(movie => id === movie.id) // FIXME: 이거 타입스크립트로 하면 타입정의는 어디애ㅔ서 이용하는거야:
    return foundMovie
}

export const deleteById = id => {
    const cleanedMovies = movies.filter(movie => movie.id !== id)
    if (movies.length > cleanedMovies.length) {
        movies = cleanedMovies
        return true // 왜 boolean을 리턴해?
    } else {
        return false
    }
}

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

 

 

이 graphql server를 가지고 REST API 를 이용할 수 있어.

사용자는 playground 콘솔과 상호작용 할거야.

 

25. 

https://yts.mx/api

https://yts.mx/api/v2/list_movies.json?limit=50&minimum_rating=9https://yts.mx/api/v2/list_movies.json?limit=50

 

이제 db.js 이거로 싹 다 고칠거여~

 

26. yarn add node-fetch

nodejs에서 fetch할 때 필요해

https://github.com/node-fetch/node-fetch

 

node-fetch/node-fetch

A light-weight module that brings window.fetch to Node.js - node-fetch/node-fetch

github.com

여기 usage 보면 res.json() 해야되는 방법 있지~ 이게 아마 내가 일하면서 배운 그 response.json()이 아닌가 싶다!

 

 

 

뮤테이션은 db상태 변할 때!

원하는 만큼 정의 가능!

schema에게 타입만 정해주면 ㅎㅎ

id는 필요없어. db에의해 자동생성될거니까.

 

 

27. 영화API 정리

graphql/schema.graphql

type Movie {
    id: Int!
    title: String!
    rating: Float!
    summary: String!
    language: String!
    medium_cover_image: String!
}

type Query {
    movies(limit: Int, rating: Float): [Movie]!
}

 

graphql/resolvers.js

import { getMovies } from './db'

const resolvers = {
    Query: {
        movies: (_, { limit, rating }) => getMovies(limit, rating),
    },
}

export default resolvers

 

graphql/db.js

import fetch from 'node-fetch'

const API_URL = "https://yts.mx/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)
}

 

 

-----기타------

 

(_, {}) 에서 _ 주목!

parent, a, root 모든게 될 수 있어. _ 그래서 _ 얘도 되는거여

 

 

 

 

nodemon: js저장할 땐 재시작, graphql 저장할 땐 작동 안함

 

질문을 해결하는 함수를 만든다.

 

mutation: db 상태가 변할 때 사용하는 것,.

 

nodemon 땜에 동일 포트 사용중 에러 뜰 수도 ?