티스토리 뷰
REST API의 한계
1. overfetch
필요 없는 데이터까지 제공됨
- 유저 이름만 필요한데 유저의 가입일, 생일, 주소 등이 함께 제공
2. underfetch
endpoint가 필요한 정보를 충분히 제공하지 못해 추가적인 요청을 보내야 함
- endpoint마다 제공하는 정보가 한정되어 유저, 유저의 포스팅 목록, 유저의 팔로워를 얻기 위해 세 번의 요청 필요
3. 클라이언트 구조 변경 시 엔드포인트 변경 또는 데이터 수정 필요
서버가 자원의 크기와 형태를 결정하기 때문에
클라이언트에서 필요한 데이터가 변할 경우 다른 endpoint를 통해 변경된 데이터를 가져오거나 수정해야 함
=> 원하는대로 정보를 가져올 수 있고, 보다 편하게 정보를 수정할 수 있는 표준화된 Query Language가 만들어짐(=GraphQL)
GraphQL(Graph Query Language)
페이스북에서 만든 쿼리 언어(오픈소스). API를 위한 쿼리 언어
(쿼리 언어: 쿼리를 보내 데이터베이스 및 정보 시스템에서 데이터를 요청하고 탐색하는 컴퓨터 프로그래밍 언어)
모든 데이터가 그래프 형태로 연결되었다는 것을 전제로 함
트리 구조로 쿼리 결과를 받기 위해 그래프를 탐색하는 쿼리 언어
클라이언트 요청에 따라 그래프를 정렬하여 트리 구조의 JSON 데이터를 응답으로 전송
-> 고정된 자원을 받는 게 아닌 클라이언트 요청에 따라 자원을 유연하게 가져올 수 있음
GraphQL 특징
1. HTTP를 통해 API 서버로 요청을 보내고 응답 받음
2. 응답받을 때 데이터 결과를 JSON 형식으로 받음
3. 서버 개발자가 작성한 각 필드에 대응하는 resolver 함수로 각 필드의 데이터를 조회
4. GraphQL 라이브러리가 조회 대상 Schema(객체유형)가 유효한지 검사
GraphQL로 데이터 순회
관련 객체 및 필드를 순회하여 한 요청으로 여러 데이터를 받을 수 있음
가지고 있는 데이터 조각들이나 나타내고자하는 엔티티 간의 관계를 나타낼 수 있음
(엔티티: 모델의 구성 요소. 노드. 그 자체로는 의미를 표현할 수 없고 이들이 모여 의미를 제공
ex. 학번, 이름, 학과 => 학생)
GraphQL 장점
1. 하나의 endpoint 요청. No under&overfetching
query(조회), mutation(수정)을 resolver 함수로 전달하여 응답. 모든 클라이어트 요청에 POST 사용
하나의 endpoint에서 쿼리를 이용해 원하는 데이터만을 정확하게 API에 요청하고 응답으로 받을 수 있음
2. 강력한 playground
graphql 서버를 실행하면 playground라는 GUI를 이용해 resolver와 schema를 한 눈에 보고 테스트해볼 수 있음(POSTMAN)과 비슷
3. 클라이언트 구조 변경에도 지장이 없음
클라이언트 구조가 바뀌어도 필요한 데이터를 결정하고 받는 주체가 클라이언트이기 때문에 서버에 영향을 받지 않음
클라이언트에서 무슨 데이터가 필요한 지에 대해서만 요구사항을 쿼리로 작성하면 됨
GraphQL 단점
1. REST API에 친숙한 개발자의 경우 GraphQL을 학습하는데 시간 필요
2. 캐싱이 REST API보다 훨씬 복잡
HTTP에선 각 메서드에 따라 캐싱이 구현되어 있으나
GraphQL은 POST 메서드만 사용하여 요청을 보내기 때문에 각 메서드에 따른 캐싱을 지원받을 수 없음
-> 이를 보완하기 위해 Apollo 엔진의 캐싱과 영속 쿼리 등이 등장
(캐싱: 이미 요청을 해서 데이터를 가지고 있다면 서버에 다시 요청하지 않고 웹 캐시 내에 가지고 있는 데이터를 반환)
3. 고정된 요청과 응답만 필요한 경우 Query로 인해 요청의 크기가 RESTful API 경우보다 커짐
REST API와 GraphQL
REST API | GraphQL | |
Resource에 대한 형태 정의와 데이터 요청 방법 |
연결 | 분리 |
Resource의 크기와 형태 결정 | 서버가 결정 | 클라이언트가 요청 시 결정 (서버는 Resource 정보만 정의) |
Resource와 작업 유형 | URI, Method | GraphQLSchema, Mutation 타입 |
여러 Resource에 접근하려면 | endpoint마다 요청 | 한 번에 여러 Resource 접근 가능 |
작업 처리 | 해당 endpoint에 핸들링된 함수 호출 | 요청받은 각 필드에 대한 resolver 호출 |
GraphQL Keywords
query, mutation은 전통적인 요청/응답 모델을 따르고, subscription은 발행/구독(pub/sub) 모델을 따름
1. query 쿼리
저장된 데이터 가져오기(REST API의 GET)
① filed 필드
GraphQL 필수 요소. 쿼리와 결과가 같은 모양임
// 요청
{
hero {
name
friends {
name
}
}
}
// 응답
# #으로 주석처리 가능
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
② arguments 전달인자
필드에 인자를 추가하여 원하는 데이터만 받을 수 있음
// 요청
{
human(id: "1000") {
name
height
}
}
// 응답
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
③ aliases 별명
필드 이름을 중복해서 사용할 수 없기 때문에
필드 이름을 중복해서 사용해야할 때 별명을 붙여 그 별명을 필드의 키로한 객체를 응답 받음
{
empireHero: hero(episode: EMPIRE) {
name
}
jediHero: hero(episode: JEDI) {
name
}
}
// 응답
{
"data": {
"empireHero": {
"name": "Luke Skywalker"
},
"jediHero": {
"name": "R2-D2"
}
}
}
④ operation name 오퍼레이션 네임
위 예시들은 쿼리와 쿼리 네임을 생략한 축약형 구문
실제 사용 시에는 코드에 명확하게 작성하는 것이 좋음
# 오퍼레이션 타입(query/mutation/subscription/describes) , 이름
query HeroNameAndFriends {
hero {
name
friends {
name
}
}
}
⑤ Variables 변수
동적으로 변수를 받아 쿼리를 작성하고 싶을 때 사용
* 템플릿 리터럴(${})은 쿼리 주입 공격에 취약하기 때문에 변수$를 사용할 것 *
$변수 이름: 타입
`
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
`
{
episode: EMPIRE
}
import { graphql } from '@octokit/graphql';
const token = process.env.REACT_APP_TOKEN
const graphqlWithAuth = graphql.defaults({
headers: {
// token 대신 bearer를 입력하기도 함 -> 토큰 인증 방식의 하나
authorization: `token ${token}`,
},
});
const getHeroNameAndFriends = async () => {
const { hero } = await graphqlWithAuth(
`
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
`
{
episode: EMPIRE
}
return hero
}
2. mutation 뮤테이션
데이터 수정
mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) {
createReview(episode: $ep, review: $review) {
stars
commentary
}
}
3. schema/type 스키마/타입
스키마의 기본 구성요소: 서비스에서 가져올 수 있는 객체의 종류, 포함하는 필드를 나타내는 객체 유형
type Character {
name: String!
appearsIn: [Episode!]!
}
Character: GraphQL의 객체 타입(=필드가 있는 객체 타입) . 스키마에 있는 대부분의 타입이 이에 해당
String: 내장된 스칼라 타입 중 하나. 단일 스칼라 객체로 확인되는 유형. 쿼리에서 하위 선택을 가질 수 없음
(Scalar: 단 하나의 값만 저장할 수 있는 타입 +ID, Int)
!: Non-Null. null이 들어오면 실행 오류 발생. 이 필드는 반드시 값이 들어와야 함. 옵셔널
[]: 배열. []!은 0개 이상의 요소를 포함한 배열을 뜻함
4. Resolver 리졸버
요청에 대한 응답을 결정해주는 함수
데이터를 가져오는 구체적인 과정을 구현한 함수(ex. 데이터베이스 쿼리, 원격 API 요청)
스키마 정의 -> 스키마 필드에 사용되는 함수를 정의(리졸버)
이 리졸버들의 모음을 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 () => {
...
}
}
};
23.01.30
코스 S4U6 GraphQL
'코드스테이츠(SEB_FE_42)' 카테고리의 다른 글
[Testing] TDD (0) | 2023.01.31 |
---|---|
[기타] 환경변수(.env), 스크롤 페이지 상단에 위치시키기, json-server (0) | 2023.01.30 |
[컴퓨터 공학] 문자열, 그래픽, 가비지 컬렉션 (0) | 2023.01.27 |
[컴퓨터 공학] OS (0) | 2023.01.27 |
[컴퓨터 공학] 컴퓨터 기본 구조, CPU, Memory (0) | 2023.01.25 |