S4 Unit 6. GraphQL

나현·2022년 12월 1일
0

학습일지

목록 보기
45/53
post-thumbnail

💡 이번에 배운 내용

  • Section4. 사람과 기계가 모두 쉽고 빠르게 접근 가능한 Web App을 만들 수 있다.
  • Unit6. Graph QL: REST API와 GraphQL의 차이 및 GraphQL을 다루는 법에 대해 학습한다.

느낀점

직접 실습해보니 생각보다 재밌었던 유닛이었다. 길이는 짧았지만 이해가 좀 안되어서 여러 번 봤던 것 같다. 실습은 난이도가 좀 있었는데 비동기 부분을 react로 사용하려니 익숙해질 필요가 있었고, git 엑세스 토큰 때문에 우왕좌왕하고, react 환경변수에 대해 잘 몰라서 해매기도 했다.
그래도 삽질하니 확실히 기억에도 남고 큰 도움이 되었고 결국 해결하니 뿌듯했다. 페어도 좋은 분을 만나서 서로 정보 공유도 하고 유익한 시간이었다. 😁


키워드

GraphQL, query 쿼리, mutation, subscription, 필드, 전달인자, 별명 aliases, 오퍼레이션 네임 operation name, 스키마 schema, 타입 type, 리졸버 resolver, explorer


학습내용

Ch1. Graph QL

GraphQL

GraphQL은 Graph + Query Language의 줄임말로 Query Language 중에서도 Server API 를 통해 정보를 주고받기 위해 사용하는 Query Language를 의미한다.Facebook에서 처음으로 개발했고, 오픈 소스로 제공된다.

Graph를 사용하는 이유

GraphQL은 모든 데이터가 그래프 형태로 연결되어 있다고 가정한다. 때문에 그래프의 일종인 트리 구조의 JSON 데이터를 클라이언트 요청에 따라 유연하게 응답으로 전송할 수 있다.
REST API 방식의 고정된 자원이 아닌 클라이언트 요청에 따라 유연하게 자원을 가져올 수 있다는 점에서 큰 장점을 가진다.

GraphQL 쿼리

GraphQL은 다음처럼 직관적으로 쿼리를 보내면 그에 맞게 응답이 돌아온다.

  • 쿼리
query {
	(ISBN "xxxxxxxxxxxx") {
		책 이름
		저자 {
			이름
		}
	}
}
  • 응답
{: {
		책 이름 : "GraphQL",
		저자 : [
			{ 이름 : "가나다"},
			{ 이름 : "나다라"},
		]
	}
}

이렇게 GraphQL은 트리 구조로 쿼리 결과를 받기 위해 그래프를 탐색하는 쿼리 언어이다.

GraphQL의 특징

  • GraphQL은 HTTP를 통해 API 서버로 요청을 보내고 응답을 받는다.
  • 응답을 받을 시, 데이터 결과를 JSON 형식으로 받는다.
  • GraphQL은 서버 개발자가 작성한 각 필드에 대응하는 resolver 함수로 각 필드의 데이터를 조회할 수 있다.
  • GraphQL은 GraphQL 라이브러리가 조회 대상 schema가 유효한지 검사한다.

REST API의 한계

REST API의 한계로 인해 GraphQL을 사용한다고 볼 수 있다.
여기서는 GraphQL이 등장한 배경이라고 할 수 있는 REST API의 한계에 대해 알아본다.

  • Overfetch: 필요 없는 데이터까지 제공
    원하는 데이터를 REST API로 요청한다면 응답 데이터에는 실제로는 클라이언트에게 필요없는 정보가 포함되어 있을 수도 있다.

  • Underfetch: endpoint 가 필요한 정보를 충분히 제공하지 못함
    이 경우 클라이언트는 필요한 정보를 모두 확보하기 위해 각각의 자원에 해당하는 여러 엔드포인트로 추가적인 요청을 보내야 한다.

  • 클라이언트 구조 변경 시 엔드포인트 변경 또는 데이터 수정이 필요
    REST API에서는 자원의 크기와 형태를 서버에서 결정하기 때문에 클라이언트가 직접 데이터의 형태를 결정할 수 없다. 때문에 만약 클라이언트에서 필요한 데이터의 내용이 변할 경우 다른 엔드포인트로 변경된 내용을 가져오거나 서버에서 데이터를 수정해줘야 한다.

REST API와 GraphQL의 차이점

위의 한계를 통해 살펴봤듯이 REST API와 GraphQL은 다음과 같은 차이가 있다.

REST APIGraphQL
Resource에 대한
형태 정의, 데이터 요청 방법
연결분리
Resource의 크기와 형태서버에서 결정클라이언트에서 결정
Resource 표시URISchema
작업 유형MethodQuery, Mutation 타입
여러 Resource에 접근시여러 번의 요청한번의 요청
작업 처리엔드포인트에 정의된 핸들링 함수를 호출각 필드에 대한 resolver를 호출

