brunch

You can make anything
by writing

C.S.Lewis

by 에디의 기술블로그 May 30. 2019

GraphQL in Spring Boot(2)

스프링부트 환경에서 GraphQL API 구축하기

지난주 회사 업무로 GraphQL 기술검토를 진행하였다. 팀원들이 이 글을 보고 조금이라도 도움이 되었으면 좋겠다. 일단, 필자가 작년에 GraphQL을 검토했던 적이 있다. 지난번 글은 스프링부트 환경에서 GraphQL을 어떻게 적용하는지에 대한 간단한 글이다. 

https://brunch.co.kr/@springboot/122

지난 포스팅은 기본 개념 설명 없이 작성한 아주 불친절한 글이었다. 이번 글에서는 좀 더 친절하게 글을 작성하겠다. 


GraphQL 이란? (Rest API 와 비교)


GraphQL 은 Graph Query Language의 줄임말이다. GraphQL은 어떤 특정 기술이 아니라, "기술을 위한 스펙"(명세)이라고 생각한다. 스펙에 맞는 구현체를 사용해야하는데, 구현체는 이미 누군가가 만들어놨다. Java, Javascript 등 각종 언어로 잘 구현이 되어있고, 이 글에서는 Java 구현체(라이브러리)를 사용하겠다.


GraphQL 이란?


GraphQL 은 페이스북에서 개발하여 발표한 "API를 위한 퀴리 언어"이다. GraphQL은 API에 있는 데이터에 대해서 이해하기 쉬운 설명을 제공하고 클라이언트에게 필요한 것을 정확하게 요청할 수 있는 기능을 제공하며 시간이 지남에 따라 API를 쉽게 진화시키고 강력한 개발자 도구를 지원한다. 

https://graphql-kr.github.io/


Rest API vs GraphQL (1) - 엔드포인트


기존에 많이 사용하던 Rest API와 GraphQL 은 서로 어떤점이 다른지 검토해보자. 필자의 개인적인 생각으로 글을 작성하고 있다. 잘못된 내용이나 부적절한 표현은 댓글로 피드백해주길 바란다.


Rest API

REST(Representational State Transfer)는 웹서비스에서 통신을 위한 아키텍처이다.이 용어는 "로이 필딩"(Roy Fielding)의 2000년 박사학위 논문에서 소개되었다. REST API는 리소스 중심으로 설계하고, 기능에 맞게 GET, POST, PUT, PATCH, DELETE 등의 메서드를 정의한다. 

Rest API에 대한 자세한 설명은 아래 링크를 참고하길 바란다.

https://docs.microsoft.com/ko-kr/azure/architecture/best-practices/api-design#organize-the-api-around-resources

https://brunch.co.kr/@springboot/59


Rest API는 매우 직관적이고 구현하기 쉽다. Rest API 예시는 아래와 같다. Cafe 시스템의 관리 API를 만들어보자.


카페에서 판매하는 커피의 리스트를 조회한다.  [GET 요청] http://도메인:포트/api/coffees

카페에서 판매하는 커피 중 특정 커피 하나를 조회한다.  

[GET 요청] http://도메인:포트/api/coffees/아메리카노

카페에 신규 커피를 메뉴에 등록한다.  [POST 요청] http://도메인:포트/api/coffees

카페에 특정 커피를 메뉴에서 삭제한다.  [DELETE 요청] http://도메인:포트/api/coffees/아메리카노

카페의 주문 리스트를 조회한다. [GET 요청] http://도메인:포트/api/orders


'커피' 및 '주문'이라는 리소스에 따라서 엔드포인트는 아래와 같이 정의할 수 있다. 

http://도메인:포트/api/coffees

http://도메인:포트/api/orders


RestAPI는 서비스가 복잡해질수록 EndPoint의 개수는 증가하게 될 것이다. 


GraphQL

RestAPI와는 다르게 GraphQL 은 주로 하나의 엔드포인트를 사용한다. (경우에 따라서는 두 개 이상의 엔드포인트를 구성할 수는 있지만, 이 글에서는 하나의 엔드포인트만 구성한다는 가정으로 진행하겠다.) GraphQL에서의 모든 요청을 아래 도메인으로 호출하도록 정의할 수 있다. 


