스프링 부트와 웹소켓을 활용한 GraphQL 서브스크립션 구현

스프링 부트와 웹소켓을 활용한 GraphQL 서브스크립션 구현

GraphQL

스프링 부트와 웹소켓을 이용하여 GraphQL 서브스크립션을 구현하는 방법에 대해 알아보겠습니다. GraphQL 서브스크립션은 실시간 데이터를 처리해야하는 상황에서 유용하게 사용됩니다. 이 기능은 GraphQL 스키마에서 subscription 타입으로 정의되어 있으며, 클라이언트가 구독을 생성하면 서버가 해당 데이터의 변경 사항을 실시간으로 알려줍니다. 이를 통해 클라이언트는 웹소켓 연결을 통해 서버와 실시간으로 데이터를 주고받을 수 있습니다.

이번 글에서는 GraphQL 서브스크립션의 개념과 사용, 스프링 부트와 웹소켓을 통한 구현 방법, 주의할 점과 문제 해결 방법에 대해 알아보겠습니다.

GraphQL 서브스크립션의 개념과 사용

GraphQL 서브스크립션은 GraphQL 스키마에서 subscription 타입으로 정의됩니다. 이 타입은 일반적인 Query와 Mutation과 같은 구조를 가지고 있지만, 클라이언트는 해당 subscription 타입을 구독하고 있으면, 서버에서 해당 타입에 대한 변경 사항이 발생하면 이를 실시간으로 전달받을 수 있습니다.

예를 들어, 실시간 채팅 애플리케이션을 구현한다고 가정해보겠습니다. 이 애플리케이션에서는 사용자가 메시지를 입력하면 다른 사용자들에게 이를 실시간으로 전달해야합니다. 이를 위해 GraphQL subscription을 사용할 수 있습니다. 사용자가 메시지를 입력하면 서버에서 해당 메시지를 받아들이고, 해당 메시지를 구독하고 있는 모든 클라이언트들에게 메시지를 전달합니다. 이를 통해 모든 클라이언트들은 서버와 실시간으로 데이터를 주고받을 수 있습니다.

GraphQL 서브스크립션은 이외에도 다양한 실시간 데이터 처리에 유용하게 사용될 수 있습니다. 예를 들어, 주식 시장에서 주식 가격이 변경될 때마다 클라이언트들에게 이를 실시간으로 전달하는 기능을 구현할 수 있습니다. 이를 통해 클라이언트들은 시장 상황을 실시간으로 파악하고, 이에 따른 전략을 수립할 수 있습니다.

스프링 부트와 웹소켓을 통한 GraphQL 서브스크립션 구현 방법

스프링 부트와 웹소켓을 이용하여 GraphQL 서브스크립션을 구현하기 위해서는 몇 가지 준비 작업이 필요합니다. 먼저, 스프링 부트 프로젝트를 생성하고, GraphQL 스키마를 작성해야합니다. 이후에는 웹소켓과 GraphQL 서브스크립션을 구현할 코드를 작성하면 됩니다.

프로젝트 생성

먼저, 스프링 부트 프로젝트를 생성합니다. 이를 위해 https://start.spring.io/ 에 접속하여 프로젝트 설정을 진행합니다. 여기에서는 Gradle 기반의 프로젝트를 생성하도록 하겠습니다.

Spring Initializr

의존성 추가

프로젝트를 생성한 후, 의존성을 추가해야합니다. 이를 위해 build.gradle 파일에 다음과 같이 의존성을 추가합니다.

implementation 'com.graphql-java-kickstart:graphql-spring-boot-starter:11.2.0'
implementation 'com.graphql-java-kickstart:graphql-java-tools:11.0.0'
implementation 'com.graphql-java-kickstart:graphql-java-tools-async:11.0.0'
implementation 'org.springframework.boot:spring-boot-starter-websocket'

위 의존성을 추가하면 GraphQL 스키마 정의를 위한 graphql-java-tools와, GraphQL 서버를 구현하기 위한 graphql-spring-boot-starter가 추가됩니다. 또한, 웹소켓을 사용하기 위한 spring-boot-starter-websocket 의존성도 추가됩니다.

GraphQL 스키마 작성

의존성을 추가한 후, GraphQL 스키마를 작성해야합니다. GraphQL 스키마는 .graphql 파일 형태로 작성됩니다. 이번 예시에서는 다음과 같은 스키마를 작성하겠습니다.

type Query {
  hello: String
}

type Subscription {
  greeting: String
}

schema {
  query: Query
  subscription: Subscription
}

위 스키마에서는 Query 타입과 Subscription 타입을 정의합니다. Query 타입은 hello 필드를 가지며, 해당 필드를 호출하면 "world" 문자열을 반환합니다. Subscription 타입은 greeting 필드를 가지며, 해당 필드를 구독하면 "Hello, world!" 문자열을 1초마다 반복해서 반환합니다.

GraphQL 서버 구현

GraphQL 스키마를 작성한 후, 이를 구현하기 위한 코드를 작성해야합니다. 이를 위해 다음과 같이 GraphQLConfig 클래스를 작성합니다.

@Configuration
public class GraphQLConfig {

  @Autowired
  private GreetingPublisher greetingPublisher;

  @Bean
  public GraphQLSchema schema() {
    return new GraphQLSchemaGenerator()
        .withOperationsFromSingleton(greetingResolver(), Greeting.class)
        .withSubscriptionFromPublisher(Greeting.class, greetingPublisher)
        .generate();
  }

  @Bean
  public GreetingResolver greetingResolver() {
    return new GreetingResolver();
  }

  @Bean
  public GreetingPublisher greetingPublisher() {
    return new GreetingPublisher();
  }
}

위 코드에서는 GraphQL 스키마를 생성하는 GraphQLSchemaGenerator를 사용합니다. 이 때, withOperationsFromSingleton 메소드를 사용하여 GreetingResolver 클래스를 singleton으로 등록하고, withSubscriptionFromPublisher 메소드를 사용하여 Greeting 클래스를 구독하고 있는 구독자(GreetingPublisher)를 등록합니다. 이를 통해 greeting 필드를 구독하면 GreetingPublisher에서 반환하는 값을 실시간으로 받을 수 있습니다.


public class GreetingPublisher implements Publisher {

  private final List<Subscriber