GraphQL의 장점

REST API의 한계때문에 원하는 대로 정보를 가져올 수 있고, 보다 편하게 정보를 수정할 수 있도록 하는 표준화된 Query language인 GraphQL이 등장하게 되었다.
GraphQL의 장점은 다음과 같다.

  • 하나의 endpoint로 요청한다.
    graphql은 /graphql이라는 하나의 endpoint로 요청을 받는다.
    그리고 그 요청에 따라 query,mutation을 resolver 함수로 전달하여 응답한다. 모든 클라이언트 요청은 POST 메소드를 사용한다.

  • underfetching, overfetching 이 없다.
    원래는 여러 endpoint로 요청해야 하는 데이터를 하나의 endpoint로 요청하여 데이터를 받아온다.

  • playground 기능이 있다.
    graphql 서버를 실행하면 playground라는 GUI를 이용해 resolver, schema 를 직관적으로 보고 테스트할 수 있다.

  • 클라이언트 구조 변경에도 지장이 없다.
    클라이언트 구조가 바뀌어도 서버에 지장이 없다. 클라이언트에서는 필요한 데이터에서 요구사항을 쿼리로 작성하기만 하면 된다.

GraphQL의 단점

GraphQL의 단점은 다음과 같다.

  • REST API에 친숙하다면 GraphQL를 학습이 필요하다.
  • 캐싱이 REST보다 훨씬 복잡하다.
    GraphQL에선 POST 메소드만을 이용해 요청을 보내기 때문에 각 메소드에 따른 캐싱을 지원받을 수 없다.
    (그래서 이를 보안하기 위해 Apollo 엔진의 캐싱, 영속 쿼리 등이 등장하게 되었다.)
  • 고정된 요청과 응답만 필요할 경우 Query 로 인해 요청의 크기가 RESTful API보다 더 커진다.

Ch2. GraphQL 다루기

GraphQL의 구조와 문법에 대해 알아본다.

GraphQL Keywords

GraphQL에서는 Query를 이용해 원하는 데이터를 요청할 수 있다. Create, Delete와 같이 저장된 데이터를 수정하는 경우 Mutation을 이용한다.
또한 GraphQL에서는 구독(Subscription)이라는 개념을 이용해 실시간 업데이트를 구현할 수 있다.
Subscription는 Client(요청)-Server(응답) 모델과 다르게, 발행/구독(pub/sub) 모델을 따른다. 클라이언트가 어떤 이벤트를 구독하면, 클라이언트는 서버와 WebSocket을 기반으로 지속적인 연결을 형성하고 유지하게 된다. 그 후 특정 이벤트가 발생하면 서버는 대응하는 데이터를 클라이언트에 전달해준다.

  • Query: 저장된 데이터 가져오기 (REST의 GET과 비슷)
  • Mutation: 저장된 데이터 수정
  • Create: 새로운 데이터 생성
  • Update: 기존의 데이터 수정
  • Delete: 기존의 데이터 삭제
  • Subscription: 특정 이벤트 발생시 서버가 대응하는 데이터를 실시간으로 클라이언트에게 전송

GraphQL 쿼리(query, 데이터 조회) 구조

  • 필드(field)
    서버에 필요한 데이터 필드명을 입력하여 요청하면 이에 대한 응답을 돌려받을 수 있다. 하위 내용은 중괄호를 이용하여 표현한다.
    주석은 #으로 작성한다.
{
  pokemon {
    name
    # 동료 목록
    friends {
      name
    }
  }
}
{
  "data": {
    "pokemon": {
      "name": "피카츄",
      "friends": [
        {
          "name": "파이리"
        },
        {
          "name": "꼬부기"
        },
        {
          "name": "이상해씨"
        }
      ]
    }
  }
}

GraphQL 쿼리는 관련 객체 및 필드를 순회할 수 있기 때문에 위처럼 클라이언트가 하나의 요청을 보냄으로써 관련 데이터를 가져올 수 있다.

  • 전달인자(Arguments)
    필드에 소괄호로 전달인자를 사용하면 해당 필드 중 전달인자에 해당하는 데이터만 받아올 수 있다.
{
  pokemon(id: "25") {
    name
    type
  }
}

[코드] id가 1000인 human의 name과 height를 쿼리

{
  "data": {
    "pokemon": {
      "name": "피카츄",
      "type": "electric"
    }
  }
}
  • 별명(Aliases)
    필드 이름을 중복해서 사용할 수 없기 때문에 만약 중복으로 사용해야 한다면 별명을 붙여 쿼리를 작성한다.
{
  startFire: pokemon(starting: fire) {
    name
  }
  startWater: pokemon(starting: water) {
    name
  }
}
{
  "data": {
    "startFire": {
      "name": "파이리"
    },
    "startWater": {
      "name": "꼬부기"
    }
  }
}
  • 오퍼레이션 네임(Operation name)
    지금까지의 예제는 쿼리와 쿼리 네임을 모두 생략했지만, 실제로는 코드를 분명하게 작성해야 한다.
