그래프큐엘 따라잡기 2탄 - 간단한 query와 mutation

둘둘·2020년 9월 17일
4

Apollo_Gql

목록 보기
3/3
post-thumbnail

업데이트가 너무 늦어졌다. 하하..
하지만 앞으로 업데이트할 포스팅들이 산더미라는거.. 🏃‍♀️🏃‍♀️🏃‍♀️

그래프큐엘로 백단 만들어보기 실습!

  1. 폴더 하나 생성
  2. package.json 생성 npm init -y (y를 하면 모두 yes를 하는 것)
  3. npm install --save-dev graphpack 설치하기
    (요기 나오는 그래프팩을 깔면 준비물이 따로 필요없다고 한다!)
  4. package.json에 아래의 script 추가!
"scripts": {
    "dev": "graphpack",
    "build": "graphpack build"
}
  1. src 폴더 하나 파고, schema.graphql, resolvers.js, db.js 파일 만들기
    (여기서 주의할 점! resolvers에 s 안 붙이면 에러뜸! 내가 그랬음!)
  2. schema.graphql 파일에 아래 코드 복붙
type Query {
  hello: String
}
  1. resolvers.js 파일에 아래 코드 복붙
import { users } from "./db";

const resolvers = {
  Query: {
    hello: () => "Hello World!"
  }
};

export default resolvers;
  1. db.js 파일에 아래 코드 복붙
export let users = [
  { id: 1, name: "Graph QL", email: "gql@gmail.com" },
  { id: 2, name: "Doori Kim", email: "doori.alice.kim@gmail.com" },
];
  1. 터미널에 npm run dev 명령어 치면 인제 4000 포트에서 서버를 켤 수 있다!

Schema란 무엇인가?

gql은 스키마 작성에 사용되는 언어 타입을 가지고 있는데, 이걸 human-readable schema syntax 다시말해 SDL(Schema Definition Language)이라고 한다. 혹시 machine-readable도 있나 찾아봤는데 있다..! 와우
어찌됐든 그래프큐엘은 SDL이라서 내가 어떤 언어나 프레임워크를 쓰든간에 상관이 없다고 한다! 다른 스키마 언어랑은 다르게 보이는 대로 이해하면 된다고 한다..!

Types란 무엇인고

Types는 gql에서 가장 중요한 특징 중에 하나이다. 타입들은 API가 어떻게 생겼는지 보여주는 custom object이다. 예를 들어.. 블로그앱을 만든다고 생각하면 포스트, 유저, 좋아요 등의 API가 있을거다.

이러한 타입들은 field를 가지고 있고, 특정한 데이터 타입을 리턴한다.
예를 들어 유저 타입을 만든다고 했을때, 우리는 이름, 이메일, 나이 등의 field를 만든다.
아까 위에서 만든 schema.graphql 파일로 가서 아래의 코드로 대체해주자!

type User {
  id: ID!
  name: String!
  email: String!
  age: Int
}

위에서 느낌표는 field가 null이 될 수 없음을 의미한다. 뒤에다 느낌표를 붙인 애들은 쿼리에서 특정 데이터를 꼭 리턴해야 한다. 위의 타입을 살펴봤을때 age만 null을 리턴할 수 있다.

타입은 아래와 같이 정의할 수 있다
(일반적으로 Object. Query, Mutation, Scalar 타입을 많이 쓴다)
👉Scalar - 단일 값을 저장합니다.
👉Object - 데이터의 객체 구조를 보여준다
👉Query - 특정 타입들의 entry point 역할
👉Mutation - 데이터를 조작하는 entry point 역할
👉Enum - 열거형 타입. 목록 중에 선택해야 하는 상황에서 사용
👉List - []를 사용해서 정의할 수 있다
👉Non-Nullable - 정의된 타입 뒤에 ! 를 붙여서 표현한다. null이 될 수 없으므로 무조건 값을 리턴해줘야 한다.

Scalar types

  • Int : 부호가 있는 32비트 정수
  • Float : 부호가 있는 부동소수점 값
  • String: UTF-8 문자열
  • Boolean : true or false
  • ID : 인덱스넘버처럼 고유한 값의 지표로 씁니다.

ID를 정의하는 것은 개발자가 읽도록 하는 의도가 아니라 객체를 다시 요청하거나 캐시의 키로써 쓸 때 필요하기 때문!

Object type

  • 위에서 정의한 User 또한 Object 타입.
  • id, name, email, age : User type에 정의된 field입니다. field의 우측에는 scalar type을 써준다.
  • String! : null이 될 수 없는 문자열을 의미한다. 즉, 무조건 값을 리턴해줘야 한다.

GQL에서 3가지 중요 뽀인트

가 있는데 그건 바로 query, mutation, subscribe 이다.
간단하게 살펴보자면...

  • query : 서버로부터 데이터를 받는 방법
  • mutation : 서버에 올라간 데이터를 업데이트 하는 것(Create, Update, Delete)
  • subscribe : 서버와 실시간으로 연결을 유지하는 방법

Query

