인프런 커뮤니티 질문&답변

kme님의 프로필 이미지
kme

작성한 질문수

Slack 클론 코딩[백엔드 with NestJS + TypeORM]

웹소켓 연동하기(socket.io, gateway)

NextJS와 NestJS 소켓IO 연결

해결된 질문

작성

·

1K

·

수정됨

1

안녕하세요 제로초님! 강의 잘 듣고 있습니다.

제가 현재 프론트엔드를 NextJS 14.xx(app router)로 작성하고 있고, 백엔드는 제로초님 강의 따라서 NestJS로 만들고 있는데 현재 연결은 로컬에서 HTTP로 하고 있습니다.

 

그런데 소켓을 연결하는 과정에서 계속

 

'WebSocket connection to 'ws://localhost:8080/socket.io/?EIO=4&transport=websocket' failed: WebSocket is closed before the connection is established.'

 

위와 같은 에러가 뜹니다. 이틀간 계속 검색해봤는데 도대체 왜 연결이 안되는지 모르겠습니다... ㅠㅠ

 

프론트는 현재 아래와 같이 되어있구요.

localhost:3000으로 돌리고 있습니다. 

 

"socket.io-client": "^4.7.4",

import io from 'socket.io-client';
  useEffect(() => {
    const options = {
      auth: `${getCookie('accessToken')}`,
      extraHeaders: { id: chatRoomId },
    };
    const socket = io(`http://localhost:8080/chats`, options);
    console.log(socket);

    socket.on('connect', () => {
      console.log('WebSocket connected.');
    });

    return () => {
      socket.off();
    };
  }, []);

 

백엔드는 아래와 같이 되어있습니다.

 

import { Socket } from 'socket.io';
import {
  ConnectedSocket,
  MessageBody,
  OnGatewayConnection,
  OnGatewayDisconnect,
  OnGatewayInit,
  SubscribeMessage,
  WebSocketGateway,
} from '@nestjs/websockets';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Message } from './entity/message.entity';
import { Logger } from '@nestjs/common';

@WebSocketGateway({
  namespace: 'chats',
  cors: true,
})
export class ChatsGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  private logger = new Logger('CHAT-SERVICE');

  constructor(
    @InjectRepository(Message)
    private readonly messageRepository: Repository<Message>,
  ) {
    this.logger.log('constructor');
  }

  afterInit() {
    this.logger.log('init');
  }

  async handleConnection(@ConnectedSocket() socket: Socket) {
    this.logger.log(`connected: ${socket.id} ${socket.nsp.name}`);
  }

  async handleDisconnect(@ConnectedSocket() socket: Socket) {
    this.logger.log(`disconnected: ${socket.id} ${socket.nsp.name}`);
  }

  @SubscribeMessage('send-message')
  async handleSubmitChat(
    @MessageBody() message: string,
    @ConnectedSocket() socket: Socket,
  ) {}
}


chat-service의 main.ts는 아래와 같습니다.

 

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const port = 8080;

  await app.listen(port);
  console.log(`CHAT-SERVICE listening on ${port}!`);
}
bootstrap();

 

app.module.ts는 아래와 같습니다.

 

import { Module } from '@nestjs/common';
import { ChatModule } from './chat/chat.module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
import jwtConfig from './config/jwt.config';
import mysqlConfig from './config/mysql.config';
import sentryConfig from './config/sentry.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [jwtConfig, mysqlConfig, sentryConfig],
    }),
    TypeOrmModule.forRootAsync({
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => {
        let object: TypeOrmModuleOptions = {
          type: 'mysql',
          host: configService.get('mysql.host'),
          port: configService.get('mysql.port'),
          database: configService.get('mysql.database'),
          username: configService.get('mysql.username'),
          password: configService.get('mysql.password'),
          autoLoadEntities: true,
          synchronize: true,
        };
        if (configService.get('STAGE') === 'LOCAL') {
          object = Object.assign(object, {
            logging: true,
          });
        }
        return object;
      },
    }),
    ChatModule,
  ],
  controllers: [],
  providers: [],
})
export class AppModule {}

 

소켓 객체는 프론트에서 찍어보면 다음과 같이 나옵니다.

Screenshot 2024-03-11 at 17.21.53.JPG

