GraphQL APIs with Spring Boot · 강의

실시간 업데이트 구현

Spring Boot에서 구독 리졸버를 개발해 이벤트를 발행하고 클라이언트에 실시간 데이터를 전송합니다.

레슨 2/411개 단계

실시간 업데이트 구현은(는) CoddyKit의 무료 GraphQL APIs with Spring Boot 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 GraphQL APIs with Spring Boot 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. GraphQL APIs with Spring Boot 강의에는 총 4개의 강의가 포함되어 있습니다.

이 강의의 일부는 아직 번역되지 않았으며 영어로 표시됩니다.

Subscription Resolvers Explained

Subscriptions deliver real-time updates. Unlike queries that return data once, subscription resolvers return a stream of data that continuously pushes updates to clients.

We'll learn how to implement these streams in a Spring Boot GraphQL application.

Embracing Reactor Flux

Spring GraphQL leverages Project Reactor's Flux to handle subscriptions. A Flux represents an asynchronous, non-blocking stream of 0 to N items.

  • It's ideal for continuous data delivery.
  • You can emit multiple values over time.

Schema for Real-time Updates

First, we define our subscription in the GraphQL Schema Definition Language (SDL). This example shows a subscription for new messages:

type Subscription {
  messageAdded(channelId: ID!): Message
}

type Message {
  id: ID!
  text: String!
  channelId: ID!
  timestamp: String!
}

Creating a Subscription Resolver

In Spring Boot, a subscription resolver is a method annotated with @SubscriptionMapping. It must return a Flux of the desired type.

We'll use a Sinks.Many to manage and publish events into this stream.

import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
import org.springframework.stereotype.Controller;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

@Controller
public class MessageSubscriptionController {

    private final Sinks.Many<Message> messageSink = 
        Sinks.many().multicast().onBackpressureBuffer();

    @SubscriptionMapping
    public Flux<Message> messageAdded() {
        return messageSink.asFlux();
    }
}

Publishing Events with Sinks.Many

The Sinks.Many instance acts as our event publisher. When an event occurs (e.g., a new message is created), we use its tryEmitNext() method to send data into the stream.

This data then flows through the Flux to all connected GraphQL subscribers.

public class MessageService {

    private final Sinks.Many<Message> messageSink;

    public MessageService(Sinks.Many<Message> messageSink) {
        this.messageSink = messageSink;
    }

    public Message createMessage(String text, String channelId) {
        // ... save message to DB ...
        Message newMessage = new Message("1", text, channelId, "now");
        messageSink.tryEmitNext(newMessage); // Publish the new message
        return newMessage;
    }
}

Full Runnable Example Setup

Let's build a complete, runnable Spring Boot application. We'll define a Message record and a MessagePublisher component to manage our Sinks.Many.

record Message(String id, String text, String channelId, String timestamp) {}

import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

@Component
public class MessagePublisher {
    private final Sinks.Many<Message> messageSink = 
        Sinks.many().multicast().onBackpressureBuffer();

    public Flux<Message> getMessageStream() {
        return messageSink.asFlux();
    }

    public void publishMessage(Message message) {
        messageSink.tryEmitNext(message);
    }
}

Main Application & Resolver

Now, we connect our MessagePublisher to the @SubscriptionMapping resolver. The Main class simulates sending a message after a delay.

Run this example to see the server-side publishing in action!

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
import org.springframework.stereotype.Controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.time.Duration;

@SpringBootApplication
public class Main implements CommandLineRunner {

    @Autowired
    private MessagePublisher messagePublisher;

    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        // Simulate sending a message after 2 seconds
        Mono.delay(Duration.ofSeconds(2))
            .subscribe(l -> {
                Message msg = new Message("2", "Hello from Spring!", "general", "now");
                messagePublisher.publishMessage(msg);
                System.out.println("Published: " + msg);
            });
    }
}

@Controller
class MessageSubscriptionController {
    private final MessagePublisher messagePublisher;

    public MessageSubscriptionController(MessagePublisher messagePublisher) {
        this.messagePublisher = messagePublisher;
    }

    @SubscriptionMapping
    public Flux<Message> messageAdded() {
        return messagePublisher.getMessageStream();
    }
}

record Message(String id, String text, String channelId, String timestamp) {}

Filtering Subscription Events

Clients often need updates specific to certain criteria. We can filter the Flux based on arguments passed to the subscription.

Here, clients only receive messages for a specified channelId.

import org.springframework.graphql.data.method.annotation.Argument;
// ... other imports ...

@Controller
class MessageSubscriptionController {
    private final MessagePublisher messagePublisher;

    public MessageSubscriptionController(MessagePublisher messagePublisher) {
        this.messagePublisher = messagePublisher;
    }

    @SubscriptionMapping
    public Flux<Message> messageAdded(@Argument String channelId) {
        return messagePublisher.getMessageStream()
            .filter(msg -> msg.channelId().equals(channelId));
    }
}

Decoupling Event Publishing

For better architecture, it's a good practice to decouple event publishing from your core business logic.

  • This keeps your services clean and focused.
  • It allows multiple independent subscribers to react to the same event without tight coupling.
  • Consider using Spring's ApplicationEventPublisher or a dedicated event bus for this.

Subscription Resolver Check

You've learned how to implement subscription resolvers in Spring Boot. Let's test your understanding!

Recap: Real-time Updates

You've successfully learned how to implement real-time updates using GraphQL subscriptions in Spring Boot!

  • Subscription resolvers return a Flux.
  • Sinks.Many is used to publish events into the Flux.
  • You can filter streams based on subscription arguments.
  • Decoupling publishing logic improves maintainability.
무료로 시작

AI 튜터와 함께 GraphQL APIs with Spring Boot을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
12
레슨
48

자주 묻는 질문

“실시간 업데이트 구현” 강의는 무료인가요?

네 — “실시간 업데이트 구현” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 GraphQL APIs with Spring Boot 강의 전체를 잠금 해제할 수 있습니다. GraphQL APIs with Spring Boot 강의에는 총 4개의 강의가 포함되어 있습니다.

“실시간 업데이트 구현”에서 뭘 배우나요?

Spring Boot에서 구독 리졸버를 개발해 이벤트를 발행하고 클라이언트에 실시간 데이터를 전송합니다. 브라우저에서 직접 실행하는 실습 코드로 GraphQL APIs with Spring Boot을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

GraphQL APIs with Spring Boot을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 GraphQL APIs with Spring Boot은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.

“실시간 업데이트 구현” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 GraphQL APIs with Spring Boot 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 GraphQL APIs with Spring Boot 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. GraphQL 구독 이해하기
  2. 실시간 업데이트 구현
  3. Spring에 WebSockets 통합하기
  4. 구독 필터링과 확장
← GraphQL APIs with Spring Boot(으)로 돌아가기