Study/에러 정리

NestJS) 함수 사용에서 일관되지 않은 this 참조

Juzdalua 2025. 2. 19. 13:55

하나의 서비스에서 this의 잘못된 참조를 발견했다.

결론부터 이야기하면, 일반 함수가 아닌 화살표 함수로 해결했다.

testFunction(){}
testFunction = () => {}

 


net 라이브러리를 사용해서 tcp통신을 한다.

main.ts 파일의 bootstrap 함수에서 tcp함수를 시작한다.

// main.ts
// Start tcp Server
  try {
    const tcpService = app.get(TcpService);
    tcpService.startServer(
      process.env.TCP_SERVER_HOST,
      Number(process.env.TCP_SERVER_PORT),
    );
  } catch (error) {
    console.log(
      `❌ Connect TCP listening on Server ${process.env.TCP_SERVER_HOST}:${Number(process.env.TCP_SERVER_PORT)} Error: ${error}`,
    );
  }

 

  • tcp서비스가 실행되면 클라이언트 접속 정보 받는 로직을 생성자에서 실행한다.
  • 클라이언트가 연결되면 checkClientConnected() 함수에서 클라이언트 정보를 받아온다.
  • sendToClient() 함수에서 this.client에 접근하면 undefined가 반환된다.

 

@Injectable()
export class TcpService {
  private readonly logger = new Logger(TcpService.name);
  private server: net.Server;
  private client: net.Socket;

  constructor(
    @Inject(forwardRef(() => PacketHandlerService))
    private readonly packetHandlerService: PacketHandlerService,
  ) {
    this.server = net.createServer((socket: net.Socket) => {
      this.handleConnection(socket);
    });

    // this.checkClientConnected();
  }

  checkClientConnected() {
    setInterval(() => {
      if (this.client) {
        console.log(this.client.remoteAddress, this.client.remotePort);
      } else {
        console.log('no cli');
      }
    }, 1000);
  }

  //////////////////////////////////////////////////////
  // Server
  //////////////////////////////////////////////////////
  async startServer(host: string, port: number): Promise<void> {
    this.server.listen(port, host, () => {
      console.log(`✅ Connect TCP Server. listening on ${host}:${port}`);
    });

    this.server.on('error', (err) => {
      console.error(`[TCP Server] Error: ${err.message}`);
    });
  }

  private handleConnection(socket: net.Socket): void {
    this.logger.debug(
      `[Client connected] ${socket.remoteAddress}:${socket.remotePort}`,
    );
    if (!this.client) {
      this.client = socket;
    }

    socket.on('data', async (data) => {
      this.logger.debug(`[RECV] ${socket.remoteAddress}:${socket.remotePort}`);
      const { header, json } = await this.parseFromRecvBuffer(socket, data);

      this.packetHandlerService.handlePacket(
        HandleProtocolType.TCP,
        header.id,
        {
          header,
          json,
          socket,
        },
      );
    });

    socket.on('end', () => {
      this.logger.debug(
        `Client disconnected. ${socket.remoteAddress}:${socket.remotePort}`,
      );
      this.client = null;
    });

    socket.on('error', (err) => {
      if (err.message == 'read ECONNRESET') {
        this.logger.debug(
          `Client disconnected. ${socket.remoteAddress}:${socket.remotePort}`,
        );
        this.client = null;
      } else {
        console.error('[TCP Server] Socket error:', err.message);
      }
    });
  }

  // sendToClient(id: number, jsonString: string, socket: net.Socket) {
  sendToClient = (id: number, jsonString: string, socket: net.Socket) => {
    if (!this.client) {
      console.log('no client');
      return;
    }
  };
}

 

일반함수에서 this는 호출되는 컨텍스트에 따라 동적으로 결정된다.

TCPService에서 this를 사용하면 계속해서 this가 변경된다는 이야기이다.

sendToClient 함수를 예로 들자면, this는 socket 객체를 참조하게 된다.

 

화살표함수를 작성하면 this는 함수가 정의될 때, 상위 스코프에서 참조한다.

화살표 함수 내에서 this는 함수가 정의된 위치에서의 this를 그대로 유지한다.

TCPService에서 this를 사용하면 this는 TCPService를 가르킨다.