query PokemonAndFriends {
  pokemon {
    name
    friends {
      name
    }
  }
}
{
  "data": {
    "pokemon": {
      "name": "피카츄",
      "friends": [
        {
          "name": "파이리"
        },
        {
          "name": "꼬부기"
        },
        {
          "name": "이상해씨"
        }
      ]
    }
  }
}

예제에서 맨 처음 작성한 query는 오퍼레이션 타입이다.
오퍼레이션 타입에는 query 말고도 mutation, subscription, describes 등이 있다. 쿼리를 약식으로 작성하지 않는 한 이런 오퍼레이션 타입은 반드시 필요하다. 오퍼레이션 이름을 작성할 때는 오퍼레이션 타입에 맞는 이름으로 작성하는 것이 가독성이 좋다.

  • 변수(Variables)
    동적으로 전달인자를 받아 쿼리하는 경우가 많기 때문에 전달인자를 변수에 할당하여 사용한다.
    오퍼레이션 네임 옆에 변수를 $변수 이름: 타입 형태 로 정의한다.
    !는 옵션으로, 만약 '$starting: Starting!' 일 때, starting 변수는 반드시 starting이여야 한다는 의미다. 또한 null하지 않고 반드시 값이 들어온다는 의미이기도 하다.
query PokemonAndFriends($starting: Starting) {
  pokemon(starting: $starting) {
    name
    friends {
      name
    }
  }
}
  • 뮤테이션(mutation, 데이터 수정)
    mutation이라는 키워드를 사용하여 서버 측 데이터를 수정할 수 있다.
mutation CreatePokemonForGeneration($gn: Generation!, $ver: Version!) {
  createPokemon(generation: $gn, version: $ver) {
    name
    type
  }
}
  • 스키마/타입(Schema/Type)
    GraphQL 스키마는 서버에서 가져올 수 있는 객체의 종류, 그리고 포함하는 필드를 나타내는 객체 유형으로 구성되어 있다.
type Character {
  name: String!
  friends: [Friend!]!
}

Character는 GraphQL 객체 타입이며, 즉 필드가 있는 타입이다. 스키마에 있는 대부분의 타입은 객체 타입이다.
name, friends 은 Character 타입의 필드이다. GraphQL 쿼리의 Character 타입 어디서든 사용할 수 있는 필드다.
String은 내장된 스칼라 타입 중 하나이다. 이는 단일 스칼라 객체로 확인되는 유형이며 쿼리에서 하위 선택을 가질 수 없다. 스칼라 타입에는 ID, Int도 있다.

  • 리졸버(Resolver)
    요청에 대한 응답을 결정해주는 함수이다. 데이터를 가져오는 구체적인 과정을 직접 구현해야 할 때 (데이터베이스 쿼리, 원격 API 요청 등)을 Resolver가 담당한다.
    GraphQL의 여러 가지 타입 중 Query, Mutation, Subscription과 같은 타입의 로직을 작성한다.
    위와 같이 스키마를 정의하면 그 스키마 필드에 사용되는 함수의 실제 행동을 Resolver에서 정의합니다. 또한 이러한 함수들이 모여 있어 보통 Resolvers라 부른다.
const db = require("./../db")
const resolvers = {
  Query: { // Query : 저장된 데이터 가져오기 (REST의 GET과 비슷)
		getUser: async (_, { email, pw }) => {
			db.findOne({
				where: { email, pw }
			}) ... // 실제 DB에서 데이터를 가져오는 로직
			//...
		}
  },
  Mutation: { // Mutation : 저장된 데이터 수정하기 ( Create , Update , Delete )
		createUser: async (_, { email, pw, name }) => {
			//...
		}
  }
  Subscription: { //Subscription : 실시간 업데이트
    newUser: async () => {
      //...
		}
  }
};

Ch3. GraphQL Explorer로 실습하기

GitHub GraphQL API는 playground의 일종인 explorer를 제공한다. 이 explorer를 사용해 graph ql 구문으로 깃헙 로그인, 레포지토리 검색 및 접근이 가능하다.

사용법은 아래와 같다.
1. explorer에 접속
🔗 https://docs.github.com/en/graphql/overview/explorer
2. GitHub id로 로그인
3. GraphQL 쿼리를 이용해 repository 등 여러 정보에 접근하거나 조회해 본다.
explorer 버튼을 누르면 항목을 체크하여 자동으로 쿼리를 완성할 수 있다.
작성하였으면 '▶'버튼을 눌러 실행하여 응답을 받을 수 있다.


참고자료

1. Graph QL 한글문서
한글로 설명된 Graph QL 문서이다.
위의 설명에 대해 자세히 참고할 수 있다.
🔗 https://graphql-kr.github.io/learn/

profile
프론트엔드 개발자 NH입니다. 시리즈로 보시면 더 쉽게 여러 글들을 볼 수 있습니다!

0개의 댓글