웹 소켓을 닫은 상태에서 메시지 수신 관련 문제 해결
문제 상황
스프링 프레임워크의 WebSocketHandler 를 이용하여 웹 소켓 서버를 구현하는 과정 중 에러 발생
public void afterConnectionEstablished(WebSocketSession session) 메서드 오버라이딩 ⇒ 쿼리 파라미터를 파싱하여 얻은 값을 이용하여 프롬프트 조회 시 예외 처리 ⇒ 예외 발생 시 웹 소켓 close 호출 및 자원 정리
close 를 한 상황임에도 handleTextMessage 클라이언트로부터 받은 메시지를 처리하는 함수 호출 ⇒ 정리된 자원에 접근하여 예외 발생!!!

원인 분석
afterConnectionEstablished 호출 시점

웹 소켓 handshake 가 완료되면 해당 메서드가 호출됨
클라이언트-서버의 연결 과정
자료를 찾아보다보니 무거워진 경향이 있으나 지금 차례에 정리해본다.
NIO Connector 를 기준으로 설명을 한다.
NIO Connector가 새로 들어온 소켓을 받아들이고, 실제 I/O 처리를 Worker 쓰레드 풀에 위임한다.
- Accpetor 컴포넌트가 serverSocket.accept()를 통해 Socket Connection 을 accept 한다.
- accept 의 결과를 얻은 Socket Channel 을 톰캣의 NIO Channel 로 NIO Channel 을 PollerEvent 로 캡슐환 후 PollerEvent 큐에 퍼블링싱한다. (Acceptor)
- Poller 가 PollerEvent 를 꺼내 Poller 내부의 Selector 에 채널 등록
- Selector 가 Channel 을 감시하여 이벤트 발생(read, write) 감지 시 요청을 처리하는 SocketProcessor 를 execututor에 제출하여 Worker 쓰레드에 할당
- 이 때, 톰캣의 Poller 에 의해 Task이 ThreadPoolExecutor 로 생산(Produce)되면, 내부 쓰레드가 소비(Consume) 한다.


결론
afterConnectionEstablished 를 수행하는 과정에서 클라이언트로부터 음성 데이터가 입력이 되어 이미 Task Queue 에 생산(Produce)된 상황이기 때문에 afterConnectionEstablished 에서 소켓을 닫더라도 handleMessage 메서드가 호출된다.
해결
클라이언트로부터 입력된 메시지를 처리할 때는 소켓이 열려(Open) 있는지 검사한 후 처리를 한다.
참고자료
WebSocketHandler (Spring Framework 6.2.8 API)
Invoked after the WebSocket connection has been closed by either side, or after a transport error has occurred.
docs.spring.io
https://mangkyu.tistory.com/422
[SpringBoot] 멀티 스레드 기반으로 다중 요청을 처리하는 톰캣(Tomcat)의 구조와 동작 방식
1. 멀티 스레드 기반으로 다중 요청을 처리하는 톰캣(Tomcat)의 구조와 동작 방식[ 웹 애플리케이션 서버(WAS, Web Application Server)과 톰캣 ]스프링 MVC 프레임워크는 자바 엔터프라이즈 개발을 편리하
mangkyu.tistory.com
https://jh-labs.tistory.com/329
[Tomcat] 아파치 톰캣9 Socket I/O 동작방식 이해
아파치 Tomcat의 BIO Connector / NIO Connector 개념 1) BIO Connector - Response를 보내고 끝내는 것이 아니라 TCP Connection이 만료될 때까지 Thread가 활성 상태로 남아있으며 소켓이 닫히면 Pool로 반환되는 구조 -
jh-labs.tistory.com