NestJS 공식문서 Streaming files

GGAE99·2023년 8월 15일
0

NestJS 공식 문서

목록 보기
23/33
post-thumbnail

Streaming files

이 장에서는 HTTP 애플리케이션에서 파일을 스트리밍하는 방법을 보여줍니다.
아래에 제시된 예제는 GraphQL이나 마이크로서비스 애플리케이션에는 적용되지 않습니다.

REST API에서 파일을 클라이언트에게 반환하려는 경우가 있을 수 있습니다. (서버에서 파일 반환)
Nest에서 이를 수행하려면 일반적으로 다음과 같이 수행합니다

@Controller('file')
export class FileController {
  @Get()
  getFile(@Res() res: Response) {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    file.pipe(res);
  }
}

하지만 이렇게 하면 컨트롤러 이후의 인터셉터 로직에 대한 접근이 손실됩니다. 이를 처리하기 위해 StreamableFile 인스턴스를 반환하고 내부에서 프레임워크가 응답을 파이핑하는 작업을 처리하도록 할 수 있습니다.

Streamable File class

StreamableFile은 반환될 스트림을 보유하는 클래스입니다. 새로운 StreamableFile을 생성하려면 BufferStreamStreamableFile 생성자에 전달할 수 있습니다.

Hint!
StreamableFile 클래스는 @nestjs/common에서 가져올 수 있습니다.

Cross-platform support

Fastify는 기본적으로 stream.pipe(res)를 호출하지 않아도 파일을 보내는 것을 지원하므로 StreamableFile 클래스를 전혀 사용할 필요가 없습니다. 그러나 Nest는 Express와 Fastify 양쪽에서 StreamableFile의 사용을 지원하므로 Express와 Fastify 간의 호환성에 대해 걱정할 필요가 없습니다.

Example

아래에서는 package.json 파일을 JSON대신 파일로 반환하는 간단한 예제를 찾을 수 있습니다. 그러나 이 아이디어는 이미지, 문서 및 다른 모든 파일 유형에 자연스럽게 확장됩니다.

import { Controller, Get, StreamableFile } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from 'path';

@Controller('file')
export class FileController {
  @Get()
  getFile(): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    return new StreamableFile(file);
  }
}

기본 콘텐츠 유형은 application/octet-stream입니다. 응답을 사용자 정의해야 하는 경우 res.set 메서드나 @Header() 데코레이터를 사용하여 다음과 같이 수행할 수 있습니다.

import { Controller, Get, StreamableFile, Res } from '@nestjs/common';
import { createReadStream } from 'fs';
import { join } from 'path';
import type { Response } from 'express';

@Controller('file')
export class FileController {
  @Get()
  getFile(@Res({ passthrough: true }) res: Response): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    res.set({
      'Content-Type': 'application/json',
      'Content-Disposition': 'attachment; filename="package.json"',
    });
    return new StreamableFile(file);
  }

  // Or even:
  @Get()
  @Header('Content-Type', 'application/json')
  @Header('Content-Disposition', 'attachment; filename="package.json"')
  getStaticFile(): StreamableFile {
    const file = createReadStream(join(process.cwd(), 'package.json'));
    return new StreamableFile(file);
  }  
}

내 정리

스트림(Stream)은 데이터를 조각조각으로 나누어 전송하는 방식을 말하는 것이다. 데이터가 한 번에 모두 로드되지 않고 작은 덩어리로 나누어져 전송되면, 메모리 효율성과 성능을 향상시킬 수 있다.
스트림의 종류 :

  • 읽기 스트림(Readable Stream)
  • 쓰기 스트림(Writable Stream)
  • 이중 스트림(Duplex Stream)
  • 변환 스트림(Transform Stream)

file.pipe(res); 대신에 return new StreamableFile(file);을 사용하는 이유는 NestJS의 기능과 권장사항을 활용하여 파일 다운로드를 더 효율적으로 처리하기 위함

process.cwd() => 현재 디렉토리

인터셉터는 NestJS에서 요청 및 응답 처리를 중간에 가로채어 로직을 적용하거나 수정할 수 있는 기능이다. 하지만 pipe를 사용하면 인터셉터 사용이 어려운 이유는 다음과 같다.

  • 파이핑 작업: file.pipe(res);를 사용하면 파일을 읽는 스트림과 응답 객체(res) 사이에 직접 파이핑 작업이 이루어진다. 이로 인해 NestJS의 미들웨어 및 인터셉터가 이 작업을 가로채거나 수정하기가 어려워진다.
  • 응답 객체 조작 제한: 직접 res 객체를 파이핑하면 해당 응답 객체를 수정하거나 상태 코드, 헤더 등을 조작하기 어려워진다. 인터셉터는 이와 같은 응답 객체의 조작이 가능하도록 설계되었다.

Stream에 대한 자료 : 링크

0개의 댓글