GraphQL - 3

Doodream·2022년 2월 12일
0

GraphQL

목록 보기
4/5
post-thumbnail

쿼리 이름 명시하기

쿼리

query findCompany {
  company(id:"1"){
    id
    description
    name
    users{
      id
      firstName
      age
      company{
        id
        users{
          age
        }
      }
    }
  }
}

위 코드에서 query, findCompany를 빼고 정의해도 상관없다. query는 데이터를 가져오는 get 형태의 요청을 보낸다는 작업의 별칭입니다.

mutation 의 경우 post 형태의 요청을 보낸다는 작업이라고 할 수 있습니다.

그리고 이렇게 findCompany 같이 쿼리의 이름을 붙이는 건 프론트엔드에서 같은 작업을 여러번 보낼수 있기에 쿼리를 중복사용하지 않기 위해서 이름을 붙입니다. 그래프큐엘 테스트 환경에서는 별로 쓸모는 없지만 프론트엔드에서는 자주사용하게 됩니다.

중복되는 쿼리 한번에 이용하기

위 사진을 보게 되면 중복되는 코드이나 id가 다른 쿼리입니다. 이때 에러메세지에서는 필드에 이름을 붙이라고 나옵니다.

데이터의 객체가 받아와 질때 각 객체별로 구분할 수 있도록 가 필요합니다. 하지만 중복되는 쿼리에 별칭이 없다면 같은 데이터가 가면서 어떤데이터가 어떤요청에 의해 오게되었는지 구분할수 없게 됩니다. 따라서 이러한 별칭으로 중복되는 쿼리들을 구분합니다.

fragment

아까의쿼리문에서 중복되는 속성들을 사용한 코드가 있었습니다. 이렇게 쿼리문 자체를 조각으로 빼두어서 코드를 좀더 DRY 하게 관리할 수 있습니다.

fragment [이름] on [접근하는 데이터타입]

Mutation

데이터 추가하기

쿼리는 데이터를 가져오는 요청이였다면 뮤테이션은 데이터를 추가하는 요청입니다.
schema.js

const mutation = new GraphQLObjectType({
  name: "Mutation",
  fields: {
    addUser: {
      type: UserType,
      args: {
        firstName: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: new GraphQLNonNull(GraphQLInt) },
        companyId: { type: GraphQLString },
      },
      resolve(parentValue, args) {
        const { firstName, age } = args;
        const data = axios
          .post("http://localhost:3000/users", { firstName, age })
          .then((res) => res.data);
        return data;
      },
    },
  },
});

module.exports = new GraphQLSchema({ query: RootQuery, mutation });

스키마에서 뮤테이션의 루트는 mutation 입니다. GraphQLObjectType으로서 필드를 가지게 되는데 필드속성의 이름은 동작을 하듯이 이름지어지므로 addUser 같이 UserType에 무언가를 하는 동사적 네이밍이 들어갑니다.

NonNull
그리고 args에는 UserType에 맞는 데이터타입들을 설정해줍니다. GraphQLNonNull이라는 데이터 클래스로 그래프 큐엘 타입들을 감싸줍니다. 이렇게되면 이 값들은 null 값을 가질수 없다는 의미로서 사용자의 인수를 받을때 그래프 큐엘에서 검사를 진행합니다. null값을 받게되면 에러가 나게 됩니다.

resolve()를 살펴봅시다. 쿼리에서의 resolve는 실제로 데이터를 받아오는 부분이였습니다. 하지만 mutation에서의 resolve는 데이터베이스에 데이터를 삽입하게 되는 로직입니다.

따라서 json 서버에 post 형태로 데이터를 보내게 되면 UserType의 데이터테이블에 args로 받아온 데이터들이 삽입되게 되고 id가 자동으로 배정됩니다.

실제로 mutation을 날려봅시다.

위 쿼리를 보면 매개변수로 받은 데이터들을 UserType에 삽입하고 생성된 새로운 레코드들의 데이터 정보를 받아오는 것을 확인 할수 있습니다.

데이터 삭제하기

schema.js

const mutation = new GraphQLObjectType({
  name: "Mutation",
  fields: {
    addUser: {
      type: UserType,
      args: {
        firstName: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: new GraphQLNonNull(GraphQLInt) },
        companyId: { type: GraphQLString },
      },
      resolve(parentValue, args) {
        const { firstName, age } = args;
        const data = axios
          .post("http://localhost:3000/users", { firstName, age })
          .then((res) => res.data);
        return data;
      },
    },
    deleteUser: {
      type: UserType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLString) },
      },
      resolve(parentValue, args) {
        const { id } = args;
        const data = axios
          .delete(`http://localhost:3000/users/${id}`)
          .then((res) => res.data);
        return data;
      },
    },
  },
});

