TypeScript using 키워드와 명시적 리소스 관리 (Explicit Resource Management)

프로덕션 환경의 메모리 누수는 종종 한 가지 실수에서 비롯됩니다. 바로 개발자가 리소스를 획득한 후 이를 해제하지 않는 것입니다.

에러 발생 후에도 데이터베이스 연결이 열려 있거나, 파일 핸들이 시스템 리소스를 점유하거나, WebSocket 클라이언트가 죽은 서버에 계속 연결되어 있는 경우가 있습니다. 이는 finally 블록에서 수동으로 정리(cleanup)하는 방식에 의존할 때 발생합니다.

TypeScript의 using 키워드가 이 문제를 해결합니다. 이는 ECMAScript의 명시적 리소스 관리(Explicit Resource Management) 제안의 일부입니다. 이 키워드는 disposable 패턴을 통해 정리를 보장합니다. 리소스가 스코프를 벗어나면 TypeScript가 해당 리소스의 disposal 메서드를 자동으로 실행합니다.

더 이상 수동으로 finally 블록을 작성할 필요가 없습니다. 정리를 잊어버릴 일도 없고, 연결이 누수되는 일도 없습니다.

작동 방식:

  • using 키워드는 리소스가 스코프를 벗어날 때 정리를 보장합니다.
  • disposable 리소스는 동기 작업의 경우 Symbol.dispose를, 비동기 작업의 경우 Symbol.asyncDispose를 구현합니다.
  • TypeScript는 using 선언을 자동 disposal 스택을 갖춘 try-finally 블록으로 변환합니다.
  • 이 패턴은 조기 반환(early return), 예외 발생 또는 코드를 누락하여 발생하는 누수를 방지합니다.

데이터베이스 연결, 파일 핸들, 락(lock) 또는 타이머와 같이 수명이 정해진 모든 리소스에 using을 사용하세요.

이 메커니즘은 disposal 프로토콜을 사용합니다. 객체는 Symbol.dispose를 키로 하는 메서드를 구현합니다. 정상 종료, return, 또는 예외로 인해 스코프를 벗어날 때 TypeScript가 해당 메서드를 호출합니다.

파일 핸들 예시:

class FileHandle {
  private handle: number;

  constructor(path: string) {
    this.handle = openFileSync(path);
  }

  [Symbol.dispose]() {
    if (this.handle !== -1) {
      closeFileSync(this.handle);
      this.handle = -1;
    }
  }

  read(buffer: Buffer): number {
    return readSync(this.handle, buffer);
  }
}

function processFile(path: string) {
  using file = new FileHandle(path);
  const buffer = Buffer.alloc(1024);
  file.read(buffer);
}

정리는 생성된 finally 블록 내에서 실행됩니다. 함수에서 에러가 발생하거나 조기에 반환되더라도 실행됩니다.

TypeScript는 disposal 스택을 유지합니다. 리소스를 획득한 역순으로 정리를 수행합니다. 이는 대부분의 정리 로직에서 자연스러운 의존성 순서와 일치합니다.

비동기 정리가 필요한 경우 await using을 사용하세요. 이 경우 리소스는 Symbol.asyncDispose를 구현해야 합니다. TypeScript는 다음 단계로 넘어가기 전에 반환된 Promise를 await 합니다.

성공을 위한 세 가지 규칙:

  • disposal 메서드 내부에서 예외를 던지지 마세요. 대신 내부에서 에러를 잡아서(catch) 로그를 남기세요.
  • 동기 Symbol.dispose 메서드 내부에서 비동기 작업을 사용하지 마세요. 대신 Symbol.asyncDispose를 사용하세요.
  • using 선언은 블록 스코프(block-scoped)임을 기억하세요. if 블록 내부의 리소스는 함수가 끝날 때가 아니라 해당 블록이 끝날 때 정리됩니다.

이 패턴은 무시할 수 있을 정도의 런타임 비용으로 안전성을 제공합니다.

Source: https://dev.to/jsmanifest/typescript-using-keyword-and-explicit-resource-management-done-right-22pg