TIL

24/02/08 TIL __ socket io disconnect 되서 입력 불가 이슈(1)

GABOJOK 2024. 2. 8. 23:47

 

 

설 연휴를 앞두고 우리 프로젝트에 큰 문제가 생겼다.😂

채팅 기능이 갑자기 계속 끊겨서 입력이 안되고, 라이브 메인 화면에서도 방송 시작하고 사라지는...

 

팀원분이랑 테스트 하다가 방송을 켯다고 하셧는데 안보여서 당황했는데

이유는 socket io 의 disconnect가 일어나는게 단편적인 원인들 중 하나였다.

 

지금까지 테스트를 혼자 하거나 잠깐 팀원들이랑 해왔는데,

이번에 다 같이 동시에 테스트 해보며 다른 문제들도 많이 발견되었는데,

발견된 문제들은 아래와 같았다.

 

 

🚨 발견된 문제

1. 메인 화면에 방송중인 유저의 방송 썸네일 및 div가 잠시 보이고 사라져 버린다.

2. 유저의 채팅은 잠시후 연결이 끊긴다.

3. 많은 유저가 함께 채팅을 칠때 상대방이 enter 를 누름과 동시에 내 입력값이 사라진다.

 

 

🧐 이전과 달라진 점

1. 도커 환경 배포에서 쿠버네티스와 로드벨런서가 적용된 도커 배포로 변경됬다.

2. 레디스에 캐시 보관 로직이 변경됬다. 

3. 메인 화면에 실시간 라이브 방송 시청자 수를 보여주고 있다.  (setInterval 도입)

4. 도배 방지 기능이 추가되었다.

 

 

일단 갑자기 많은게 않되고 있어서 당황했지만, 하나씩 살펴보기로 했다.

 

 

 

일단 방송을 시작하고 메인에 보여지다 10초 내외로 메인 화면에서 사라지는 현상이 있었고

아래와 같은 메세지가 나왔다.

//에러메세지
TypeError: Cannot read properties of undefined (readinq 'userId') at ChatGatewayDisconnect. 
handleDisconnect (/finalProject/dist/src/chat / chat. disconnect. js: 32:55) at ConsumerObserver.next

 

어디선가 userId를 못찾아서 나온 에러였는데, 이 이유로 disconnect가 일어나고 있지는 않았다.

그러나 일단 이 에러 먼저 찾아가 보기로 했다.

 

 

CORS 가 문제인가 싶어 credentials: true 를 추가해 줬고,

credentials: true 를 사용할 때는 origin 옵션을  *로 설정할 수 없다. 구체적인 출처 또는 출처의 목록을 지정해야만 한다.

라는 글을 봐서 아래와 같이 수정했다

 

기본적으로, 웹 브라우저는 같은 출처 정책(Same-Origin Policy)을 적용하여, 스크립트가 다른 출처의 리소스와 상호 작용하는 것을 제한합니다. 하지만, 실제 웹 애플리케이션 개발에서는 다른 출처의 API나 리소스에 접근해야 할 필요가 종종 있습니다. 이때 CORS 정책을 통해 안전한 방법으로 이러한 제한을 완화할 수 있습니다.
credentials: true 설정은 Socket.IO 서버와 클라이언트 간의 연결 설정에서 CORS 요청을 할 때, 인증 정보(예: 쿠키, HTTP 인증 토큰 등)를 포함하여 요청하도록 허용합니다

 

cors: {
	origin: ['https://배포주소', 'https://배포주소'],
	credentials: true,
},

 

 

 

 

또 추가로

팀원분이 이미지 빌드 후 배포하기 전에, 윈스턴 로그를 몇개 찍어두었다.

이미지 빌드하는데 시간이 소요되고, 로컬 환경에  콘솔 로그처럼 테스트를 바로 해볼수 있는게 아니라

로그로 상황을 살펴보고자 했다.

 

async handleDisconnect(client: Socket) {
	try {
		Logger.log(`Client disconnected: ${client.id}`);
		Logger.log(`client.handshake: ${client.handshake}`);
		Logger.log(`client.handshake.auth: ${client.handshake.auth}`);
		Logger.log(`client.handshake.auth.user: ${client.handshake.auth.user}`);

 

 

 

이 handleDisconnect 함수 위에는 wsguard를 걸어두었는데,

이 wsguard에 찍어놓은 윈스턴 로그가 안보였다.

 

뭔가 예상과 다르게 작동해서 찾아보니.

handleDisconnect 이벤트 함수는 소켓 내부에서 자동으로 호출되는 이벤트 핸들러 인데,

연결이 끊겼을때 일련의 로직을 수행하며, 이런 wsguard 를 붙여도 실행되지 않는다고 한다.

 

NestJS의 `WebSocketGateway`에서 `@UseGuards`를 사용하여 지정된 가드(`WsGuard` 등)는 연결 설정 시에만 적용됩니다. 즉, 클라이언트가 웹소켓 서버에 처음 연결을 시도할 때 가드가 실행되어 인증 및 권한 검사 등의 로직을 처리합니다. 그러나, 클라이언트의 연결이 끊어지는 시점(`handleDisconnect` 이벤트)에서는 이러한 가드가 다시 실행되지 않습니다. `handleDisconnect` 메소드는 클라이언트 연결이 끊어졌을 때 자동으로 호출되는 이벤트 핸들러입니다. 이 메소드는 연결이 종료되었을 때 필요한 청소 작업을 수행하거나, 사용자가 방을 나갔음을 처리하는 등의 로직을 포함할 수 있습니다.

 

 

즉 wsguard를 handleDisconnect 위에 놓은 것은 의미 없는 코드였다..

그럼 문제가 있었는데, userId 를 토큰인증할때 소켓 객체에 넣어두기 때문에,

이 인증 가드를 통과하지 않으면 유저아이디를 볼 수 없다.

 

이전에는 disconnect가 유저가 해당 방송을 나가거나 할때만 작동해서 몰랐는데,

(메인페이지로 이동되도록 했기 때문이다.)

현재는 10초 정도의 텀을 두고 지속적으로 disconnect가 실행되는 상황이라 이 부분에 대한 처리가 매우 중요해 졌다.

 

 

결국 disconnect에서 일단 verify를 진행해서 테스트 해보기로 했고, 

아래와 같이 테스트 하니 userId를 못찾는 이슈는 해결되었다.

const token = client.handshake.auth.token;
if (!token || token.split(' ')[0] !== 'Bearer') return;

Logger.log(`디스커넥트 함수내부 client.handshake.auth: ${client.handshake.auth.token}`);

const tokenValue = token.split(' ')[1];
const verify = jwt.verify(tokenValue, this.secretKey);
const userId = verify.sub;
Logger.log(`디스커넥트 함수내부 userId  확인필수: ${userId}`);

 

그렇지만 여전히 왜 disconnect가 되는지 못찾았다..
내일 다시 봐야겠다