SSE를 활용한 알림 기능을 구현하고 있다. 계속 서버가 다운되는 문제가 있었는데, SSE 연결이 되어있는 상태에서 연결이 끊어지지 않고 계속 되어서 스레드 풀을 잡아먹고, 사용 가능한 스레드 풀을 다 채워서 서버가 자꾸 죽는 것이었다.
이미 SSE가 연결된 상태에서 끊어지지 않는 이유가 뭘까? 클라이언트 측에서는 아래와 같이 EventStrem을 생성한 후 특정 시점에 연결을 종료하려고 하였다. 현재는 EventStream이 메인 헤더 컴포넌트에 종속적이어서, 이 컴포넌트가 마운트 될 때 event를 연결하고, 언마운트될 때 연결을 끊고자 하였다.
그런데 이상하게 서버 로그를 확인해도 그 어디에서도 연결이 끊어진 것을 볼 수가 없고, 컴포넌트가 새롭게 마운트 될 때마다 연결만 가중되는 현상이 있었다. 우리는 헤더에 AccessToken을 전송하기 위해서 Event-source-polyfill이라는 확장 버전의 라이브러리를 사용중이었는데, 물론 이렇게 close를 한 경우에 알림은 수신이 되지 않았지만, 서버와 연결은 끊어지지 않았다.
이유를 알아보자. 일단 이 블로그에 의하면, 클라이언트의 요청으로 생성된 EventStrem은 서버에서 연결을 종료하지 않는 한 계속 유지된다고 한다.
우리도 원인을 확인하기 위해 백엔드분들과 로그를 보며 계속 확인했는데, 말씀하시길 Eventstream이 연결된 이후에 생성된 Emitter 가 삭제되지 않기 때문에 연결이 종료되지 않는 것으로 보인다고 하셨다. Emitter는 일정 시간이 지날 때 timeout 되는 경우 이외에는 임의로 연결을 끊을 수 없다고 하셨는데 Emitter가 뭐하는애지? 백엔드에서 설명해주시기로는 이벤트가 발생했을 때 알림 수신을 위해서 유저당 하나씩 생성되는 것이라고 말씀해주셨다.
구글에 검색하니 아래와 같은 결과가 나왔다. 무언가 방출하는 것?
검색을 하다 이에 대해 조금 친절하게 설명해주는 스택오버플로우 글을 찾았다. 이 글에 달린 답변에 따르면, 코드가 콜백을 받아서 실행하는 것보다, 뭔가 이벤트를 구독해서 실행되어야 하는 경우에 쓰인다고 한다.
티켓팅 서비스를 만든다고 할 때, 티켓이 DB에 삽입되는 경우 유저에게 이메일도 보내고, 알림까지 보내는 기능을 만든다면 함수가 호출될 때 여러 콜백이 수행되도록 하는 것보다 어떤 이벤트를 기반으로 관련된 여러 작업들을 수행함으로써 더 효율적으로 코드를 작성할 수 있다고 함. 뭔가 이벤트를 기반으로하는 인터페이스 정도로 이해하고 넘어가도록 하겠다.
궁금해서 좀 더 검색하다보니 SseEmitter라는 개념이 등장했다. 백엔드단 코드가 어떻게 짜여져 있는지 정확히는 모르겠으나 Spring에서 제공되는 SseEmitter 를 토대로 구현되었을 것으로 짐작됨. 이 글에 따르면 브라우저에서 서버에 재 연결 요청을 보낼 때 Emitter 객체를 다시 생성하기 때문에 기존 객체를 제거해 주어야 한다는 설명이 나온다.
이를 토대로 30000ms (30초)를 기준으로 Emitter 도 삭제하고, SSE 연결도 Timeout 되도록 설정해주었다. 계속 새롭게 연결을 하면서 기존 연결을 유지하지 않기 때문에 더 이상 서버도 죽지 않았다. 물론 위 글에 나온 DB connection 문제를 해결하셔서 그런 것도 있겠지만...
아무튼 결론은 클라이언트에서 EventSource.close로 연결을 아무리 끊는다고 한들 이를 토대로 서버에서 Emitter를 제거해주지 않으면 여러개의 Emitter 객체로 인해서 서버의 자원을 잡아먹는다는 것이다.
그리고 다른 얘기지만, 어떤 컴포넌트가 렌더링 될 때에만 알림을 수신하기 보다는, 전체 앱 - 유저의 로그인 ~ 로그아웃의 생명 주기와 동일하게 연결을 유지하는 것이 더 자연스러운 것 같다.
참고자료
https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/
'프로젝트 > 데일리 옥션' 카테고리의 다른 글
# 작업일지 19 / SSE 연결하여 알림 기능 구현하기 (0) | 2023.03.02 |
---|---|
# 작업일지 18 / 조건에 따른 데이터 refetch, 검색 기능 로직 수정 (0) | 2023.03.02 |
# 작업일지 16 / SSE를 연결해보자! Event source polyfill 라이브러리 사용해보기. 연결은 왜 끊어지지 않는가. (0) | 2023.02.27 |
# 작업일지 14,15 / 웹소켓 연결, 차트에 실시간 데이터 업데이트하기(Chart.js) (0) | 2023.02.24 |
# 작업일지 12,13 / STOMP publish 요청 성공! + 에러 처리는..? (0) | 2023.02.22 |