영감을 (inspire) 주고픈 개발 블로그

GraphQL 에 대한 간단한 이해 본문

개발

GraphQL 에 대한 간단한 이해

inspire12 2024. 2. 4. 22:02

이번 프로젝트의 어드민 개발에 GraphQL(이하 gql)을 사용하면서 내용을 정리해봤습니다 

기본 개념 kakao tech에서 정리한 내용이 너무 괜찮아서 주요 내용을 가져오고 Spring 과 vue 를 이용해 구현해보겠습니다.

 

https://tech.kakao.com/2019/08/01/graphql-basic/

 

GraphQL 개념잡기

GraphQL은 페이스북에서 만든 쿼리 언어입니다. GrpahQL은 요즘 개발자들 사이에서 자주 입에 오르내리고 있으나, 2019년 7월 기준으로 얼리스테이지(early-stage)임은 분명합니다. 국내에서 GraphQL API를 O

tech.kakao.com

 

GraphQL 이란?

Graph QL(이하 gql)은 Structed Query Language(이하 sql)와 마찬가지로 쿼리 언어입니다. 하지만 gql과 sql의 언어적 구조 차이는 매우 큽니다. 또한 gql과 sql이 실전에서 쓰이는 방식의 차이도 매우 큽니다. gql과 sql의 언어적 구조 차이가 활용 측면에서의 차이를 가져왔습니다. 이 둘은 애초에 탄생 시기도 다르고 배경도 다릅니다. sql은 데이터베이스 시스템에 저장된 데이터를 효율적으로 가져오는 것이 목적이고, gql은 웹 클라이언트가 데이터를 서버로 부터 효율적으로 가져오는 것이 목적입니다. sql의 문장(statement)은 주로 백앤드 시스템에서 작성하고 호출 하는 반면, gql의 문장은 주로 클라이언트 시스템에서 작성하고 호출 합니다.

 

GraphQL 구조 

쿼리/뮤테이션(query/mutation)

gql에서는 굳이 쿼리와 뮤테이션을 나누는데 내부적으로 들어가면 사실상 이 둘은 별 차이가 없습니다. 쿼리는 데이터를 읽는데(R) 사용하고, 뮤테이션은 데이터를 변조(CUD) 하는데 사용 한다는 개념 적인 규약을 정해 놓은 것 뿐입니다.

스키마/타입(schema/type)

오브젝트 타입과 필드를 정의합니다. Request와 Response 등 데이터를 표현하는 

 

리졸버(resolver)

gql 에서는 데이터를 가져오는 구체적인 과정을 직접 구현 해야 합니다. gql 쿼리문 파싱은 대부분의 gql 라이브러리에서 처리를 하지만, gql에서 데이터를 가져오는 구체적인 과정은 resolver(이하 리졸버)가 담당하고, 이를 직접 구현 해야 합니다. 

gql 쿼리에서는 각각의 필드마다 함수가 하나씩 존재 한다고 생각하면 됩니다. 이 함수는 다음 타입을 반환합니다. 이러한 각각의 함수를 리졸버(resolver)라고 합니다. 

인트로스펙션(introspection)

 REST의 API 명세서 공유와 같은 문제를 해결하는 것이 gql의 인트로스펙션 기능입니다. gql의 인트로스펙션은 서버 자체에서 현재 서버에 정의된 스키마의 실시간 정보를 공유를 할 수 있게 합니다. 이 스키마 정보만 알고 있으면 클라이언트 사이드에서는 따로 연동규격서를 요청 할 필요가 없게 됩니다

 

개념 정리 

 

Spring boot 에서 GraphQL 서버 만들기 

build.gradle 

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
    implementation 'org.springframework.boot:spring-boot-starter-graphql'
    implementation 'com.graphql-java-kickstart:playground-spring-boot-starter:5.10.0'
    ...
}

 

application.yml

spring:
  graphql:
    path: /graphql
    graphiql:
      path: /graphiql
      enabled: true
      printer:
        enabled: true
    schema:
      locations: classpath:graphql/**/
      file-extensions: .graphqls,.gqls
      introspection:
        enabled: true

 

여기서 주요하게 볼건 spring.graphql.schema.locations 입니다. 여기 위치로 graphql의 schema 파일을 설정합니다

 

resources/graphql/schema.graphqls

type Member{
    id: ID!
    name: String!
    age: Int!
}

type Query{
    getMember(id: ID!): Member
    getMembers: [Member]
}

type Mutation{
    save(name: String!, age: Int!): Member
}

 

이 graphqls 파일만 지정해줘도 네트워크 통신이 가능합니다. 위 요청에 맞게 데이터를 전송하기 위해 비즈니스로직을 만드는 과정입니다. 

 

이제 위 type 에 매핑할 코드를 만들어 추가합니다. 

 

src/main/kotlin/com/inspire12/apiver2/graphql/schema/Member.kt

package com.inspire12.apiver2.graphql.schema

data class Member (
    val id: Long? = null,
    val name: String? = null,
    val age: Int = 0
)

 

controller가 resolver의 역할을 합니다. 요청을 받아서 데이터를 넘기는 부분입니다. 

src/main/kotlin/com/inspire12/apiver2/graphql/resolvers/MemberResolver.kt

package com.inspire12.apiver2.graphql.resolvers

import com.inspire12.apiver2.graphql.schema.Member
import org.springframework.graphql.data.method.annotation.Argument
import org.springframework.graphql.data.method.annotation.MutationMapping
import org.springframework.graphql.data.method.annotation.QueryMapping
import org.springframework.graphql.data.method.annotation.SchemaMapping
import org.springframework.stereotype.Controller

@SchemaMapping
@Controller
class MemberResolver {

    @QueryMapping
    fun getMember(@Argument id: Long?): Member {
        return Member(id, "inspire12", 22)
    }

    @QueryMapping
    fun getMembers(): List<Member> {
        return listOf(Member(1, "inspire12", 22), Member(2, "inspire12", 22))
    }

    @MutationMapping
    fun save(@Argument name: String?, @Argument age: Int): Member {
        return Member(3, name, age)
    }
}