From eb9f0df8cc903afa91ca542084ace64ac97c4866 Mon Sep 17 00:00:00 2001 From: eun-byeol Date: Wed, 18 Sep 2024 17:27:41 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EC=9B=B9=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=ED=95=B8=EB=93=A4=EB=9F=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ody/common/exception/OdyException.java | 2 +- .../exception/OdyWebSocketException.java | 8 ++++ .../com/ody/eta/config/WebSocketConfig.java | 12 ++++++ .../ody/eta/config/WebSocketErrorHandler.java | 43 +++++++++++++++++++ .../ody/eta/config/WebSocketPreHandler.java | 43 +++++++++++++++++++ .../com/ody/eta/domain/WebSocketEndpoint.java | 32 ++++++++++++++ 6 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 backend/src/main/java/com/ody/common/exception/OdyWebSocketException.java create mode 100644 backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java create mode 100644 backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java create mode 100644 backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java diff --git a/backend/src/main/java/com/ody/common/exception/OdyException.java b/backend/src/main/java/com/ody/common/exception/OdyException.java index 763477509..868e1eede 100644 --- a/backend/src/main/java/com/ody/common/exception/OdyException.java +++ b/backend/src/main/java/com/ody/common/exception/OdyException.java @@ -8,7 +8,7 @@ public abstract class OdyException extends RuntimeException { private final HttpStatus httpStatus; - public OdyException(String message, HttpStatus httpStatus) { + protected OdyException(String message, HttpStatus httpStatus) { super(message); this.httpStatus = httpStatus; } diff --git a/backend/src/main/java/com/ody/common/exception/OdyWebSocketException.java b/backend/src/main/java/com/ody/common/exception/OdyWebSocketException.java new file mode 100644 index 000000000..35a183e51 --- /dev/null +++ b/backend/src/main/java/com/ody/common/exception/OdyWebSocketException.java @@ -0,0 +1,8 @@ +package com.ody.common.exception; + +public class OdyWebSocketException extends RuntimeException { + + public OdyWebSocketException(String message) { + super(message); + } +} diff --git a/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java b/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java index a3e18bf83..0dc5d8ed7 100644 --- a/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java +++ b/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java @@ -1,19 +1,26 @@ package com.ody.eta.config; +import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @Configuration +@RequiredArgsConstructor @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { + private final WebSocketPreHandler webSocketPreHandler; + private final WebSocketErrorHandler webSocketErrorHandler; + @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/connect") .setAllowedOrigins("*"); + registry.setErrorHandler(webSocketErrorHandler); } @Override @@ -21,4 +28,9 @@ public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/topic"); registry.setApplicationDestinationPrefixes("/publish"); } + + @Override + public void configureClientInboundChannel(ChannelRegistration registration) { + registration.interceptors(webSocketPreHandler); + } } diff --git a/backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java b/backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java new file mode 100644 index 000000000..c13f6920a --- /dev/null +++ b/backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java @@ -0,0 +1,43 @@ +package com.ody.eta.config; + +import com.ody.common.exception.OdyException; +import com.ody.common.exception.OdyWebSocketException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.Message; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.web.socket.messaging.StompSubProtocolErrorHandler; + +@Slf4j +@Configuration +public class WebSocketErrorHandler extends StompSubProtocolErrorHandler { + + @Override + public Message handleClientMessageProcessingError(Message clientMessage, Throwable ex) { + Throwable cause = ex.getCause(); + String message = cause.getMessage(); + + if (cause instanceof OdyWebSocketException) { + log.warn("message: {}", message); + return createErrorMessage(message); + } + + if (cause instanceof OdyException) { + log.warn("message: {}", message); + return createErrorMessage(message); + } + + if (cause instanceof Exception) { + log.error("exception: {}", cause); + return createErrorMessage("서버 에러"); + } + return super.handleClientMessageProcessingError(clientMessage, ex); + } + + private Message createErrorMessage(String exceptionMessage) { + StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.ERROR); + return MessageBuilder.createMessage(exceptionMessage.getBytes(), accessor.getMessageHeaders()); + } +} diff --git a/backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java b/backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java new file mode 100644 index 000000000..dcceadf0c --- /dev/null +++ b/backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java @@ -0,0 +1,43 @@ +package com.ody.eta.config; + +import com.ody.common.exception.OdyWebSocketException; +import com.ody.eta.domain.WebSocketEndpoint; +import org.springframework.context.annotation.Configuration; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.simp.stomp.StompCommand; +import org.springframework.messaging.simp.stomp.StompHeaderAccessor; +import org.springframework.messaging.support.ChannelInterceptor; + +@Configuration +public class WebSocketPreHandler implements ChannelInterceptor { + + @Override + public Message preSend(Message message, MessageChannel channel) { + StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); + StompCommand command = accessor.getCommand(); + String destination = accessor.getDestination(); + + if (StompCommand.SUBSCRIBE.equals(command)) { + validateSubscribeEndpoint(destination); + } + if (StompCommand.SEND.equals(command)) { + validateSendEndpoint(destination); + } + return message; + } + + private void validateSubscribeEndpoint(String destination) { + if (WebSocketEndpoint.getSubscribeEndpoints().contains(destination)) { + return; + } + throw new OdyWebSocketException(destination + "은 유효하지 않은 subscribe endpoint 입니다."); + } + + private void validateSendEndpoint(String destination) { + if (WebSocketEndpoint.getSendEndpoints().contains(destination)) { + return; + } + throw new OdyWebSocketException(destination + "은 유효하지 않은 send endpoint 입니다."); + } +} diff --git a/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java b/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java new file mode 100644 index 000000000..09a2d843e --- /dev/null +++ b/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java @@ -0,0 +1,32 @@ +package com.ody.eta.domain; + +import java.util.Arrays; +import java.util.List; + +public enum WebSocketEndpoint { + + OPEN("/publish/open/"), + ETA_UPDATE("/publish/etas/"), + ETAS("/topic/etas/"), + LOCATION("/topic/coordinates/"), + DISCONNECT( "/topic/disconnect/"), + ; + + private final String endpoint; + + WebSocketEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public static List getSubscribeEndpoints() { + return Arrays.stream(values()).map(value -> value.endpoint) + .filter(endpoint -> endpoint.startsWith("/topic/")) + .toList(); + } + + public static List getSendEndpoints() { + return Arrays.stream(values()).map(value -> value.endpoint) + .filter(endpoint -> endpoint.startsWith("/publish/")) + .toList(); + } +} From f6fe3e22029521d991dc1c2f8dd38294ff0d1c6c Mon Sep 17 00:00:00 2001 From: eun-byeol Date: Wed, 18 Sep 2024 17:35:16 +0900 Subject: [PATCH 2/4] =?UTF-8?q?test:=20=EC=9B=B9=EC=86=8C=EC=BC=93=20?= =?UTF-8?q?=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ody/eta/domain/WebSocketEndpoint.java | 4 +++ .../ody/eta/domain/WebSocketEndpointTest.java | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java diff --git a/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java b/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java index 09a2d843e..e2422acd9 100644 --- a/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java +++ b/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java @@ -29,4 +29,8 @@ public static List getSendEndpoints() { .filter(endpoint -> endpoint.startsWith("/publish/")) .toList(); } + + public String getEndpoint() { + return endpoint; + } } diff --git a/backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java b/backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java new file mode 100644 index 000000000..c5a27b7de --- /dev/null +++ b/backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java @@ -0,0 +1,35 @@ +package com.ody.eta.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class WebSocketEndpointTest { + + @DisplayName("subscribe endpoint 리스트를 반환한다.") + @Test + void getSubscribeEndpoints() { + List actual = WebSocketEndpoint.getSubscribeEndpoints(); + List expected = List.of( + WebSocketEndpoint.ETAS.getEndpoint(), + WebSocketEndpoint.LOCATION.getEndpoint(), + WebSocketEndpoint.DISCONNECT.getEndpoint() + ); + + assertThat(actual).containsExactlyInAnyOrderElementsOf(expected); + } + + @DisplayName("send endpoint 리스트를 반환한다.") + @Test + void getSendEndpoint() { + List actual = WebSocketEndpoint.getSendEndpoints(); + List expected = List.of( + WebSocketEndpoint.OPEN.getEndpoint(), + WebSocketEndpoint.ETA_UPDATE.getEndpoint() + ); + + assertThat(actual).containsExactlyInAnyOrderElementsOf(expected); + } +} From 49b8a57eee058d982cd1aa278910e4a1b39a8209 Mon Sep 17 00:00:00 2001 From: eun-byeol Date: Fri, 11 Oct 2024 00:19:51 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20MessageExceptionHandler=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ody/eta/config/WebSocketConfig.java | 2 +- .../eta/controller/EtaSocketController.java | 19 ++++++++ .../com/ody/eta/domain/WebSocketEndpoint.java | 17 +++---- .../ody/common/websocket/BaseStompTest.java | 3 +- .../common/websocket/MessageFrameHandler.java | 3 +- .../controller/EtaSocketControllerTest.java | 44 ++++++++++++++++++- .../ody/eta/domain/WebSocketEndpointTest.java | 3 +- 7 files changed, 74 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java b/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java index 0dc5d8ed7..fd5273b20 100644 --- a/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java +++ b/backend/src/main/java/com/ody/eta/config/WebSocketConfig.java @@ -25,7 +25,7 @@ public void registerStompEndpoints(StompEndpointRegistry registry) { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.enableSimpleBroker("/topic"); + registry.enableSimpleBroker("/topic", "/queue"); registry.setApplicationDestinationPrefixes("/publish"); } diff --git a/backend/src/main/java/com/ody/eta/controller/EtaSocketController.java b/backend/src/main/java/com/ody/eta/controller/EtaSocketController.java index 312114af5..7c330e747 100644 --- a/backend/src/main/java/com/ody/eta/controller/EtaSocketController.java +++ b/backend/src/main/java/com/ody/eta/controller/EtaSocketController.java @@ -1,5 +1,6 @@ package com.ody.eta.controller; +import com.ody.common.exception.OdyException; import com.ody.eta.annotation.WebSocketAuthMember; import com.ody.eta.dto.request.MateEtaRequest; import com.ody.eta.service.EtaSocketService; @@ -7,10 +8,14 @@ import com.ody.member.domain.Member; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; import org.springframework.messaging.handler.annotation.DestinationVariable; +import org.springframework.messaging.handler.annotation.MessageExceptionHandler; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.Payload; import org.springframework.messaging.handler.annotation.SendTo; +import org.springframework.messaging.simp.annotation.SendToUser; import org.springframework.web.bind.annotation.RestController; @Slf4j @@ -36,4 +41,18 @@ public MateEtaResponsesV2 etaUpdate( log.info("--- etaUpdate 호출 ! - {}, {}, {}", meetingId, member, etaRequest); return etaSocketService.etaUpdate(meetingId, member, etaRequest); } + + @MessageExceptionHandler + @SendToUser("/queue/errors") + public ProblemDetail handleOdyException(OdyException exception) { + log.warn("exception: ", exception); + return ProblemDetail.forStatusAndDetail(exception.getHttpStatus(), exception.getMessage()); + } + + @MessageExceptionHandler + @SendToUser("/queue/errors") + public ProblemDetail handleException(Exception exception) { + log.error("exception: ", exception); + return ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "서버 에러"); + } } diff --git a/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java b/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java index e2422acd9..894cc43a3 100644 --- a/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java +++ b/backend/src/main/java/com/ody/eta/domain/WebSocketEndpoint.java @@ -2,25 +2,26 @@ import java.util.Arrays; import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +@Getter +@RequiredArgsConstructor public enum WebSocketEndpoint { OPEN("/publish/open/"), ETA_UPDATE("/publish/etas/"), ETAS("/topic/etas/"), LOCATION("/topic/coordinates/"), - DISCONNECT( "/topic/disconnect/"), + DISCONNECT("/topic/disconnect/"), + ERROR("/user/queue/errors"), ; private final String endpoint; - WebSocketEndpoint(String endpoint) { - this.endpoint = endpoint; - } - public static List getSubscribeEndpoints() { return Arrays.stream(values()).map(value -> value.endpoint) - .filter(endpoint -> endpoint.startsWith("/topic/")) + .filter(endpoint -> endpoint.startsWith("/topic/") || endpoint.contains("/queue/")) .toList(); } @@ -29,8 +30,4 @@ public static List getSendEndpoints() { .filter(endpoint -> endpoint.startsWith("/publish/")) .toList(); } - - public String getEndpoint() { - return endpoint; - } } diff --git a/backend/src/test/java/com/ody/common/websocket/BaseStompTest.java b/backend/src/test/java/com/ody/common/websocket/BaseStompTest.java index eb86edb23..ab15e254d 100644 --- a/backend/src/test/java/com/ody/common/websocket/BaseStompTest.java +++ b/backend/src/test/java/com/ody/common/websocket/BaseStompTest.java @@ -60,8 +60,7 @@ public BaseStompTest() { @BeforeEach public void connect() throws ExecutionException, InterruptedException, TimeoutException { this.stompSession = this.websocketClient - .connect(url + port + ENDPOINT, new StompSessionHandlerAdapter() { - }) + .connect(url + port + ENDPOINT, new StompSessionHandlerAdapter() {}) .get(3, TimeUnit.SECONDS); } diff --git a/backend/src/test/java/com/ody/common/websocket/MessageFrameHandler.java b/backend/src/test/java/com/ody/common/websocket/MessageFrameHandler.java index 947deb0ff..ad9a3f5fe 100644 --- a/backend/src/test/java/com/ody/common/websocket/MessageFrameHandler.java +++ b/backend/src/test/java/com/ody/common/websocket/MessageFrameHandler.java @@ -24,8 +24,7 @@ public Type getPayloadType(StompHeaders headers) { @Override public void handleFrame(StompHeaders headers, Object payload) { - if (completableFuture.complete((T) payload)) { - } + completableFuture.complete((T) payload); } public CompletableFuture getCompletableFuture() { diff --git a/backend/src/test/java/com/ody/eta/controller/EtaSocketControllerTest.java b/backend/src/test/java/com/ody/eta/controller/EtaSocketControllerTest.java index 271515980..da516575c 100644 --- a/backend/src/test/java/com/ody/eta/controller/EtaSocketControllerTest.java +++ b/backend/src/test/java/com/ody/eta/controller/EtaSocketControllerTest.java @@ -8,10 +8,10 @@ import com.ody.auth.service.AuthService; import com.ody.common.Fixture; +import com.ody.common.exception.OdyNotFoundException; import com.ody.common.websocket.BaseStompTest; import com.ody.common.websocket.MessageFrameHandler; import com.ody.eta.dto.request.MateEtaRequest; -import com.ody.eta.service.SocketMessageSender; import com.ody.mate.service.MateService; import com.ody.meeting.dto.response.MateEtaResponsesV2; import com.ody.meeting.service.MeetingService; @@ -25,13 +25,19 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.stream.Stream; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.Mockito; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ProblemDetail; class EtaSocketControllerTest extends BaseStompTest { @@ -147,6 +153,42 @@ void subscribe() throws ExecutionException, InterruptedException, TimeoutExcepti assertThat(mateEtaResponsesV2.requesterMateId()).isEqualTo(response.requesterMateId()); } + @DisplayName("controller 진입 후 에러 발생 시 /user/queue/errors 로 에러 메시지를 받는다") + @ParameterizedTest + @MethodSource("provideExceptionAndExpectedProblemDetail") + void exceptionHandling(Throwable exception, ProblemDetail expectedErrorMessage) + throws ExecutionException, InterruptedException, TimeoutException { + MessageFrameHandler handler = new MessageFrameHandler<>(ProblemDetail.class); + + Mockito.doThrow(exception).when(mateService).findAllMateEtas(any(), any(), any()); + + stompSession.subscribe("/user/queue/errors", handler); // 에러 수신용 구독 + Thread.sleep(3000); + + Mockito.when(timeCache.get(anyLong())) + .thenReturn(LocalDateTime.now().plusMinutes(10L)) // meeting 시간 (10분 뒤) + .thenReturn(LocalDateTime.now()); //trigger 당긴지 0초 > 새로 예약 x + + sendEtaRequest(); + + ProblemDetail actualErrorMessage = handler.getCompletableFuture().get(10, TimeUnit.SECONDS); + + assertThat(actualErrorMessage).isEqualTo(expectedErrorMessage); + } + + public static Stream provideExceptionAndExpectedProblemDetail() { + return Stream.of( + Arguments.of( + new OdyNotFoundException("not found"), + ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, "not found") + ), + Arguments.of( + new RuntimeException(), + ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "서버 에러") + ) + ); + } + private void sendEtaRequest() throws InterruptedException { MateEtaRequest request = new MateEtaRequest(false, "37.515298", "127.103113"); stompSession.send("/publish/etas/1", request); diff --git a/backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java b/backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java index c5a27b7de..e13db2a2f 100644 --- a/backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java +++ b/backend/src/test/java/com/ody/eta/domain/WebSocketEndpointTest.java @@ -15,7 +15,8 @@ void getSubscribeEndpoints() { List expected = List.of( WebSocketEndpoint.ETAS.getEndpoint(), WebSocketEndpoint.LOCATION.getEndpoint(), - WebSocketEndpoint.DISCONNECT.getEndpoint() + WebSocketEndpoint.DISCONNECT.getEndpoint(), + WebSocketEndpoint.ERROR.getEndpoint() ); assertThat(actual).containsExactlyInAnyOrderElementsOf(expected); From 79aad6f73aaa8426a847a91726dd598c68b3f4bd Mon Sep 17 00:00:00 2001 From: eun-byeol Date: Fri, 11 Oct 2024 02:09:28 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20destination=20preSend=20=EA=B2=80?= =?UTF-8?q?=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ody/eta/config/WebSocketErrorHandler.java | 14 ++------- .../ody/eta/config/WebSocketPreHandler.java | 29 ++++++++++++++----- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java b/backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java index c13f6920a..bce781026 100644 --- a/backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java +++ b/backend/src/main/java/com/ody/eta/config/WebSocketErrorHandler.java @@ -1,6 +1,5 @@ package com.ody.eta.config; -import com.ody.common.exception.OdyException; import com.ody.common.exception.OdyWebSocketException; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; @@ -23,21 +22,14 @@ public Message handleClientMessageProcessingError(Message client log.warn("message: {}", message); return createErrorMessage(message); } - - if (cause instanceof OdyException) { - log.warn("message: {}", message); - return createErrorMessage(message); - } - - if (cause instanceof Exception) { - log.error("exception: {}", cause); - return createErrorMessage("서버 에러"); - } return super.handleClientMessageProcessingError(clientMessage, ex); } private Message createErrorMessage(String exceptionMessage) { StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.ERROR); + accessor.setMessage(exceptionMessage); + accessor.setLeaveMutable(true); + return MessageBuilder.createMessage(exceptionMessage.getBytes(), accessor.getMessageHeaders()); } } diff --git a/backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java b/backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java index dcceadf0c..f016929e3 100644 --- a/backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java +++ b/backend/src/main/java/com/ody/eta/config/WebSocketPreHandler.java @@ -2,6 +2,7 @@ import com.ody.common.exception.OdyWebSocketException; import com.ody.eta.domain.WebSocketEndpoint; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; @@ -9,6 +10,7 @@ import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptor; +@Slf4j @Configuration public class WebSocketPreHandler implements ChannelInterceptor { @@ -18,8 +20,11 @@ public Message preSend(Message message, MessageChannel channel) { StompCommand command = accessor.getCommand(); String destination = accessor.getDestination(); + if (command == null) { + throw new OdyWebSocketException("stomp command는 null 일 수 없습니다."); + } if (StompCommand.SUBSCRIBE.equals(command)) { - validateSubscribeEndpoint(destination); + validateSubEndpoint(destination); } if (StompCommand.SEND.equals(command)) { validateSendEndpoint(destination); @@ -27,17 +32,25 @@ public Message preSend(Message message, MessageChannel channel) { return message; } - private void validateSubscribeEndpoint(String destination) { - if (WebSocketEndpoint.getSubscribeEndpoints().contains(destination)) { - return; + private void validateSubEndpoint(String destination) { + if (invalidSubEndpoint(destination)) { + throw new OdyWebSocketException(destination + "은 유효하지 않은 subscribe endpoint 입니다."); } - throw new OdyWebSocketException(destination + "은 유효하지 않은 subscribe endpoint 입니다."); + } + + private boolean invalidSubEndpoint(String destination) { + return destination == null || WebSocketEndpoint.getSubscribeEndpoints().stream() + .noneMatch(destination::startsWith); } private void validateSendEndpoint(String destination) { - if (WebSocketEndpoint.getSendEndpoints().contains(destination)) { - return; + if (invalidSendEndpoint(destination)) { + throw new OdyWebSocketException(destination + "은 유효하지 않은 send endpoint 입니다."); } - throw new OdyWebSocketException(destination + "은 유효하지 않은 send endpoint 입니다."); + } + + private boolean invalidSendEndpoint(String destination) { + return destination == null || WebSocketEndpoint.getSendEndpoints().stream() + .noneMatch(destination::startsWith); } }