저희 팀에서 기획한 '오늘의빵' 서비스에서는 백엔드와 프론트엔드 통신에 GraphQL을 사용합니다. NestJSReact Native에서 GraphQL을 사용해본 경험은 있지만, Spring bootFlutter 환경에서는 GraphQL을 사용해본 경험이 없어 실제 프로젝트를 실행하기에 앞서 각 환경에서 GraphQL을 사용하는 방법에 대해 익혀보려고 합니다.

먼저 Spring boot 환경에서 GraphQL을 사용하는 방법에 대해 알아봤습니다. Spring boot에서 GraphQL을 사용할때 graphql-java-kickstart을 사용하는 방법도 있지만, Spring for GraphQL v1.0.x이 릴리즈 되었기 때문에 spring-boot-starter-graphql를 사용해 보기로 결정했습니다.

저는 spring-boot-starter-graphql을 연습해보기 위해 간단하게 CRUD로 제작할 수 있는 Todo API를 제작해보았습니다. 그러기 위해 먼저 제공하고자 하는 기능들을 GraphQL Schema로 표현하였습니다.

### Type ###  
type Todo {  
    id: String!,  
    title: String!,  
    content: String,  
    status: Boolean!,  
    createdAt: String!,  
    updatedAt: String!  
}  
  
input CreateTodoProps {  
    title: String!,  
    content: String  
}  
  
input UpdateTodoProps {  
    id: String!,  
    title: String,  
    content: String,  
}  
  
### Query ###  
type Query {  
    todo(todoId: String!): Todo!, # Todo 아이템 하나 조회하기
    todos: [Todo!]!               # Todo 전체 아이템 리스트 조회하기
}  
  
### Mutation ###  
type Mutation {  
    createTodo(props: CreateTodoProps): Todo!, # Todo 생성하기
    updateTodo(props: UpdateTodoProps): Todo!, # Todo 내용 수정하기
    updateTodoStatus(todoId: String!): Todo!   # Todo 완료 상태 수정하기
    deleteTodo(todoId: String!): Boolean!,     # Todo 삭제하기
}

아직까지는 spring-boot-starter-graphql을 사용하는 글이 많지 않아서 대부분 공식문서를 참고하였습니다. 가장 먼저 build.gradle에 의존성을 추가해주었습니다. spring-boot-starter-graphql을 추가해주면서, Spring for GraphQL이 제공해주는 GraphQL 관련 기능들을 사용할 수 있습니다.

// build.gradle

implementation 'org.springframework.boot:spring-boot-starter-graphql'

다음으로 src/main/resouces 아래에 앞서 정의하였던 GraphQL Schematodo.graphqls로 저장해주었습니다. 그렇다면 Spring boot에서 GraphQL을 사용하기 위한 기본 준비가 끝났습니다.

GraphQL 통신의 endpoint의 역할을 할 Controller를 정의하였습니다. NestJS와 graphql-java-kickstart에서는 Resolver로 표현하는데, spring-boot-starter-graphql의 문서에서는 Controller로 표현하여 따라서 Controller로 생성하였습니다.

spring-boot-starter-graphql에서 QueryMutation을 정의하는 것은 생각보다 간단했습니다. Query에는 메소드에 @QueryMapping을 붙여주고, Mutation@MutationMapping을 붙여주는 것만으로도 todo.graphqls에서 정의한 기능들을 Spring boot에서 구현할 수 있었습니다.

@Controller  
@RequiredArgsConstructor  
public class TodoController {  
  
   private final TodoServiceImpl todoService;  
  
   @QueryMapping  
   public Todo todo(@Argument String todoId) {  
      return todoService.getOne(todoId);  
   }  
  
   @QueryMapping  
   public List<Todo> todos() {  
      return todoService.getAll();  
   }  
  
   @MutationMapping  
   public Todo createTodo(@Argument CreateTodoRequest props) {  
      return todoService.create(props);  
   }  
  
   @MutationMapping  
   public Todo updateTodo(@Argument UpdateTodoRequest props) {  
      return todoService.update(props);  
   }  
  
   @MutationMapping  
   public Todo updateTodoStatus(@Argument String todoId) {  
      return todoService.updateStatus(todoId);  
   }  
  
   @MutationMapping  
   public Boolean deleteTodo(@Argument String todoId) {  
      return todoService.delete(todoId);  
   }  
}

다만 주의할 점은 메소드의 이름이 GraphQL Schema에서 정의한 이름과 일치해야한다는 것입니다. 하지만 이름만 신경써준다면, NestJS 환경에서 구현하였던 것처럼 쉽게 구현할 수 있었습니다.

이번 예제에서는 GraphQL의 아주 간단한 기능들만 사용하였지만, 유용한 기능들과 해결해야할 문제들이 더 있습니다. NestJS에서는 @ResolveField()로 사용되던 기능이 Spring boot에는 @SchemaMapping으로 지원하고 있습니다. 또한 Dataloader 또한 동일하게 지원되고 있습니다.

다음에는 @SchemaMapping을 사용하여 특정 필드의 로직을 분리하는 방법과, 이때 발생할 수 있는 N+1의 문제를 해결하기 위해 Dataloader를 사용하는 방법을 익혀보려고 합니다. 😄

예제 링크 - Github

0개의 댓글