목표
웹 소켓 사용 시 예외 처리하는 방법에 대해 학습한다.
학습 내용
예외 처리의 편리성을 위해서 언체크 루트 예외 객체인 ApplicationException 을 정의하여 해당 객체로 catch 부분을 구성한다.
웹 소켓 핸들러 내부
먼저 떠오르는 방법은 웹 소켓 핸들러 구현체에서 try~catch 문을 선언하는 방법이다.
public void afterConnectionEstablished(WebSocketSession session){
try{
Long themeId = Long.parseLong((String)session.getAttributes().get("theme"));
log.info("Client WebSocket 연결 성공: session id = {}", session.getId());
eventPublisher.publishEvent(new ClientWebSocketConnectedEvent(session.getId(), messageChannelFactory.create(session), themeId));
}catch(ApplicationException e){
...
}
}
편리한 방법이긴 하지만 웹 소켓 핸들러가 예외 처리에 대한 책임까지 지닌다면 객체의 책임은 무너지고 가독성도 떨어진다.
웹 소켓 핸들러 데코레이터(Decorator) 활용
스프링에서는 웹 소켓 핸들러를 데코레이트할 수 있는 클래스를 제공한다.
여기서, 데코레이터(Decorator) 는 디자인 패턴 중 하나로 대상 객체에 대한 기능 확장이나 변경이 필요할 때 객체의 결합을 통해 서브클래싱 대신 사용할 수 있는 패턴이다.

스프링에서는 WebSocketHandler를 캡슐화한 WebSocketHandlerDecorator 를 제공한다. package org.springframework.web.socket.handler;

실제 구현은 기능 확장이 필요한 메서드를 오버라이딩하여 정의하고 웹 소켓 핸들러 설정 파일에서 기존 웹 소켓 핸들러가 아닌 데코레이터를 감싼(wrap) 핸들러를 등록한다.
// 데코레이터
public class ExceptionHandlingWebSocketHandlerDecorator extends WebSocketHandlerDecorator {
public ExceptionHandlingWebSocketHandlerDecorator(ClientWebSocketHandler delegate) {
super(delegate);
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
try {
super.afterConnectionEstablished(session);
}catch(ApplicationException e){
log.error(e.getMessage());
session.close();
}
}
}
// 웹 소켓 설정
public class WebSocketServerConfig implements WebSocketConfigurer {
private final ClientWebSocketHandler clientWebSocketHandler;
private final WebSocketInterceptor webSocketInterceptor;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
WebSocketHandler handler = new ExceptionHandlingWebSocketHandlerDecorator(clientWebSocketHandler);
registry.addHandler(handler, "/conversation/streaming")
.addInterceptors(webSocketInterceptor);
}
}
Spring AOP 활용
데코레이터를 이용하여 처리하는 방법이 스프링 문서에서도 추천하는 방법이지만 Spring AOP 를 활용하여 공통 관심사인 예외 처리를 구현하는 방법도 있다. Spring AOP 에 대해서 자세한 설명은 하지 않겠다.
어노테이션을 활용하여 부가 기능(예외 처리)가 필요한 클래스 혹은 메서드에 어노테이션을 추가한다.
// Aspect
@Aspect
@Component
@RequiredArgsConstructor
public class WebSocketErrorHandlingAspect {
@Around("@annotation(me.khw7385.conversation.core.annotation.WebSocketErrorHandling) && args(session, ..)")
public Object handleOnError(ProceedingJoinPoint point, WebSocketSession session) throws Throwable{
try{
return point.proceed();
}catch (ApplicationException e){
log.error(e.getMessage());
session.close();
}
return null;
}
}
// 웹 소켓 핸들러
@WebSocketErrorHandling
public void afterConnectionEstablished(WebSocketSession session){
Long themeId = Long.parseLong((String)session.getAttributes().get("theme"));
log.info("Client WebSocket 연결 성공: session id = {}", session.getId());
eventPublisher.publishEvent(new ClientWebSocketConnectedEvent(session.getId(), messageChannelFactory.create(session), themeId));
}
참고 자료
https://inpa.tistory.com/entry/GOF-💠-데코레이터Decorator-패턴-제대로-배워보자
'개발일지' 카테고리의 다른 글
| Realtime API 응답 EventType에 따른 처리 구분 (0) | 2025.06.23 |
|---|---|
| 주변 사용자 조회 로직 변경 (0) | 2025.03.11 |
| 사용자 위치 변경에 대한 기능 수정 (0) | 2025.03.11 |
| [소프티어 부트캠프] 지도(공유) 기능에 대한 고찰 (1) | 2025.03.11 |