저장을 습관화

NestJS - 네임스페이스와 생명주기(Lifecycle) 메모 본문

공부/node.js

NestJS - 네임스페이스와 생명주기(Lifecycle) 메모

ctrs 2023. 10. 19. 21:51

1. 네임스페이스

http 연결 방식의 경우 엔드포인트를 통해 api를 나누었다.
예를 들어

users/signup, users/login, users/update의 API를 가진 컨트롤러, 서비스 등의 내용이 담긴
users.module.ts

post/write, post/update, post/delete의 API를 가진 컨트롤러, 서비스 등의 내용이 담긴
posts.module.ts

이렇게 users와 post 두 개의 모듈로 나누어 유지 보수성을 향상시킬 수 있었다.

 


웹소켓을 사용한 양방향 통신 방식에서는 네임스페이스를 통해 모듈을 나눌 수 있다.

- chats.gateway.ts - 소켓의 게이트웨이, 클라이언트와 서버 간의 소켓 통신을 처리하는 엔드포인트

// 중략
import { WebSocketGateway } from '@nestjs/websockets';

@WebSocketGateway({ namespace: 'chattings' }) 
export class ChatsGateway {

  // 중략
  
}

 

웹 소켓 게이트웨이에서 namespace가 'chattings'라고 선언하였다.

 

- script.js

const socket = io('/chattings'); // socket.io의 메소드를 사용하기 위한 준비이며,
// 네임스페이스는 '/chattings'가 된다.

// 중략..

const helloUser = () => {
  const username = prompt('What is your name?');
  socket.emit('new_user', username, (data) => {
    console.log(data);
  });
  
// 중략..

이러한 상황에서 서버와 클라이언트 간의 통신이 'chattings' 네임스페이스 안에서 이루어진다.

 

 

2. 생명주기(Lifecycle)

- chats.gateway.ts

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

@WebSocketGateway({ namespace: 'chattings' })
export class ChatsGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
  // OnGateWayInit, OnGatewayConnection, OnGatewayDisconnect은 인터페이스이며 인터페이스는 규약이다.
  // 규약에 따라 OnGatewayInit의 뒤에는 반드시 afterInit()이 와야하며,
  // OnGatewayConnection의 뒤에는 반드시 handleConnection()이,
  // OnGatewayDisconnect의 뒤에는 반드시 handleDisconnect()가 와야한다.
{
  private logger = new Logger('chat');
  constructor() {
    this.logger.log('this is constructor');
  }

  handleDisconnect(@ConnectedSocket() socket: Socket) {
    this.logger.log(`disconnected: ${socket.id}, ${socket.nsp.name}`); // nsp는 네임스페이스의 약자
  }

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

  afterInit() {
    this.logger.log('this is init');
  }
// 중략..
}

 

nestjs에서 socket 통신을 할 때에 쓰이는 주요 lifecycle hooks에는 3가지가 있다

이 셋은 모두 인터페이스이며 규약이 정해져 있다.

 

onGatewayInit - 게이트웨이 클래스가 실행되었을때 제일 먼저 호출된다.

반드시 afterInit()이 뒤따라온다.

 

OnGatewayConnection - 통신이 연결되었을때 호출된다.

반드시 handleConnection()이 뒤따라 온다.

소켓 인스턴스를 전달인자로 사용할 수 있다.

 

OnGatewayDisconnect - 통신의 연결이 끊어졌을때(창 닫음, 새로고침 등) 호출된다. 

반드시 handleDisconnect()가 뒤따라 온다.

소켓 인스턴스를 전달인자로 사용할 수 있다.

 

https://docs.nestjs.com/websockets/gateways#lifecycle-hooks

 

Documentation | NestJS - A progressive Node.js framework

Nest is a framework for building efficient, scalable Node.js server-side applications. It uses progressive JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Rea

docs.nestjs.com

 

실행 결과

- 웹 서버 가동 시

[오후 9:41:01] Starting compilation in watch mode...

[오후 9:41:04] Found 0 errors. Watching for file changes.

[Nest] 16004  - 2023. 10. 19. 오후 9:41:05     LOG [NestFactory] Starting Nest application...
[Nest] 16004  - 2023. 10. 19. 오후 9:41:05     LOG [chat] this is constructor
[Nest] 16004  - 2023. 10. 19. 오후 9:41:05     LOG [InstanceLoader] MongooseModule dependencies initialized +1ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:05     LOG [InstanceLoader] ChatsModule dependencies initialized +0ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:05     LOG [InstanceLoader] AppModule dependencies 
initialized +0ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:05     LOG [InstanceLoader] ConfigHostModule dependencies initialized +1ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:05     LOG [InstanceLoader] ConfigModule dependencies initialized +0ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:06     LOG [InstanceLoader] MongooseCoreModule dependencies initialized +509ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:06     LOG [chat] this is init
[Nest] 16004  - 2023. 10. 19. 오후 9:41:06     LOG [WebSocketsController] ChatsGateway subscribed to the "new_user" message +1ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:06     LOG [RoutesResolver] AppController {/}: +2ms[Nest] 16004  - 2023. 10. 19. 오후 9:41:06     LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 16004  - 2023. 10. 19. 오후 9:41:06     LOG [NestApplication] Nest application successfully started +2ms

 

클래스의 생성자 constructor의 로그가 가장 먼저 찍히며

그 다음 onGatewayInit - afterInit의 로그가 찍힌다.

 

- 웹 페이지 접속 시

접속 후 chattings 네임스페이스의 socket.emit을 실행시킨다.

[Nest] 16004  - 2023. 10. 19. 오후 9:43:12     LOG [chat] this is connect
[Nest] 16004  - 2023. 10. 19. 오후 9:43:12     LOG [chat] connected: 5dr8aPTIAHFCySsaAAAH, 
/chattings

OnGatewayConnection - handleConnection의 로그가 찍힌다.

 

- 웹 페이지를 닫아 연결을 끊는다.

[Nest] 16004  - 2023. 10. 19. 오후 9:43:12     LOG [chat] this is connect
[Nest] 16004  - 2023. 10. 19. 오후 9:43:12     LOG [chat] connected: 5dr8aPTIAHFCySsaAAAH, 
/chattings
[Nest] 16004  - 2023. 10. 19. 오후 9:46:46     LOG [chat] disconnected: 5dr8aPTIAHFCySsaAAAH, /chattings

OnGatewayDisconnect - handleDisconnect의 로그가 찍혔다.

 

 

아래 게이트웨이에서 확인할 수 있듯 

메소드 handleDisconnect, handleConnection, afterInit가 적힌 순서에 상관없이

각자의 상황에서 메소드를 실행시키며,

constructor는 클래스 자체의 생성자이기 때문에 다른 인터페이스의 메소드들보다 앞서서 실행된다.

@WebSocketGateway({ namespace: 'chattings' })
export class ChatsGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  private logger = new Logger('chat');
  constructor() {
    this.logger.log('this is constructor');
  }

  handleDisconnect(@ConnectedSocket() socket: Socket) {
    this.logger.log(`disconnected: ${socket.id}, ${socket.nsp.name}`); // nsp는 네임스페이스의 약자
  }

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

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