위 스키마 파일에 작성된 deleteUser 필드를 보면 addUser와 굉장히 흡사하다.
resolve에서 args로 id를 받아서 데이터를 찾은다음에 resolve로 해당데이터에 대한 행동을 실행합니다. json 서버에서는 id로 데이터를 구별하기 때문에 데이터 값을 식별하기위해서 id 값을 args로 넣어줍니다.

그과정에서 axios.delete 함수가 실행될떄 url이 들어가는데 이 url 문법은 json 서버에 이러한 url을 보내면 delete 함수가 작동하는 규칙입니다.

쿼리문을 보내고 난 결과를 보면 id: null 이 뜨게 되는데 이는 이미 JSON 서버에서 resolve를 수행한다음 해당 데이터의 id를 보내려고 보니 없기 때문에 null값을 보내는 것입니다. 즉, resolve가 수행에 성공했다는 뜻입니다.

이부분이 조금 이상하게 느껴질수도 있습니다. 차라리 이럴거면 null값을 보내는게 의미가 없으니 애초에 mutation 정의 부터 기대값이 없다고 정의하는게 어떻느냐

아쉽게도 현재로선 그러한 문법은 graphQL에 없습니다. 없는 데이터라고 할지라도 돌려받는 데이터가 있어야 합니다.

데이터 업데이트 하기

put VS patch

먼저 데이터를 업데이트 하는 방식은 두가지 입니다. put은 데이터 전체를 새롭게 대체하는 것입니다. 즉, 기존에 있던 데이터 전체를 버리고 들어오는 데이터들로만 다시 채우는 것입니다.

하지만 patch는 기존의 데이터가 있는 상황에서 들어오는 속성과 같은 속성만 덮어씌우는 것 입니다.

여기에서는 덮어 씌우는 방식으로 데이터를 바꿔보겠습니다.
schema.js

const mutation = new GraphQLObjectType({
  name: "Mutation",
  fields: {
    addUser: {
      type: UserType,
      args: {
        firstName: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: new GraphQLNonNull(GraphQLInt) },
        companyId: { type: GraphQLString },
      },
      resolve(parentValue, args) {
        const { firstName, age } = args;
        const data = axios
          .post("http://localhost:3000/users", { firstName, age })
          .then((res) => res.data);
        return data;
      },
    },
    deleteUser: {
      type: UserType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLString) },
      },
      resolve(parentValue, args) {
        const { id } = args;
        const data = axios
          .delete(`http://localhost:3000/users/${id}`)
          .then((res) => res.data);
        return data;
      },
    },
    editUser: {
      type: UserType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLString) },
        firstName: { type: new GraphQLNonNull(GraphQLString) },
        age: { type: new GraphQLNonNull(GraphQLInt) },
        companyId: { type: GraphQLString },
      },
      resolve(parentValue, args) {
        const { id, firstName, age, companyId } = args;
        const data = axios
          .patch(`http://localhost:3000/users/${id}`,args)
          .then((res) => res.data);
        return data;
      },
    },
  },
});

editUser를 보면 args로 id(수정할 데이터를 찾기 위한 id), 와 바꿀 내용(firstName, age, companyId)들을 인수로 받고 해당 내용을 axios.patch를 이용해서 바꾸게 됩니다.

쿼리를 보면 해당 데이터를 id를 이용해서 찾고 나머지 인수들을 이용해서 바꾼 데이터를 response로 받은 모습입니다.

그리고 보면 이 뮤테이션은 꼭 firstname, age는 바꿔야만하고 companyId는 굳이 넣지 않는다는 규칙을 NonNull을 통해 전달하고 있습니다. 이러한 patch는 보통 일부만 바꾸기 때문에 중복코드를 작성하지 않기 위해서 NonNull을 하지 않습니다. 하지만 혹시 분명하게 꼭 일부 데이터를 변경해야만하는 뮤테이션의 경우 NonNull을 잘 활용하도록 합니다.

그리고 들어오는 모든 args를 이용해 값을 바꾸는데 id는 값을 찾는데에 이용되므로 이 id값이 업데이트 되어 바뀔 염려는 하지 않아도 됩니다.

profile
일상을 기록하는 삶을 사는 개발자 ✒️ #front_end 💻

0개의 댓글