@WebSocketGateway({ namespace: 'chats', cors: true, }) 에서 cors를 http://localhost:3000으로 설정하고 main에서도 다 열었을때도 안되는걸로 보아 cors 문제는 아닌듯합니다.

답변 1

1

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

localhost:8080 http 연결은 되나요? ChatModule에 ChatGateway는 잘 연결하셨나요

kme님의 프로필 이미지
kme
질문자

캡처.JPG

백엔드에서 chat-service 구동시 다음과 같이 뜹니다.

웹에서 테스트로 만든 'localhost:8080/ping'으로 들어가면

Screenshot 2024-03-11 at 20.02.08.JPG

와 같이 떠서, HTTP 서버는 잘 구동되고 있구요.
chat.module.ts는 아래와 같습니다.

 

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ChatRoom } from './entity/chatRoom.entity';
import { Message } from './entity/message.entity';
import { ChatsGateway } from './chats.gateway';
import { ChatsController } from './chats.controller';

@Module({
  imports: [TypeOrmModule.forFeature([ChatRoom, Message])],
  controllers: [ChatsController],
  providers: [ChatsGateway],
})
export class ChatModule {}

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

지금 afterInit의 init도 안 뜨는 상태인거죠? 챗모듈에서 exports: [ChatGateway]도 한 번 추가해보세요

kme님의 프로필 이미지
kme
질문자

네 맞습니다. init 조차 뜨질 않습니다. ㅠㅠ exports에 추가해도 여전히

image
이와 같이 connected가 false 상태로 뜨네요... ㅠㅠ

서버쪽에는 아무것도 안들어오구요...

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

@WebSocketGateway(8080, { namespace: 'chats', cors: true, transports: ['websocket'] })

한 번 해보세요. 또한 프론트랑 백엔드의 웹소켓 버전이 4로 동일한지도 확인해보세요.

kme님의 프로필 이미지
kme
질문자

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const port = 8000;

  await app.listen(port);
  console.log(`CHAT-SERVICE listening on ${port}!`);
}
bootstrap();

 

일단 포트 충돌을 방지하기 위하여 main.ts의 포트를 8000으로 바꾼 뒤,

 

@WebSocketGateway(8080, {
  transports: ['websocket'],
  namespace: 'chats',
  cors: true,
})


제로초님 말씀처럼 위와 같이 바꿨더니 오류 메시지가 아래처럼 바뀌기는 했습니다.

GET http://localhost:8080/socket.io/?EIO=4&transport=polling&t=OujvTur 400 (Bad Request)


그래도 여전히 문제는 해결되지 않네요... ㅠㅠ

 

백엔드 소켓 버전도 ^4.7.4 버전으로 프론트 엔드와 동일합니다.

 

    "@nestjs/platform-socket.io": "^10.3.3",
    "@nestjs/websockets": "^10.3.3",
    "socket.io": "^4.7.4"

 

제로초(조현영)님의 프로필 이미지
제로초(조현영)
지식공유자

포트 바꾸시면 프론트에서도 포트 바꾸셔야합니다.

kme님의 프로필 이미지
kme
질문자

해결했습니다!

 

혹시 다른 분들에게도 도움이 될까하여 공유합니다.

 

  useEffect(() => {
    const options = {
      transports: ['websocket'],
      // auth: `${getCookie('accessToken')}`,  <- problem!
      extraHeaders: { id: chatRoomId },
    };
    const socket = io(`http://localhost:8080/chats`, options);

    socket.on('connect', () => {
      console.log('WebSocket connected.');
    });

    return () => {
      socket.disconnect();
    };
  }, []);


문제는 위의 프론트엔드 코드에서 주석 처리 되어있는 부분이었습니다.

백엔드 main.ts는 아래와 같습니다.

 

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const port = 8000;

  await app.listen(port);
  console.log(`CHAT-SERVICE listening on ${port}!`);
}
bootstrap();


chat.gateway.ts는 아래와 같습니다.

 

@WebSocketGateway(8080, {
  transports: ['websocket'],
  namespace: 'chats',
  cors: {
    origin: ['http://localhost:3000'],
  },
})


그간 답변해주셔서 감사합니다 제로초님! :)

kme님의 프로필 이미지
kme

작성한 질문수

질문하기