GraphQL Query 심화

기운찬곰·2020년 9월 9일
5

GraphQL

목록 보기
3/6
post-thumbnail

시작하기에 앞서서...

지난시간에 기초적인 쿼리 사용법에 이어서 이번시간에는 조금 더 심화적인 내용으로 들어가보겠습니다.


🚀 프래그먼트(Fragement)

아래와 같은 쿼리가 있다고 가정해보겠습니다. 음... 보시면 알겠지만 Lift와 Trail에 accessedByLifts는 중복되는 필드가 많아보입니다. 왜냐하면 둘다 Lift 타입이기 때문이죠.

query {
  Lift(id: "jazz-cat") {
    name
    status
    capacity
    night
    elevationGain
    trailAccess {
      name
      difficulty
    }
  }
  Trail(id: "river-run") {
    name
    difficulty
    accessedByLifts {
      name
      status
      capacity
      night
      elevationGain
    }
  }
}

프래그먼트는 이럴때 사용하기 좋습니다. 아래는 프래그먼트를 이용했을때 쿼리입니다.

query {
  Lift(id: "jazz-cat") {
    ...liftInto
    trailAccess {
      name
      difficulty
    }
  }
  Trail(id: "river-run") {
    name
    difficulty
    accessedByLifts {
      ...liftInto
    }
  }
}

fragment liftInto on Lift {
  name
  status
  capacity
  night
  elevationGain
}

fragment 를 정의하는 방법은 fragment 식별자를 쓴 다음 이름을 써줍니다. on Lift는 어떤 타입에 대한 fragment인지를 나타내고 이 정보는 꼭 써줘야 합니다.

이렇게 정의가 끝나면 사용방법은 자바스크립트 spread 연산자와 비슷하게 ...을 사용합니다. 기능또한 비슷합니다.

추가적으로, fragement는 원하는 만큼 정의가 가능하며 같은 타입에 대한 fragment를 여러개 쓸 수도 있고, fragment 정의내에서 ...으로 다른 fragment를 불러올 수도 있습니다.


인라인 프래그먼트(Inline-Fragment)

예제 실행 : https://github.com/gratiaa/union_types_example

객체 리스트를 반환하는 방법은 이미 살펴봤습니다. 하지만 이들은 모두 한가지 타입의 리스트로만 반환되었습니다. 타입 여러개를 한번에 리스트에 받아 반환하도록 할 수는 없을까요?

이때는 유니언 타입을 만들면 됩니다.

  type StudyGroup {
      name: String!
      subject: String!
      students: Int!
  }

  type Workout {
      name: String!
      reps: Int!
  }

  union AgendaItem = Workout | StudyGroup

  type Query {
      agenda: [AgendaItem!]!
  }

{
  agenda {
    ...on Workout {
      name
      reps
    }
    ...on StudyGroup {
      name
      subject
      students
    }
  }
}

이렇게 사용하면 쿼리를 작성할때 프래그먼트를 사용해서 AgendaItem이 Workout일때와 StudyGroup일때 특정 필드만 선택되도록 만들 수 있습니다.

즉, 유니온 타입에서 각각의 객체가 어떤 필드를 반환할 것인지 정할 때 인라인 프래그먼트를 사용하면 됩니다.

결과를 보면 알겠지만 하나는 {name, subject, students} 이고, 또 다른 하나는 {name, reps}로 타입이 다른것을 알 수 있습니다.


인터페이스(Interface)

  interface ScheduleItem {
    name: String!
    start: Int
    end: Int
  }
  type StudyGroup implements ScheduleItem {
      name: String!
      start: Int
      end: Int
      subject: String!
      students: Int!
  }
  type Workout implements ScheduleItem {
      name: String!
      start: Int
      end: Int
      reps: Int!
  }
  type Query {
      agenda: [ScheduleItem!]!
  }

인터페이스란 자바에서 말하는 그 의미와 동일합니다. 추상화를 의미하며, 이걸 상속받은 타입은 그 필드는 반드시 구현해야한다는 것을 의미합니다.

{
  agenda {
    name
    start
    end
    ...on Workout {
      reps
    }
  }
}

이제 agenda는 name, start, end 뿐만 아니라 Workout에만 있는 reps같이 인라인 프래그먼트를 이용해서 선택해서 쓸 수 있습니다. (유니온과 비슷하게 쓰일 수 있다는 것을 알 수 있습니다)


🪁 뮤테이션(Mutation)

쿼리는 데이터를 읽기 위한 행위에 관한 기술이라면 데이터를 쓰기 위한 행위는 뮤테이션이 있습니다.

일반적으로 세 가지 종류의 뮤테이션이 있습니다.

  • 새로운 데이터 생성
  • 기존 데이터 업데이트
  • 기존 데이터 삭제

뮤테이션은 쿼리문과 동일한 문법 구조를 가지지만, 반드시 mutation 키워드와 함께 시작해야 한다는 점이 다릅니다.

