알림 구현하기

업데이트:

알림 구현하기

알림을 구현하는 방식

알림을 구현하는 방식에는 여러가지 방법이 있습니다.

1. 폴링 (Polling)

  • 클라이언트가 주기적으로 서버에 새로운 알림이 있는지 확인하는 방식
  • 구현이 간단하지만 불필요한 네트워크 요청이 발생할 수 있음

2. 롱 폴링 (Long Polling)

  • 클라이언트가 서버에 연결을 유지하고, 새 알림이 있을 때만 응답을 받는 방식
  • 실시간성이 개선되지만 서버 리소스 사용량이 증가할 수 있음

3. 웹소켓 (WebSocket)

  • 클라이언트와 서버 간 양방향 통신을 지원하는 프로토콜
  • 실시간 알림에 적합하며 효율적인 리소스 사용이 가능

4. 서버-센트 이벤트 (Server-Sent Events)

  • 서버에서 클라이언트로 단방향 실시간 이벤트를 전송하는 기술
  • HTTP 프로토콜을 사용하여 구현이 비교적 간단함

5. 푸시 알림 (Push Notifications)

  • 모바일 디바이스를 위한 알림 시스템
  • 앱이 백그라운드 상태일 때도 알림 전달 가능


해당 방법 중에 자신의 서버 구성과 요구사항에 맞는 방법을 선택하여 구현하는 것이 좋습니다.

대부분의 경우에는 서버-센트 이벤트 (Server-Sent Events)를 사용하여 알림을 구현하는 것이 적합합니다.

서버-센트 이벤트 (Server-Sent Events)란?

서버-센트 이벤트(SSE)는 실시간 데이터 스트리밍을 위한 효율적인 웹 기술입니다. SSE의 주요 특징과 작동 방식은 다음과 같습니다:

HTML5 표준 기술로, 서버에서 클라이언트로 단방향 실시간 데이터 전송을 가능하게 합니다.

HTTP의 지속적 연결(persistent connections)을 기반으로 동작합니다.

📋 작동 방식

① 클라이언트가 서버와 SSE 연결을 맺습니다.(구독) ② 서버는 변동사항이 발생할 때마다 연결된 모든 클라이언트에게 데이터를 전송합니다. ③ 클라이언트는 수동적으로 데이터를 수신만 합니다.

🗝️ 특징

단방향 통신
서버에서 클라이언트로만 데이터를 전송할 수 있습니다.

텍스트 기반
주로 텍스트 메시지 형태의 데이터를 전송합니다.

자동 재연결
연결이 끊어진 경우 클라이언트가 자동으로 재연결을 시도합니다. (서버에서 retry 필드를 통해 재연결 간격을 설정할 수 있습니다.)

실시간 업데이트
서버의 변경사항을 즉시 클라이언트에 반영할 수 있습니다.

?? 구현 방법(Java Spring Boot + SSE + Redis stream)

1. Gradle 설정

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    
    // Redis
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    
    // jedis
    implementation 'redis.clients:jedis'
    
}

2. Redis 설정


@Configuration
public class RedisConfig {

  @Bean
  public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory connectionFactory) {
    RedisTemplate<String, String> template = new RedisTemplate<>();
    template.setConnectionFactory(connectionFactory);
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new StringRedisSerializer());
    return template;
  }
}


3. 이벤트 생성자 서비스

@Service
public class EventPublisher {

    private final RedisTemplate<String, String> redisTemplate;
    private static final String STREAM_KEY = "sse-events";

    public EventPublisher(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void publishEvent(String event) {
        redisTemplate.opsForStream().add(STREAM_KEY, Collections.singletonMap("data", event));
    }
}


4. 이벤트 컨트롤러

@RestController
public class SseController implements StreamListener<String, MapRecord<String, String, String>> {

    private final RedisTemplate<String, String> redisTemplate;
    private final ExecutorService executorService = Executors.newCachedThreadPool();
    private static final String STREAM_KEY = "sse-events";

    public SseController(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @GetMapping("/sse")
    public SseEmitter handleSse() {
        SseEmitter emitter = new SseEmitter();

        executorService.execute(() -> {
            try {
                redisTemplate.opsForStream().read(
                    StreamListener.of(STREAM_KEY, this),
                    StreamListener.StreamReadOptions.empty().count(1).block(Duration.ofSeconds(30)),
                    StreamListener.ReadOffset.lastConsumed()
                );
            } catch (Exception ex) {
                emitter.completeWithError(ex);
            }
        });

        return emitter;
    }

    @Override
    public void onMessage(MapRecord<String, String, String> message) {
        String event = message.getValue().get("data");
        try {
            emitter.send(SseEmitter.event().data(event));
        } catch (IOException e) {
            emitter.completeWithError(e);
        }
    }
}

✂️ SSE emitter만 사용한 방법과 redis stream을 사용한 방법의 차이점

확장성

  • redis stream을 사용하게 되면 여러 서버 인스턴스간 이벤트 공유가 가능
  • 수평적 확장에 용이함

영속성

  • redis stream을 사용하게 되면 이벤트를 영속적으로 저장할 수 있음
  • sse emitter만 사용시에는 서버 재시작 시 모든 이벤트 손실


결론

. SSE는 HTTP 프로토콜을 기반으로 동작하며, 단방향 실시간 데이터 전송을 지원합니다. Java Spring Boot와 Redis stream을 활용하여 SSE를 구현하는 방법을 살펴보았습니다. 이를 통해 실시간 알림 시스템을 구축할 수 있으며, 확장성과 영속성을 고려하여 구현할 수 있습니다.

참고사이트

https://velog.io/@bsangyong93/SSE%EB%A1%9C-%EC%95%8C%EB%A6%BC-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-feat.Spring-boot

댓글남기기