- 스프링부트 환경에서 간단하게 GraphQL 적용해보기
이 글을 조금 수정해서, 신규 포스팅을 하였습니다. 아래 글을 읽어주시길 바랍니다.
https://brunch.co.kr/@springboot/191
이 글은, Spring Boot 에서 GraphQL 을 적용하여 API 를 구현하는 방법에 대한 글이다.
GraphQL 에 대한 자세한 설명은 깔끔하게 생략하겠다. GraphQL 에 대해서 전혀 모른다면 공식 레퍼런스 문서를 읽어보고 오기를 바란다.
스프링 부트환경에서 GraphQL 샘플 코드를 작성한다.
GraphQL 관련 디펜던시를 아래와 같이 추가한다.
com.graphql-java:graphql-spring-boot-starter:3.6.0
com.graphql-java:graphql-java-tools:3.2.0
또한 간단하게 JPA 를 사용할 예정인데, 로컬 테스트 환경이기 때문에 실제 mysql 를 연동하지는 않겠다. 간단하게 hsqldb 를 디펜던시 추가한다.
Coffee 라는 이름의 Entity 를 정의한다.
CoffeeRepository 라는 이름의 Repository 를 정의한다. Repository 에서 사용할 메서드는 아래와 같다.
findAll() - 모든 커피 리스트를 조회한다.
findByCid(String cid) - cid조건으로 특정 커피를 조회한다.
필자가 정의한 findByCid() 라는 메서드와 JpaRepository 에서 제공해주는 findAll() 메서드를 사용할 것이다. findAll() 는 JpaRepository 에 정의되어 있다. 필요하다면 Override 해서 사용해도 상관없다.
GraphQL 을 사용하기 전에 샘플 코드를 먼저 보자. 일단, Service (UseCase) 를 구현하기 전에 기능 명세를 정의하는 인터페이스를 정의한다.
Service 레이어를 구현한다.
CoffeeDetails 인터페이스를 구현하는 Provider 클래스를 작성한다.
이제 엔드포인트가 되는 RestController 를 생성한다.
하지만, 지금 이 글은 GraphQL 에 대해서 설명하는 글이다. GraphQL 을 사용한다면 기존 구현 방식과 조금 다르게 구현이 되는데, Rest 엔드포인트를 각각 별도로 정의할 필요가 없다. 즉, Mapping 메서드를 정의할 필요가 없다는 얘기다. 위에서 작성한 Provider, Service, Controller 레이어의 샘플 코드는 모두 지우고, 다시 작성하겠다.
Entity 와 Repository 는 그대로 사용할 것이다.
일단 기능명세를 정의하는 인터페이스를 생성한다. 아래와 같이 기존에 정의했던 메서드들은 필요가 없다. 그리고 execute 라는 메서드를 정의하였고 query 를 변수로 받는다.
커피 전체 리스트를 조회할 때, 특정 커피를 조회할 때 등 모든 조회 조건을 execute(String query) 라는 메서드 하나로 처리할 수 있다. 그리고 리턴은 graphql.ExecutionResult 로 반환한다. 자, 이제 인터페이스를 사용하는 Service 레이어를 작성한다. 역시 위에서 정의했던 각각의 메서더들은 모두 주석처리해서 없앤다.
Controller 레이어는 아래와 같이 작성한다. @PostMapping 가 적용된 getCoffeeByQuery 라는 메서드가 엔드포인트 끝점이다. localhost:8080/coffees 를 Post 메서드로 Request 하면 된다. 단, RequestBody 로 query 를 전송해야 한다. 중요!!
자, 여기서 가장 중요한 개념은 바로 query 를 전달받아야 한다는 것이다. 중요!
Query 는 프로젝트 내에서 미리 정의가 되어있어야 한다. 즉, 사용자가 요청하는 Query 에 대한 조건을 미리 정의를 해야 한다는 것이다. 그리고, Query 스펙에 대한 가이드를 반드시 따라야 한다.Query 스펙에 대해서는 공식 레퍼런스를 참고하길 바란다.
필자는 아래와 같이 coffees.graphql 이라는 이름의 파일을 Resource 하위에 생성하였다.
allCoffees 는 모든 커피의 리스트를 조회하는 쿼리이다. 아래와 같이 호출해볼 수 있다.
특정 커피를 조회하고 싶다면 아래와 같이 하자.
정리해보면, graphQL 을 사용하면 엔드포인트 끝점을 하나로 할 수 있고, Query 에 조회 조건을 정의해서 원하는 데이터를 조회할 수 있다. 대부분의 개발자들이 Rest API 에 익숙하기 때문에, 해당 개발 방법이 매우 어색할 수 있다. 참고로 이 글을 보고 있는 분들은 아직 필자처럼 응답이 되지 않을 것이다. 내부 로직이 구현이 되지 않았기 때문이다. graphQL 을 처리하는 내부 로직은 천천히 알아보자.
이제 본격적으로, 내부 로직을 구현해보자.
GraphQL 라이브러리에는 DataFetcher 라는 인터페이스에 get 이라는 단 하나의 메서드가 정의되어 있다. 어쨋든, DataFetcher 인터페이스를 구현해야 한다. 두 개의 클래스로 구현하자.
AllCoffeesDataFetcher
CoffeeDataFetcher
Override 한 get 메서드 내부에서는 실제로 데이터를 조회하는 로직이 구현되어야 한다. 필자는, CoffeeRepository 의 findAll() 메서드를 그대로 호출하였다. CoffeeDataFetcher 도 비슷한 방식으로 DataFetcher 인터페이스를 구현한다.
단, 차이점이 있다면 dataFetchingEnvironment 객체에서 getArgument 로 id 값을 가져오고, 해당 값으로 데이터를 조회한다.
CoffeeDetails 를 구현하는 CoffeeProvider 클래스를 수정해보자. 일단, 아래와 같이 Repository 와 DataFetcher 를 의존성 주입을 한다. 또한, coffees.graphql 를 Resource 로 선언한다.
CoffeeDetails 인터페이스에는 execute 단 하나의 메서드만 정의되어 있다. 구현해보자. query 를 매개변수로 받아서 graphQL 객체의 execute 메서드를 실행한다.
테스트 데이터를 입력하고..
아래와 같이 DataFetcher 를 설정해야 하고, graphQL 객체에 주입한다.
필자의 코드 중 일부는 google 에서 구글링을 했다.
샘플 코드는 필자의 github 에서 참고하면 된다.
https://github.com/sieunkr/spring-boot-graphql/tree/master/spring-boot-graphql-h2
스프링부트 환경에서 graphQL 을 사용하는 방법에 대해서 간단하게 정리해봤다. RestAPI 를 대체할 수 있겠지만, 필자가 실무에서 사용해본적이 전혀 없어서 정확히 어떤 장단점이 있는지 판단하기 어렵다. 혹시라도 GraphQL 을 잘 아는 개발자가 있다면 장단점에 대해서 피드백을 남겨주길 바란다. 끝!
OrderBy 관련해서 내용 추가. 설명은 생략
https://github.com/TechPrimers/spring-boot-graphql-query-example
https://github.com/igorkosandyak/spring-boot-graphql
https://github.com/geowarin/graphql-webflux
https://www.baeldung.com/spring-graphq
https://github.com/graphql-java-kickstart/graphql-spring-boot
https://facebook.github.io/graphql/October2016/
https://medium.com/oril/spring-boot-graphql-mongodb-8733002b728a
https://medium.com/@FourwingsY/graphql%EC%9D%84-%EC%98%A4%ED%95%B4%ED%95%98%EB%8B%A4-3216f404134
https://medium.freecodecamp.org/so-whats-this-graphql-thing-i-keep-hearing-about-baf4d36c20cf