http://도메인:포트/api/graphql


RestAPI와 비교하면 단 하나의 유일한 엔드포인트라서 API 주소가 매우 심플하다.



Rest API vs GraphQL (2) - 요청 과 응답


Rest API 는 일반적으로 Response 구조가 정해져 있다. 즉, Rest API 서버에서 항상 똑같은 Response 를 응답한다. 또한, Rest API 는 직관적이고 구현하기 단순하지만, 데이터가 복잡해질 수록 네트워크 요청이 여러번 이뤄져야 하는 경우가 발생한다. 반면에, GraphQL 은 사용자가 원하는 데이터를 직접 정확하게 요청할 수 있기 때문에, 복잡한 데이터에 대한 요청도 단 한번으로 수행할 수 있다. 예시를 들어보자. "아이유"라는 이름의 가수를 조회한다고 가정하자. 이때, 응답결과로 "아이유"의 이름, 나이, 본명을 응답받고 싶다면 어떻게 할 수 있을까?  아래와 같이 Request Body 에 원하는 데이터를 요청하면 된다. 


POST http://도메인:포트/api/graphql

Request Body

즉, GraphQL 에서의 요청과 응답은 클라이언트에서 필요한 데이터를 처음부터 스스로 요청할 수 있다.



Rest API vs GraphQL 정리


GraphQL은 엔드포인트를 심플하게 구성할 수 있고, 원하는 데이터를 직접 요청할 수 있다는 장점이 있다. GraphQL 의 장단점에 대해서 모든 내용을 이 글에서 담아내기는 조금 어려울 듯 하다. 장단점은 이정도로 정리하고, 자세한 구현 과정을 알아보도록 하자. 



GraphQL 구현 과정 및 Spec


GraphQL 를 구현하기 위한 과정 및 Spec 을 알아보자. 


GraphQL 구현 과정


GraphQL 구현하기 위해서는 아래와 같은 작업을 수행해야 한다.

스키마 정의

요청

응답


풀어서 표현하면 아래와 같다.


1. 데이터를 표현하세요(스키마 정의)

2. 원하는 것을 요청하세요(요청)

3. 예측 가능한 결과를 얻으세요(응답)



1. 데이터를 표현하세요!

데이터를 표현하기 위해서 스키마를 정의한다. "커피"를 표현하기 위해서 type Coffee를 정의하였다. 


2. 원하는 것을 요청하세요.

API를 호출할 때 원하는 것이 무엇인지 정의를 해야 한다. 그럼, 원하는 것을 요청하기 위해서 어떻게 해야 하는가?? POST 요청으로 원하는 데이터를 전송하면 된다. 필자는 "커피"를 조회하고 싶은데 "커피" 중에서 "mocha"라는 이름의 커피를 찾아서, 커피의 이름과 가격의 정보를 요청한다. 


아래와 같이 원하는 데이터를 요청할 수 있다. 


3. 예측 가능한 결과를 얻으세요. 

원하는 것을 요청하였다면, 서버에서 데이터를 반환하여 예측 가능한 결과를 얻게 될 것이다. "커피" 중에서 "mocha"라는 이름의 커피를 찾아서, 커피의 이름과 가격의 결과를 얻을 수 있다. 



Type System


아주 기본적인 Type의 예는 아래 Coffee와 같다. Coffee는 "모카", "라테", "아메리카노"와 같이 표현할 수 있는 이름으로 정의할 수 있다. 모든 Coffee는 이름을 가져야만 한다. 

A common pattern in many APIs, and indeed in GraphQL is to give objects an ID that can be used to refetch the object. So let's add that to our Human type. We'll also add a string for their home planet.


interface를 추가할 수도 있다. 카페에서 근무하는 "바리스타" 타입을 만들어보자. Human이라는 인터페이스를 구현하는 Barista 타입을 정의하였다. enum을 사용하였다.


Query Syntax


자세한 내용은, 생략한다. 공식 github을 참고하길 바란다.

https://github.com/graphql/graphql-spec



이제, 두가지 샘플 예시를 만들 것이다. 

첫번째 예시는 엔드포인트 및 상세 내용을 직접 구현한다.