mutation {
  createPerson(name: "Bob", age: 36) {
    id
    name
    age
  }
}

name은 Bob이고 age는 36인 데이터를 서버로 보내면 새 사람 정보가 데이터베이스에 추가될 것을 예상할 수 있습니다. 그리고 나서 생성이 완료되면 id, name, age를 다시 반환하도록 괄호안에 작성했습니다. 무언가 잘못되면 에러 정보가 반환될 것입니다.


다음 쿼리는 snowtooth 사이트에서 직접 해볼 수 있는 예제입니다.

mutation closeLift {
  setLiftStatus(id: "jazz-cat", status: CLOSED) {
    name
    status
  }
}

이렇게 쓸 수 있는 건 다 스키마에 작성되어있기 때문인거 아시죠??


쿼리변수 사용하기(Variable)

mutation closeLift($id:ID! $status:LiftStatus!) {
  setLiftStatus(id: $id, status: $status) {
    id
    name
    status
  }
}

위 코드를 보면 뮤테이션이 매개변수를 받을 수 있고, 이 변수를 setLiftStatus를 실행하는데 넘겨준것을 볼 수 있습니다.

정리해보면 변수를 사용하기 위해서는 다음 세 가지 작업을 해야 합니다.

  1. 쿼리(혹은 뮤테이션)안의 정적 값을 $variableName 으로 변경합니다.
  2. $variableName 을 쿼리(혹은 뮤테이션)에서 받는 변수로 선언합니다.
  3. 별도의 전송규약(일반적으로는 JSON) 변수에 variableName: value 을 전달하세요.

GraphQL 플레이그라운드 좌측하단을 보면 쿼리변수를 설정하는 부분이 있습니다.


🌹 구독(Subscription)

서버와의 실시간 통신을 통한 주요 이벤트에 대한 즉각적인 알림은 오늘날의 많은 어플리케이션에서 요구되는 중요한 기능입니다. 이런 경우를 위하여 GraphQL은 구독(Subscription)이라는 개념을 제공합니다.

클라이언트가 어떤 이벤트를 구독하면, 클라이언트는 서버와 지속적인 연결을 형성하고 유지하게 됩니다. 특정 이벤트가 발생하면, 서버는 대응하는 데이터를 클라이언트에 푸시해줍니다. 전형적인 "요청-응답 순환"을 따르는 쿼리문 또는 뮤테이션과 달리, 구독은 클라이언트를 향한 데이터 흐름을 나타냅니다.

구독은 쿼리문과 뮤테이션과 동일한 문법을 사용하여 작성됩니다. 스키마를 보면 쿼리, 뮤테이션과 같이 Subscription도 마찬가지로 정의되어있습니다. (Query, Mutation, Subscription을 루트타입이라고 보면 됩니다. 이 3대장은 그만큼 중요하다는 의미가 되겠습니다.)

아래 코드를 실행해보면 작업이 끝나지 않는 것을 볼 수 있습니다.

subscription {
  liftStatusChange {
    name
    capacity
    status
  }
}

subscription이 제대로 실행되는지 보려면 새창을 열어서 데이터를 바꿔줘야 됩니다. 이렇게 실행되면 데이터 변경이 발생된 리프트의 name, capacity, status가 반환되는 것을 볼 수 있습니다.

mutation closeLift {
  setLiftStatus(id: "astra-express", status: HOLD) {
    id 
    name
    status
  }
}

다른 뮤테이션을 연달아 실행하면 이런식으로 변경내역이 시간에 따른 순서로 출력됩니다.


인트로스펙션(introspection)

introspection을 사용하면 API 스키마를 통해 어떤 데이터를 반환받을 수 있는지 조사할 수 있습니다

query {
  __schema {
    types {
      name
      description
    }
  }
}

보면 루트타입, 커스텀타입, 스칼라 타입까지 볼 수 있습니다.


query liftDetails {
  __type(name: "Lift") {
    name
    fields {
      name
      description
      type {
        name
      }
    }
  }
}

특정 타입인 Lift에 대한 필드 정보를 보려면 위에 처럼 해주면 됩니다.


마침

이렇게 쿼리에 대한 기본적인 사용을 마치고 다음시간부터는 스키마를 정의하는 방법에 대해 살펴보도록 하겠습니다. 쿼리 언어를 사용하기 위해 반드시 필요한 작업이기 때문이죠.

참고자료 및 사이트

Learning GraphQL by Eve Porcello and Alex Banks, published by O'Reilly. Copyright 2018 Moon Highway, LLC, 978-1-492-03071-3
한국어번역 공식 소개 사이트 : https://graphql-kr.github.io/learn/
튜토리오를 통한 학습 사이트 : https://www.howtographql.com/
cadenzah님 velog(위 사이트 번역) : https://velog.io/@cadenzah/graphql-01-introduction

profile
배움을 좋아합니다. 새로운 것을 좋아합니다.

0개의 댓글