diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/api/SseController.java b/backend/src/main/java/io/f1/backend/domain/game/sse/api/SseController.java new file mode 100644 index 00000000..1e01704f --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/api/SseController.java @@ -0,0 +1,23 @@ +package io.f1.backend.domain.game.sse.api; + +import io.f1.backend.domain.game.sse.app.SseService; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@RestController +@RequestMapping("/sse") +@RequiredArgsConstructor +public class SseController { + + private final SseService sseService; + + @GetMapping("/lobby") + public SseEmitter subscribe() { + return sseService.subscribe(); + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java b/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java new file mode 100644 index 00000000..adfd41dc --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java @@ -0,0 +1,31 @@ +package io.f1.backend.domain.game.sse.app; + +import io.f1.backend.domain.game.sse.store.SseEmitterRepository; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.io.IOException; + +@Service +@RequiredArgsConstructor +public class SseService { + + private final SseEmitterRepository emitterRepository; + + public SseEmitter subscribe() { + SseEmitter emitter = new SseEmitter(30_000L); + emitterRepository.save(emitter); + + try { + // emitter 정상 전송확인 메시지 + emitter.send(SseEmitter.event().name("connect").data("connected")); + } catch (IOException e) { + // emitter send() 호출 시 예외 처리 + emitterRepository.remove(emitter); + } + return emitter; + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java b/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java new file mode 100644 index 00000000..4cf61242 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java @@ -0,0 +1,34 @@ +package io.f1.backend.domain.game.sse.store; + +import org.springframework.stereotype.Repository; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +@Repository +public class SseEmitterRepository { + + private final List emitters = new CopyOnWriteArrayList<>(); + + public void save(SseEmitter emitter) { + emitters.add(emitter); + // 연결종료 객체정리 + emitter.onCompletion( + () -> { + emitters.remove(emitter); + }); + emitter.onTimeout(() -> emitters.remove(emitter)); + emitter.onError(error -> emitters.remove(emitter)); + } + + // 연결 종료 객체 정리 + public void remove(SseEmitter emitter) { + emitters.remove(emitter); + } + + // 브로드캐스팅 + public List getAll() { + return emitters; + } +}