두번째 예시는 라이브러리를 최대한 활용해서 구현한다.


GraphQL in 스프링부트 구현 (1)


이번 예시는 GraphQL 엔드포인트 및 상세 내용을 직접 구현한다.


디펜던시


스프링 부트 2.1.5.RELEASE 버전에서 실행하겠다. 

핵심 디펜던시로 graphql-java-kickstart/graphql-spring-boot 를 추가하자. 버전은 2019년5월30일현재 최신버전인 5.9.0 이다. 참고로, 이번 예시는 graphql-spring-boot-starter 에서 제공하는 기능을 최소한만 사용하고 나머지 기능은 전부 직접 구현할 것이다.


데이터를 표현하기 위한 스키마 정의


데이터를  표현하기 위해서, 스키마를 정의하자. resources 디렉터리에 cafe.graphql이라는 파일을 생성한다. 



EndPoint(엔드포인트) 및 상세 구현


엔드포인트를 직접 정의한다. @RestController 어노테이션을 정의하고, PostMapping을 받을 수 있도록 한다. 아래 샘플 코드와 같이 작성한 후 localhost:port/graphql를 호출하면 GraphQL API를 호출하게 된다. 

GraphUseCase를 구현하는 Provider 구현체를 작성해야 하는데, 이때 cafe.coffees 파일을 가져와서 로드해야 한다. 

Resource에서는 스키마를 정의한 파일을 불러온다. 그리고, GraphQL 클래스를 필드로 선언한다. 그리고, 아래와 같이... 작성한다. 역시 자세한 설명은 생략한다...

GraphQL 요청 RequestBody 에 


coffee 가 요청되면 coffeeDataFetcher를 실행하고, 

order 가 요청되면 orderDataFetcher를 실행한다. 


coffeeDataFetcher, orderDataFetcher는 DataFetcher를 구현한다. DataFetcher 인터페이스는 아래와 같다. 해당 인터페이스를 구현하는 구현체는 get 메서드를 구현해야 한다. 

해당 인터페이스를 구현함으로, 각각의 DataFetcher 컴포넌트에서는 get 메서드를 상세 구현을 해야 하는데, 실제 DB에서 데이터를 조회하는 비즈니스 로직이 포함된다. 



요청, 응답 테스트 (원하는 것을 요청해보자)


원하는 것을 요청해보자. 필자는 "mocha"라는 이름의 커피를 찾아서 응답을 받을 것이다. 만약, 응답 결과로 id, name, price 값을 리턴 받고 싶다. 어떻게 요청하면 되는가?  

POST 메서드 호출한다.

엔드포인트는 localhost:port/graphql이다. 

Request Body에 원하는 것을 요청한다.




참고로, OrderBy 또는 Sort를 적용할 수도 있다. 스키마를 정의해주고, DataFetchingEnvironment를 통해서 넘어온 값으로 실제 구현을 해주면 된다. 자세한 내용은 생략하겠다.



샘플 코드 참고

https://github.com/sieunkr/spring-boot-graphql/tree/master/spring-boot-graphql


지금까지 스프링 부트 환경에서 GraphQL을 구현하였다. 해당 방법은 graphql-spring-boot-starter에서 제공하는 기능을 최소한으로만 사용하고, 엔드포인트 정의 등 모든 기능을 직접 구현한 방법이다. 다음 예시에서는 graphql-spring-boot-starter에서 제공하는 기능을 충분히 사용해서 좀 더 간단하게 구축해보겠다. 



GraphQL in 스프링부트 구현 (2)


이번에는 graphql-spring-boot-starter  및 graphql-java-tools 라이브러리에서 제공하는 기능을 사용해서, 조금 더 간단하게 구현해보자. (간단하지만 실무에서 반드시 이렇게 하라고 얘기는 아니다.)


디펜던시


