[GraphQL]GraphQL 구조
GraphQL 구조
GraphQL Keywords
조회(Read) 시, REST API에선 GET 요청, GraphQL에서는 Query를 이용해 데이터를 요청.
또한 Create, Delete와 같이 저장된 데이터를 수정하는 경우에는 Mutation을 이용해 수행.
GraphQL에서는 구독(Subscription)이라는 개념을 제공, 실시간 업데이트 구현
Subscription는 전통적인 Client(요청)-Server(응답) 모델을 따르는 Query 또는 Mutation과 달리, 발행/구독(pub/sub) 모델을 따름
클라이언트가 어떤 이벤트를 구독하면, 클라이언트는 서버와 WebSocket을 기반으로 지속적인 연결을 형성, 유지. 그 후 특정 이벤트가 발생하면, 서버는 대응하는 데이터를 클라이언트에 푸시.
- Query: 저장된 데이터 가져오기
- Mutation: 저장된 데이터 수정
- Create: 새로운 데이터 생성
- Update: 기존의 데이터 수정
- Delete: 기존의 데이터 삭제
- Subscription: 특정 이벤트가 발생 시 서버가 대응하는 데이터를 실시간으로 클라이언트에게 전송
쿼리
필드(field)
{
hero {
name
}
}
쿼리(조회)
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
결과
쿼리와 결과가 정확하게 같은 모양, raphQL에 있어서 필수적. 서버에 요청했을 때 예상했던 대로 돌려받고, 서버는 GraphQL을 통해 클라이언트가 요구하는 필드를 정확히 알기 때문
{
hero {
name
# 주석도 가능
friends {
name
}
}
}
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke Skywalker"
},
{
"name": "Han Solo"
},
{
"name": "Leia Organa"
}
]
}
}
}
필드를 중첩하여 쿼리하는 것도 가능
GraphQL 쿼리는 관련 객체 및 필드를 순회할 수 있기 때문에, 하나의 요청으로 관련 데이터를 가져올 수 있음
전달인자(Arguments)
데이터만 받아올 수 있음
{
human(id: "1000") {
name
height
}
}
{
"data": {
"human": {
"name": "Luke Skywalker",
"height": 1.72
}
}
}
별명(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는 오퍼레이션 타입
오퍼레이션 타입은 mutation, subscription, describes 등
오퍼레이션 네임을 작성할 때는 오퍼레이션 타입에 맞는 이름으로 작성
변수(Variables)
동적으로 인수를 받아 쿼리하는 경우가 대다수
query HeroNameAndFriends($episode: Episode) {
hero(episode: $episode) {
name
friends {
name
}
}
}
변수를 써서 작성된 쿼리
$변수 이름: 타입 형태
로 정의
!가 붙는다면 episode는 반드시 Episode여야 한다는 뜻
뮤테이션(mutation, 데이터 수정)
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 쿼리의 Character 타입 어디서든 사용할 수 있는 필드.
- String은 내장된 스칼라 타입 중 하나. 단일 스칼라 객체로 확인되는 유형, 쿼리에서 하위 선택을 가질 수 없음. 스칼라 타입에는 ID, Int도 있음.
- !가 붙는다면 이 필드는 nullable하지 않고 반드시 값이 들어온다는 의미. !가 붙은 쿼리는 반드시 값을 받을 수 있을 것 예상.
- [ ]는 배열 의미. 배열에도 !가 붙을 수 있음. 여기서는 ! 이 뒤에 붙어 있어 null 값을 허용하지 않으므로 항상 0개 이상의 요소를 포함한 배열 기대
리졸버(Resolver)
요청에 대한 응답을 결정해주는 함수
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 () => {
...
}
}
};
댓글남기기