일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- react
- react fragment
- React #effect hook #useEffect
- axios
- 코드스테이츠 #알고리즘 #그리디
- RateLimit
- interceptors
- html entities
- React #리액트 이벤트 주기 #리액트 이벤트
- 백준 #적록색약
- raect typescript #react #typescript #styled-component
- react #useCallback #react Hook
- 이친수
- DP #c++
- npm #not being able to find a file #npm install Error
- 얕은 복사 #깊은 복사 #shallow copy #deep copy
- React #controlled component #비제어 컴포넌트 #제어 컴포넌트
- useState #Hooks
- 백준 #직각삼각형
- JWT #토큰 #refreshToken #accessToken #Token #token #localStorage #sessionStorage
- #useRef #언제 쓰는데?
- 빡킹독
- rate limit
- React #Hook rules #Hook 규칙
- 플로이드 #c++
- 버블링 #갭쳐링 #이벤트 #JS
- React-Query
- donwstream #upstream #origin
- 다익스트라 #파티 #백준
- 노마드 코더 #타입스크립트 #typescript #class
- Today
- Total
꿈꾸는 개발자
GraphQL찍먹하기 본문
GraphQL은 페이스북에서 만든 쿼리 언어이다. graphQL은 Graph+Query Language의 줄임말이다. Server API를 통해 정보를 주고 받기 위해 사용하는 Query Language를 의미한다. 결론: API를 위한 쿼리 언어이다.
GraphQL에선 모든 데이터가 그래프 형태로 연결되어 있다고 전제한다. 일대일로 연결된 관계도 여러 계층으로 이루어진 관계도 모두 그래프이다. GraphQL은 클라이언트 요청에 따라 유연하게 트리 구조의 JSON 데이터를 응답으로 전송할 수 있다. (엄청난 장점)
GraphQL로 그래프 순회
상황 가정: 도서 목록 시스템을 구축
하나의 도서 목록에는 많은 책과 저자가 있으며, 각 책에는 최소한 한 명의 저자가 있다. 또한, 최소한 한 권의 책을 같이쓴 공동저자도 있을 것이다. 이러한 상황을 그래프로 표현하면 위와 같을 것이다. 데이터 조각들이나 나태내고자 하는 엔티티(책, 혹은 저자) 간의 관계를 나타낼 수 있게 된다.
엔티티는 사물의 구조나 상태, 동작 등을 모델로 표현하는 경우, 그 모델의 구성요소를 말합니다
이렇게 그래프를 그릴 수 있게 된다면, GraphQL을 사용해 트리를 추출할 수 있게 된다.
그래프에서 트리 추출하는 방법
GraphQL 쿼리와 위의 그래프에서 트리를 추출해보면 다음과 같을 것이다.
query {
책(ISBN이 "9780674430006") {
책 이름
저자 {
이름
}
}
}
한 권의 책만 검색하기 위해 ISBN이 "9780674430006인 조건을 걸었다. 서버가 해당 요청에 따른 반환된 쿼리는 아래와 같을 것이다.
{
책 : {
책 이름 : "GraphQL은 어렵지 않다",
저자 : [
{ 이름 : "김코딩"},
{ 이름 : "박해커"},
]
}
}
ISBN 번호를 사용하여 선택한 “책" 노드에서 시작한다. 그 다음 graphQl은 중첩된 각 필드로 표시된 간선을 따라 그래프를 탐색하기 시작한다. 쿼리 내 중첩된 "책 이름" 필드를 통해 책의 제목이 있는 노드로 이동을 한다. "저자"로 레이블이 지정된 "책"의 간선을 따라 "저자" 노드를 가져오고, 각 저자의 이름을 얻어오는 것이다.
GraphQL의 특징
- GraphQL에서는 Resource에 대한 형태 정의와 데이터 요청이 완전히 분리되어 있다.
- GraphQL에서는 Resource에 대한 정보만 정의하고, 필요한 크기와 형태는 클라이언트 단에서 요청 시 결정한다.
- GraphQL에서는 GraphQL Schema가 Resource를 나타내고 Query, Mutation 타입이 작업의 유형을 나타낸다.
- GraphQL에서는 한 번의 요청에서 여러 Resource에 접근할 수 있다.
- GraphQL에서는 요청받은 각 필드에 대한 resolver를 호출하여 작업을 처리한다.
GraphQL의 장단점
- 하나의 endpoint 요청
- graphql이라는 하나의 endpoint로 요청을 받고 그 요청에 따라 query , mutation을 resolver 함수로 전달해서 요청에 응답한다. (모든 클라이언트 요청은 POST 메서드를 사용한다)
- No! under & overfetching
- 여러 개의 endpoint 요청을 할 필요 없이 하나의 endpoint에서 쿼리를 이용해 원하는 데이터를 정확하게 API에 요청하고 응답으로 받을 수 있다.
- 강력한 playground (GUI를이용resolver와schema를 한 눈에 보고 테스트해 볼 수 있다)
GraphQL Playground
Visually explore Apollo Server
www.apollographql.com
- 클라이언트 구조 변경에도 지장이 없음
- 클라이언트 구조가 바뀌어도 필요한 데이터를 결정하고 받는 주체가 클라이언트이기 때문에 서버에 지장이 없다. 클라이언트에서는 무슨 데이터가 필요한 지에 대해 요구사항을 쿼리로 작성하면 된다.
GraphQL의 구조
GraphQL Keywords
서버로부터 데이터를 조회하는 경우, REST API에서는 GET 요청을 보내지만, GraphQL에선 Query를 이용해 원하는 데이터를 요청할 수 있다. Create/Delete와 같이 저장된 데이터를 수정하는 경우는 Mutation을 이용해 수행 가능하다.
구독(subscription)이라는 개념을 통해 실시간 업데이트를 구현할 수 있다. 이는 전통적인 Client(요청)- Server(응답) 모델을 따르는 Query 또는 Mutation과 달리 발행/구독 모델을 따른다. 클라이언트가 이벤트를 구독하면 클라이언트는 서버와 Websocket을 기반으로 지속적인 연결을 형성하고 유지한다. 특정 이벤트가 발생하면 서버는 대응하는 데이터를 클라이언트에 푸시한다.
- Query: 저장된 데이터 가져오기 (REST의 GET과 비슷)
- Mutation: 저장된 데이터 수정하기
- Create: 새로운 데이터 생성
- Update: 기존의 데이터 수정
- Delete: 기존의 데이터 삭제
- Subscription: 특정 이벤트가 발생 시 서버가 대응하는 데이터를 실기간으로 클라이언트에 전송한다.
Query(쿼리 데이터 조회)
필드(field):
매우 간단한 query(데이터 조회)를 실행했을 때 얻는 결과
- hero의 name을 쿼리
{
hero {
name
}
}
- 쿼리를 실행했을 때의 결과
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
필드 name은 string의 타입을 반환한다. 위 경우 hero의 name이 "R2-D2"임을 알 수 있다. 쿼리와 결과가 정확하게 같은 모양임을 알 수 있다. GraphQL에서 필수적인 부분이다.
{
hero {
name
# 이런 식으로 GraphQL 내에서 주석도 작성할 수 있습니다.
friends {
name
}
}
}
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
위와 같이 원하는 필드를 중첩하며 쿼리하는 것도 가능하다. friends 필드는 배열을 반환한다. GraphQL 쿼리는 관련 객체 및 필드를 순회할 수 있기 때문에 고전적인 REST API처럼 다양한 endpoint를 만들어 각기 요청을보내는 대신 client가 하나의 요청을 보내 관련 데이터를 가져올 수 있다.
전달인자(Arguments)
필드에 인수를 전달하는 부분을 추가하게 되면 쿼리의 필드 및 중첩된 객체들에 전달하여 원하는 데이터만 받을 수 있다.
{
human(id: "1000") {
name
height
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
id가 1000인 human의 이름과 키를 쿼리해 올 수 있다. REST API의 ?id=1000과 비슷한 개념
별명(Aliases)
필드를 중복해서 사용할 수 없기 때문에 중복해야 할 때는 별명을 붙여서 쿼리는 한다.
{
hero(episode: EMPIRE) {
name
}
hero(episode: JEDI) {
name
}
}
위처럼 중복해서 쿼리할 수 없음
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
위처러 별명을 붙여야 함
{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
퀴러 결과
오퍼레이션 네임 (Operation name)
실제 앱에서는 코드를 모호하지 않게 작성하는 것이 중요하다.
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
위처럼 query keyword와 query name을 작성한다.
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
퀴러 결과
위 쿼리는 오퍼레이션 타입이다. 오퍼레이션 타입에는 query 뿐만 아니라, mutation , subscription, describes 등이 있다. 쿼리를 약식으로 작성하지 않는 한 이런 오퍼레이션 타입은 반드시 필요하다. 오퍼레이션 네음을 작성할 때는 타입에 맞는 이름을 작성하는 것이 가독성에 좋다.
변수(Variables)
고정된 인수 대신 동적 인수를 받아 쿼리하는 경우가 대다수이다. 변수는 그런 인수들을 동적으로 받고 싶을 때 사용한다.
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
위는 변수를 사용해서 작성된 쿼리이다.
오퍼레이션 네임 옆에 변수를 $변수 이름: 타입 형태로 정의한다. 위의 예시처럼 $episode: Episode일 때, 뒤에 !가 붙는다면 episode는 반드시 Episode여야 한다는 뜻입니다. !는 옵셔널한 사항입니다.
뮤테이션(mutation, 데이터 수정)
GraphQL은 대개 데이터를 가져오는 데에 중점을 두고 있지만, 서버 측 데이터를 수정하기도 한다.
REST API에서 GET 요청을 사용하여 데이터를 수정하지 않고, POST 혹은 PUT 요청을 사용하는 것처럼 GraphQL도 유사하다. GraphQL은 mutation이라는 키워드를 사용하여 서버 측 데이터를 수정한다.
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
스키마/타입(schema/Type)
GraphQL 스키마의 가장 기본적인 구성 요소는 서비스에서 가져올 수 있는 객체의 종류, 그리고 포함하는 필드를 나타내는 객체 유형이다.
type Character {
name: String!
appearsIn: [Episode!]!
}
- character는 GraphQL 객체 타입이다, 필드가 있는 타입임을 의미한다. 스키마에 있는 대부분의 타입은 객체 타입이다.
- name과 appearIn은 character 타입의 필드이다. name과 appearIn은 GraphQl 쿼리의 Chareacter 타입 어디서든 사용할 수 있는 필드이다.
- string은 내장된 스칼라 타입 중 하나이다. 단일 스칼라 객체로 확인되는 유형이며 쿼리에서 하위 선택을 가질 수 없다. (ID, Int도 있음)
- !가 붙는다면 필드는 nullable하지 않고 반드시 값이 들어와야 한다. 이것을 붙여 쿼리하면 반드시 값을 받을 수 있을 것이라고 예상할 수 있다.
- []는 배열을 의미합니다. 배열에도 !가 붙을 수 있다. ! 뒤에 붙어 있어 null 값을 허용하지 않음으로 항상 0개 이상의 요소를 포함한 배열을 기대할 수 있다.
리졸버(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 }
}) ... // 실제 디비에서 데이터를 가져오는 로직을 작성합니다.
...
}
},
Mutation: { // **Mutation :** 저장된 데이터 수정하기 ( Create , Update , Delete )
createUser: async (_, { email, pw, name }) => {
...
}
}
Subscription: { // **Subscription :** 실시간 업데이트
newUser: async () => {
...
}
}
};
GraphQL에서는 데이터를 가져오는 구체적인 과정을 직접 구현해야 하는데 이와 같은 작업(e.g. 데이터베이스 쿼리, 원격 API 요청)을 Resolver가 담당하게 된다.
보다 자세한 기능에 대한 링크