일단, graphql-spring-boot-starter 를 디펜던시 추가하면 자동으로 graphql-java-tools 가 같이 추가된다. 단, 기존 예시와는 다르게 graphql-java-tools 를 온전하게 잘 사용하기 위해서는 반드시 코틀린 버전을 명시해줘야 한다. 필자는 스프링부트 2.1.5.RELEASE 에서 검토 중인데, 해당 버전에서의 기본 코틀린 디펜던시는 1.2.X 버전이다. graphql-java-tools는 반드시 코틀린 1.3.X 버전이 적용되어야 한다. 왜냐면, graphql-java-tools에서 핵심 로직에서 코루틴(coroutine)을 사용했기 때문이다. 코루틴을 사용하기 위한 코틀린 최소버전은 1.3.X 이기 때문에, 이번 예시에서는 코틀린 버전을 1.3.10 으로 정확하게 명시를 하였다.


만약 스프링 부트 2.2.X로 버전을 올리면 별도로 코틀린 버전을 명시하지 않아도 코틀린 버전이 1.3.X으로 적용이 될 것이다. 하지만, 스프링부트 2.2.X 는 아직 마일스톤 버전이기 때문에 검토 대상에서 제외하였다. 조만간 스프링부트 2.2.0.RELEASE 가 릴리스 되면 그때 다시 검토하겠다. 


어쨌든, 코틀린 버전을 1.3.10으로 아래와 같이 정의하였다. 



Type, Repository


생략


스키마 정의


위 샘플과 동일하지만, 확장자를 반드시 graphqls로 지정하자. s 가 붙어야 한다. 


Resolver


GraphQLQueryResolver를 구현하는 Query 클래스를 만들자.  

GraphQLQueryResolver를 구현하는 클래스의 메서드는 스키마에 정의한 type Query와 매핑된다. 필자가 첫 번째 예시에서는 DataFetcher를 직접 구현하고, RuntimeWiring, GraphQLSchema 등의 클래스를 사용해서 한 땀 한 땀 코드를 작성하였는데, 이번 예시에서는 별도의 매핑 작업 없이 GraphQLQueryResolver를 구현하면 자동으로 매핑이 된다. 참고로 스키마 파일은 cafe.graphqls로, 반드시 확장자가 *. graphqls 이어야 한다. 확장자가 graphqls 인 경우에만 스키마 파일을 읽을 수 있다.  R

ㅇㅎunRuntimeWiringtimeWiring

Properties


첫 번째 예시와는 다르게 별도의 엔드포인트를 정의하지 않아도 된다. graphql-spring-boot-starter에서 제공하는 기능을 사용해서 아래와 같이 프로퍼티 정의만 해주면 된다. 엔드포인트를 /graphql로 정의하였다. 


요청, 응답 테스트


테스트를 해본다. 

응답 결과는 아래와 같다. 


graphql-spring-boot-starter, graphql-java-tools 기타 기능


필자가 상세하게 검토하진 않았지만, graphql-spring-boot-starter, graphql-java-tools 에서 더 많은 기능을 제공한다. 예를 들어서, 스키마를 파일로 정의하지 않고 자바 코드로 정의할 수도 있다. 기타 많은 기능을 제공하지만 더이상 검토하진 않았다. 

https://github.com/sieunkr/spring-boot-graphql/tree/master/spring-boot-graphql-tools



마무리


개인적으로 잠시 공부해본 결과 GraphQL 이 꽤 쓸만하다는 생각이 든다. 앞으로 GraphQL 프로젝트를 다시 할 기회가 언제 올지 모르겠지만, 기회가 되면 좀 더 상세하게 공부할 예정이다.




레퍼런스


https://www.graphql-java.com

https://www.holaxprogramming.com/2018/01/20/graphql-vs-restful-api/

https://medium.com/@kiyeopyang/%EA%B0%80%EC%9E%A5-%ED%98%84%EB%8C%80%EC%A0%81%EC%9D%B8-%EC%9B%B9%EC%9D%84-%EB%A7%8C%EB%93%A4%EC%9E%90-3%ED%8E%B8-graphql-cb69ce1a64b5

https://www.pluralsight.com/guides/building-a-graphql-server-with-spring-boot

https://medium.com/dtevangelist/graphql-%EC%B1%84%ED%83%9D-%ED%9B%84-netflix%EA%B0%80-%EB%B0%B0%EC%9A%B4-%EA%B2%83%EB%93%A4-2443180096da

https://pragmaticcoders.com/blog/how-to-use-graphql-in-spring-boot/

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari