From 3bb764ca5f9723b7630e974ab6cf92c512046c6d Mon Sep 17 00:00:00 2001 From: limkanghyun Date: Fri, 11 Jul 2025 15:00:49 +0900 Subject: [PATCH 1/5] =?UTF-8?q?:sparkles:=20feat:=20SSE=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/sse/SseController.java | 21 ++++++++++++ .../domain/game/sse/SseEmitterRepository.java | 32 +++++++++++++++++++ .../backend/domain/game/sse/SseService.java | 29 +++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java create mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java create mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java new file mode 100644 index 00000000..018ab05d --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java @@ -0,0 +1,21 @@ +package io.f1.backend.domain.game.sse; + +import java.io.IOException; +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/SseEmitterRepository.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java new file mode 100644 index 00000000..58b6353a --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java @@ -0,0 +1,32 @@ +package io.f1.backend.domain.game.sse; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.springframework.stereotype.Repository; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@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; + } +} diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java new file mode 100644 index 00000000..314d7fbd --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java @@ -0,0 +1,29 @@ +package io.f1.backend.domain.game.sse; + +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@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; + } +} From ba9e7bc1832edb33fc7561d65e316dd8c0b3872b Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Fri, 11 Jul 2025 06:04:03 +0000 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/f1/backend/domain/game/sse/SseController.java | 2 +- .../domain/game/sse/SseEmitterRepository.java | 12 +++++++----- .../io/f1/backend/domain/game/sse/SseService.java | 8 ++++---- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java index 018ab05d..544b715d 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java @@ -1,7 +1,7 @@ package io.f1.backend.domain.game.sse; -import java.io.IOException; import lombok.RequiredArgsConstructor; + import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java index 58b6353a..156af77c 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java @@ -1,10 +1,11 @@ package io.f1.backend.domain.game.sse; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; 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 { @@ -13,9 +14,10 @@ public class SseEmitterRepository { public void save(SseEmitter emitter) { emitters.add(emitter); // 연결종료 객체정리 - emitter.onCompletion(() -> { - emitters.remove(emitter); - }); + emitter.onCompletion( + () -> { + emitters.remove(emitter); + }); emitter.onTimeout(() -> emitters.remove(emitter)); emitter.onError(error -> emitters.remove(emitter)); } diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java index 314d7fbd..fd1d9e4b 100644 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java @@ -1,10 +1,12 @@ package io.f1.backend.domain.game.sse; -import java.io.IOException; 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 { @@ -17,9 +19,7 @@ public SseEmitter subscribe() { try { // emitter 정상 전송확인 메시지 - emitter.send(SseEmitter.event() - .name("connect") - .data("connected")); + emitter.send(SseEmitter.event().name("connect").data("connected")); } catch (IOException e) { // emitter send() 호출 시 예외 처리 emitterRepository.remove(emitter); From 2fbe642e079f19952901e57922ae65b9d31d2120 Mon Sep 17 00:00:00 2001 From: limkanghyun Date: Fri, 11 Jul 2025 15:00:49 +0900 Subject: [PATCH 3/5] =?UTF-8?q?:truck:=20rename:=20SSE=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=ED=8C=8C=EC=9D=BC=20=ED=8C=A8=ED=82=A4=EC=A7=80=20?= =?UTF-8?q?=EA=B5=AC=EB=B6=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/sse/api/SseController.java | 21 ++++++++++++ .../domain/game/sse/app/SseService.java | 30 +++++++++++++++++ .../game/sse/store/SseEmitterRepository.java | 32 +++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/api/SseController.java create mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java create mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java 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..1ab78adc --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/api/SseController.java @@ -0,0 +1,21 @@ +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..b1cb14f0 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/app/SseService.java @@ -0,0 +1,30 @@ +package io.f1.backend.domain.game.sse.app; + +import io.f1.backend.domain.game.sse.store.SseEmitterRepository; +import java.io.IOException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@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..5f83f7f1 --- /dev/null +++ b/backend/src/main/java/io/f1/backend/domain/game/sse/store/SseEmitterRepository.java @@ -0,0 +1,32 @@ +package io.f1.backend.domain.game.sse.store; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import org.springframework.stereotype.Repository; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +@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; + } +} From a47c1d506790178ea7483d0a2a0acca9da334b39 Mon Sep 17 00:00:00 2001 From: github-actions <> Date: Fri, 11 Jul 2025 06:55:26 +0000 Subject: [PATCH 4/5] =?UTF-8?q?chore:=20Java=20=EC=8A=A4=ED=83=80=EC=9D=BC?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/game/sse/api/SseController.java | 2 ++ .../f1/backend/domain/game/sse/app/SseService.java | 9 +++++---- .../domain/game/sse/store/SseEmitterRepository.java | 12 +++++++----- 3 files changed, 14 insertions(+), 9 deletions(-) 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 index 1ab78adc..1e01704f 100644 --- 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 @@ -1,7 +1,9 @@ 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; 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 index b1cb14f0..adfd41dc 100644 --- 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 @@ -1,11 +1,14 @@ package io.f1.backend.domain.game.sse.app; import io.f1.backend.domain.game.sse.store.SseEmitterRepository; -import java.io.IOException; + 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 { @@ -18,9 +21,7 @@ public SseEmitter subscribe() { try { // emitter 정상 전송확인 메시지 - emitter.send(SseEmitter.event() - .name("connect") - .data("connected")); + emitter.send(SseEmitter.event().name("connect").data("connected")); } catch (IOException e) { // emitter send() 호출 시 예외 처리 emitterRepository.remove(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 index 5f83f7f1..4cf61242 100644 --- 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 @@ -1,10 +1,11 @@ package io.f1.backend.domain.game.sse.store; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; 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 { @@ -13,9 +14,10 @@ public class SseEmitterRepository { public void save(SseEmitter emitter) { emitters.add(emitter); // 연결종료 객체정리 - emitter.onCompletion(() -> { - emitters.remove(emitter); - }); + emitter.onCompletion( + () -> { + emitters.remove(emitter); + }); emitter.onTimeout(() -> emitters.remove(emitter)); emitter.onError(error -> emitters.remove(emitter)); } From 5aa3f94a37f527275c61043dbda55f2f8d762c32 Mon Sep 17 00:00:00 2001 From: limkanghyun Date: Fri, 11 Jul 2025 16:19:12 +0900 Subject: [PATCH 5/5] =?UTF-8?q?:wastebasket:=20remove:=20SSE=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=A4=91=EB=B3=B5=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/game/sse/SseController.java | 21 ------------ .../domain/game/sse/SseEmitterRepository.java | 34 ------------------- .../backend/domain/game/sse/SseService.java | 29 ---------------- 3 files changed, 84 deletions(-) delete mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java delete mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java delete mode 100644 backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java deleted file mode 100644 index 544b715d..00000000 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/SseController.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.f1.backend.domain.game.sse; - -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/SseEmitterRepository.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java deleted file mode 100644 index 156af77c..00000000 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/SseEmitterRepository.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.f1.backend.domain.game.sse; - -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; - } -} diff --git a/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java b/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java deleted file mode 100644 index fd1d9e4b..00000000 --- a/backend/src/main/java/io/f1/backend/domain/game/sse/SseService.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.f1.backend.domain.game.sse; - -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; - } -}