본문 바로가기
  • 분조장의 개발 블로그
GraphQL

Spring Boot서버에서 GraphQL요청 처리하기

by 무아니 2021. 5. 17.

Spring Boot 서버에서 GraphQL 요청을 처리하는 방법으로 두 가지 방법을 소개하겠습니다.

아래 두 가지 방법은 공통적으로는 아래 처럼 작동합니다. 사용하는 의존성에 따라 구체적인 방법이 조금 다를 뿐입니다.

  • GraphQL의 쿼리문이 Resolver 구현체의 메서드로 매핑되고
  • 요청 및 응답에 사용되는 GraphQL 타입이 java의 dto 객체에  매핑됩니다.

Resolver는 기존 controller의 역할을 수행한다고 보면 되며, endpoint(요청 주소)에 따라 구분하지 않고 query 및 mutation의 요청을 구분하거나 복잡한 요청 을 구현하는 대상에 따라 구분합니다.

 

1. Resolver를 구현하여 처리하기(kickstart 의존성 사용)

Resolver를 구현하여 데이터를 처리하기 위해선 스키마를 정의해야합니다. 스키마란 GraphQL의 쿼리문을 정의하고, 요청 및 응답에 사용되는 데이터를 정의하는 데이터 집합입니다. 아래의 의존성을 등록하여 구현할 수 있습니다.

 

<dependency>
  <groupId>com.graphql-java-kickstart</groupId>
  <artifactId>graphql-spring-boot-starter</artifactId>
  <version>7.1.0</version>
</dependency>

 

Step1. 스키마 작성하기

스키마는 아래와 같은 형식으로 작성할 수 있습니다. 사용자 지정 스칼라 타입으로 Post, Author를 정의하고, query및 mutation 요청을 Query, Mutation 내부에 정의해줍니다. 내부적으로는 스키마의 타입 안 필드 각각마다 data fetcher가 연관돼있어서 data fetcher의 내부 로직에 따라 데이터를 가져올 수 있습니다.

type Post {
  id: ID!            #각각에 data fetcher
  title: String!     #각각에 data fetcher
  text: String!      #각각에 data fetcher
  category: String   # ...
  author: Author!
}

type Author {
  id: ID!
  name: String!
}

type Query {
  getPosts : [Post]
}

type Mutation  {
  savePost(title : String, text: String,  category  : String) : Post
}

 

Step2. Resolver 구현하기

다음 스키마에서 정의한 쿼리 메소드를 아래의 인터페이스를 구현하여 작성해 줄 수 있습니다.

data fetcher가 데이터를 가져오면 아래 GraphQLResolver<T>, GraphQLMutationResolver, GraphQLQueryResolver 인터페이스를 구현해서 요청을 처리하며 각각의 상위 인터페이스는 아래와 같습니다.

GraphQLMutationResolver → GraphQLResolver<Void>
GraphQLResolver<PostResponse>
GraphQLQueryResolver → GraphQLResolver<Void>

Resolver 구현체는 빈으로 등록해야하므로 반드시 @Component같은 어노테이션을 붙여줍니다.

예시)

@Component
@RequiredArgsConstructor
public class RootQueryResolver implements GraphQLQueryResolver {
    
    private final PostRepository postRepository;

    public List<PostResponse> getRecentPosts(int count, int offset) {
        final List<Post> all = postRepository.findAll();
        return PostResponse.from(all);
    }
    
}

 


 

2. annotation을 사용하여 처리하기(spqr 의존성 사용)

스키마를 정의하여 매핑하는 방식이 아닌 어노테이션을 사용하여 매핑을 해주는 방식입니다. 아래의 의존성을 등록해주면 따로 스키마와 resolver를 구현하지 않아도 GraphQL을 사용할 수 있어 편리하다는 장점이 있지만 단점으로 Multipart 를 이용한 파일 업로드 구현이 어렵다는 점이 있습니다.

<dependency>
  <groupId>io.leangen.graphql</groupId>
  <artifactId>graphql-spqr-spring-boot-starter</artifactId>
  <version>0.0.4</version>
</dependency>
링크: https://github.com/leangen/graphql-spqr-spring-boot-starter 

 

Step1. @GraphqlApi로 Resolver 등록

@GraphqlApi 을 달아준 spring application context의 모든 빈은 운영 소스로 간주되며 이 어노테이션은 빈 등록 어노테이션인@Bean, @Component, @Service, @Repository 등과 함께 사용 됩니다.

이렇게 어노테이션을 적용해주면 Resolver 구현체와 같은 기능을 하는 클래스로 인식됩니다.

@Component
@GraphQLApi
private class UserService {
	
    // query, mutation, subscription methods
	...
    
}

 

Step2. 쿼리 메서드 설정하기(쿼리 유형, 인자 설정)

@GraphqlQuery(조회) 또는 @GraphqlMutation(생성,수정,삭제) 를 달아주면 기본적으로는 메소드 이름, 인자 이름으로 GraphQL 쿼리문에 매핑됩니다. 해당 메서드를 다른 쿼리문에 매핑시키려면 @GraphQLQuery, @GraphQLArgument 에서 name 프로퍼티로 이름을 변경할 수 있습니다.

@RequiredArgsConstructor
@GraphQLApi
@Service
public class demoService {

    private final PostRepository postRepository;

    @GraphQLQuery(name = "posts")
    public List<PostDto> RecentPosts(@GraphQLArgument(name="cnt")int count, int offset) {
        final List<Post> all = postRepository.findAll();        
        return PostDto.from(all);
    }

    @GraphQLMutation
    public PostDto writePost(String title, String text, String category) {
        Post post = new Post(title, text, category);
        postRepository.save(post);
        PostDto dto = PostDto.from(post);
        return dto;        
    }
}

댓글