<aside> <img src="/icons/bookmark_purple.svg" alt="/icons/bookmark_purple.svg" width="40px" />

목차

</aside>

Nest.js와 Gateway를 처음 사용해보며 겪은 방황을 정리하였습니다. 해당 글은 Nest.js와 socket.io의 코드를 포함하고 있습니다.

Nest.js의 @WebSocketGateway는 어떻게 동작할까

해당 데코레이터가 어떠한 메타데이터를 설정하는지는 이전 글에서 살펴보았습니다. 그러므로 오늘은 @WebSocketGateway를 사용한 경우 어떻게 웹소켓 연걸 정보를 처리하는지 확인해 보겠습니다.

NestApplication

const app = await NestFactory.create(AppModule);

해당 코드가 동작하면 NestApplication을 생성하게 됩니다.

const { SocketModule } = optionalRequire(
  '@nestjs/websockets/socket-module',
  () => require('@nestjs/websockets/socket-module'),
);

NestApplication 상단에는 위와 같은 코드가 존재합니다.

export function optionalRequire(packageName: string, loaderFn?: Function) {
  try {
    return loaderFn ? loaderFn() : require(packageName);
  } catch (e) {
    return {};
  }
}

만약 loaderFn이 존재하면 해당 함수를 동작시키고, 없다면 명시한 패키지를 가져옵니다. 명시한 패키지가 없더라도 빈 객체를 반환하여 문제가 없게 만듭니다. 즉 '있으면 가져오고, 없으면 말고'의 코드가 되겠습니다.

프로젝트에서 '@nestjs/websockets' 종속성을 추가한 경우, 해당 코드를 통해 '@nestjs/websockets/socket-module'의 SocketModule을 불러오게 됩니다.

public async init(): Promise<this> {
  if (this.isInitialized) {
    return this;
  }

  this.applyOptions();
  await this.httpAdapter?.init();

  const useBodyParser =
    this.appOptions && this.appOptions.bodyParser !== false;
  useBodyParser && this.registerParserMiddleware();

  await this.registerModules(); // 여기서 모듈을 불러옵니다.
  await this.registerRouter();
  await this.callInitHook();
  await this.registerRouterHooks();
  await this.callBootstrapHook();

  this.isInitialized = true;
  this.logger.log(MESSAGES.APPLICATION_READY);
  return this;
}

init 과정에서 모듈을 불러옵니다.

public async registerModules() {
  this.registerWsModule();
  ...
}
public registerWsModule() {
  if (!this.socketModule) {
    return;
  }
  this.socketModule.register(
    this.container,
    this.config,
    this.graphInspector,
    this.appOptions,
    this.httpServer,
  );
}

해당 과정을 통해 SocketModule의 register가 호출되는군요.

SocketModule

SocketModule 코드를 살펴보겠습니다.

public register(
  container: NestContainer,
  applicationConfig: ApplicationConfig,
  graphInspector: GraphInspector,
  appOptions: TAppOptions,
  httpServer?: THttpServer,
) {
  this.applicationConfig = applicationConfig;
  this.appOptions = appOptions;
  this.httpServer = httpServer;

  const contextCreator = this.getContextCreator(container);
  const serverProvider = new SocketServerProvider(
    this.socketsContainer,
    applicationConfig,
  );
  this.webSocketsController = new WebSocketsController(
    serverProvider,
    applicationConfig,
    contextCreator,
    graphInspector,
    this.appOptions,
  );
  const modules = container.getModules();
  modules.forEach(({ providers }, moduleName: string) =>
    this.connectAllGateways(providers, moduleName),
  );
}