쿼리는 쉽게 말하자면 데이터를 받는 방식을 말한다. gql의 매력뽀인트는 바로 데이터를 내가 원하는 방식 그대로 받을 수 있다는 것! REST API와 다르게 over-fetching이나 under-fetching의 염려가 없다.

아까 위에서 만든 src폴더 아래에 있는 schema.graphql에 쿼리라는 타입을 추가해주자.

type Query {
  users: [User!]!
}

users 쿼리는 Users 배열을 리턴할텐데, null을 리턴하면 안되서 !을 붙여주었다.
근데 Users 배열 안에서 특정 유저를 불러와야 하기 때문에 아래에 요거 한줄을 덧붙여준다.
user(id: ID!): User!

평소에 메시지 보낼때 느낌표 많이 쓰는 1인으로서 이거 참 이상하다.. 굉장히 강조하는 넉낌
널 리턴하면 안돼!!!!!!!!!! 요런 느낌으로 보인다 ㅋㅋㅋ

여기까지 써도 gql이 어떻게 동작할진 여전히 예상이 가지 않는다.
그래서 resolvers.js가 존재하는 것..!

아까 헬로 월드 썼던걸 지워주고 아래의 코드 복붙 고고

import { users } from "./db";

const resolvers = {
  Query: {
    user: (parent, { id }, context, info) => {
      return users.find(user => user.id === id);
    },
    users: (parent, args, context, info) => {
      return users;
    }
  }
};

export default resolvers;

위의 코드를 보면 각각의 쿼리 resolver는 4개의 argument를 갖고 있다.
id를 넘겨주고 특정 유저를 리턴하게 된다. 아주 간단쓰!

아까 서버는 켜놨고.. 쿼리가 잘 들어오나 확인해보자!
로컬 4000에 아래처럼 쿼리를 날려주자!

query {
  users {
    id
    name
    email
    age
  }
}

짠! 요로코롬 잘 호출되고 있따!
특정 유저를 리턴하고 싶다면 id값을 주면 된다.

Mutation

gql에서 mutation은 내가 서버에 변경된 데이터를 보내거나 업데이트된 데이터를 받는 방식이다. REST에서의 CUD(Create, Update, Delete)를 생각하면 편하다
이제 실습 타임!! schema.graphql파일로 고고

type Mutation {
  createUser(id: ID!, name: String!, email: String!, age: Int): User!
  updateUser(id: ID!, name: String, email: String, age: Int): User!
  deleteUser(id: ID!): User!
}

백앤드를 잘 몰라서 모르겠는데, 신기하게 함수식에 타입을 다 정의한다
그래프큐엘만의 특징인건가! 모르겠다 ㅋㅋ

  • createUser: 새로운 유저 등록하는 뮤테이션
  • updateUser: 기존 유저 업뎃하는 뮤테이션. 삭제될 수도 있는 정보도 있어서 느낌표를 뺀건가?!
  • deleteUser: ID를 넘겨받고 해당 유저 삭제하는 뮤테이션

이제 resolver.js로 돌아가서 Query 객체 아래에 Mutation 객체를 만들어준다

Mutation: {
    createUser: (parent, { id, name, email, age }, context, info) => {
      const newUser = { id, name, email, age };

      users.push(newUser);

      return newUser;
    },
    updateUser: (parent, { id, name, email, age }, context, info) => {
      let newUser = users.find(user => user.id === id);

      newUser.name = name;
      newUser.email = email;
      newUser.age = age;

      return newUser;
    },
    deleteUser: (parent, { id }, context, info) => {
      const userIndex = users.findIndex(user => user.id === id);

      if (userIndex === -1) throw new Error("User not found.");

      const deletedUsers = users.splice(userIndex, 1);

      return deletedUsers[0];
    }
  }

이제 4000포트로 가서 뮤테이션을 날려보자!

mutation {
  createUser(id: 3, name: "삼돌이", email: "samdol@gmail.com", age: 33) {
    id
    name
    email
    age
  }
}

사실 이 글을 작성하기 전까진 왜 query날릴때 객체 형식인지 몰랐는데
resolvers에 정의된 Query와 Mutation이 모두 객체 형태라서 이렇게 날리는 것이였다 크..

유저 정보가 제대로 들어갔는지 확인해보자.

잘 들어왔다, 최고다!
하지만 subscribe을 해주지 않아서 로컬에 있는 db.js 파일은 동일할 것이다.

일단 이 정도까지만 알아도 백앤드와 통신할 때 무리가 없을 것 같은데
프로젝트 하면서 필요하면 3탄도 나올 예정!

느낀점

GraphQL에서 진짜 이 정도는 해줘야 내가 필요할 때 mock data를 서버로부터 받아올 수 있어서 좋다.
왜냐... Apollo 공식문서에 있는 codesandbox 링크는 CORS ERROR가 너무 빈번히 일어난다. 도무지 이용할 수가 없다...

급한대로 필요한 양식에 맞춰서 내가 데이터 만들어놓으면
나중에 그거 토대로 불러오면 되는거니까 을매나 좋게요! 😝

profile
Dooreplay! 안 되면 될 때까지,

0개의 댓글