앱을 위한 고성능 스토리지: Storage Foundation API

토마스 슈타이너
토마스 슈타이너

웹 플랫폼에서는 개발자에게 웹용으로 미세 조정된 고성능 애플리케이션을 빌드하는 데 필요한 도구를 점점 더 많이 제공하고 있습니다. 특히 WebAssembly (Wasm)는 빠르고 강력한 웹 애플리케이션의 문을 열었고, 이제 Emscripten과 같은 기술을 통해 개발자는 웹에서 검증된 코드를 재사용할 수 있습니다. 이러한 잠재력을 실제로 활용하려면 개발자는 스토리지와 관련하여 동일한 성능과 유연성을 갖추어야 합니다.

여기에 Storage Foundation API가 필요합니다. Storage Foundation API는 성능이 뛰어난 데이터베이스 구현, 대규모 임시 파일 관리 등 많은 요청이 있었던 신규 웹 사용 사례를 빠르고 독단적이지 않은 새로운 스토리지 API로 제공합니다. 이 새로운 인터페이스를 통해 개발자는 웹에 '자체 저장소를 사용'하여 웹 코드와 플랫폼별 코드 간의 기능 격차를 줄일 수 있습니다.

Storage Foundation API는 매우 기본적인 파일 시스템과 비슷하도록 설계되어 있어, 개발자에게 더 높은 수준의 구성요소를 빌드할 수 있는 일반적이고 단순하며 성능이 뛰어난 프리미티브를 제공하여 개발자에게 유연성을 제공합니다. 애플리케이션은 사용성, 성능, 안정성 사이에서 적절한 균형을 찾아 필요에 맞는 최상의 도구를 활용할 수 있습니다.

웹에 다른 Storage API가 필요한 이유는 무엇인가요?

웹 플랫폼은 개발자를 위한 다양한 저장소 옵션을 제공하며, 각 옵션은 특정 사용 사례를 염두에 두고 구축되었습니다.

  • 이러한 옵션 중 일부는 이 제안서와 확실히 중복되지 않습니다. 쿠키 또는 sessionStoragelocalStorage 메커니즘으로 구성된 Web Storage API와 같이 매우 적은 양의 데이터만 저장할 수 있기 때문입니다.
  • 다른 옵션은 File and Directory Entries API 또는 WebSQL과 같은 다양한 이유로 이미 지원 중단되었습니다.
  • File System Access API도 API 노출 영역과 비슷하지만 클라이언트의 파일 시스템과 인터페이스하고 출처 또는 브라우저의 소유권 외부에 있을 수 있는 데이터에 대한 액세스를 제공하는 데 사용됩니다. 이렇게 중점을 두는 이유는 보다 엄격한 보안 고려사항과 높은 성능 비용입니다.
  • IndexedDB API는 Storage Foundation API 사용 사례 중 일부의 백엔드로 사용할 수 있습니다. 예를 들어 Emscripten에는 IndexedDB 기반 영구 파일 시스템인 IDBFS가 포함되어 있습니다. 하지만 IndexedDB는 기본적으로 키-값 저장소이므로 상당한 성능 제한이 발생합니다. 또한 IndexedDB에서 파일의 하위 섹션에 직접 액세스하는 것은 훨씬 더 어렵고 느립니다.
  • 마지막으로 CacheStorage 인터페이스는 널리 지원되며 웹 애플리케이션 리소스와 같은 대규모 데이터를 저장하는 데 맞게 조정되지만 값은 변경할 수 없습니다.

Storage Foundation API는 애플리케이션 원본 내에 정의된 변경 가능한 대용량 파일을 고성능으로 저장할 수 있도록 하여 이전 저장소 옵션의 모든 격차를 줄이려고 시도하는 것입니다.

Storage Foundation API의 추천 사용 사례

이 API를 사용할 수 있는 사이트의 예는 다음과 같습니다.

  • 대량의 동영상, 오디오 또는 이미지 데이터에서 작동하는 생산성 또는 창의성 앱입니다. 이러한 앱은 세그먼트를 메모리에 보관하는 대신 디스크에 오프로드할 수 있습니다.
  • Wasm에서 액세스할 수 있는 영구 파일 시스템을 사용하며 IDBFS가 보장하는 것보다 더 많은 성능이 필요한 앱

Storage Foundation API란 무엇인가요?

API에는 두 가지 주요 부분이 있습니다.

  • 파일 시스템 호출: 파일 및 파일 경로와 상호작용하는 기본 기능을 제공합니다.
  • 파일 핸들: 기존 파일에 대한 읽기 및 쓰기 액세스 권한을 제공합니다.

파일 시스템 호출

Storage Foundation API에는 window 객체에 위치하고 여러 함수가 포함된 새로운 객체 storageFoundation가 도입되었습니다.

  • storageFoundation.open(name): 지정된 이름의 파일이 있으면 열고 그렇지 않으면 새 파일을 만듭니다. 열린 파일로 확인되는 프로미스를 반환합니다.
  • storageFoundation.delete(name): 지정된 이름의 파일을 삭제합니다. 파일이 삭제되면 확인되는 프로미스를 반환합니다.
  • storageFoundation.rename(oldName, newName): 파일 이름을 이전 이름에서 새 이름으로 원자적으로 바꿉니다. 파일 이름이 변경될 때 확인되는 프로미스를 반환합니다.
  • storageFoundation.getAll(): 모든 기존 파일 이름의 배열로 결정되는 프로미스를 반환합니다.
  • storageFoundation.requestCapacity(requestedCapacity): 현재 실행 컨텍스트에서 사용할 새 용량 (바이트)을 요청합니다. 남은 용량으로 해결된 프로미스를 반환합니다.
  • storageFoundation.releaseCapacity(toBeReleasedCapacity): 현재 실행 컨텍스트에서 지정된 바이트 수를 해제하고 남은 용량으로 확인되는 프로미스를 반환합니다.
  • storageFoundation.getRemainingCapacity(): 현재 실행 컨텍스트에 사용할 수 있는 용량으로 결정되는 프로미스를 반환합니다.

파일 핸들

파일 작업은 다음 함수를 통해 수행됩니다.

  • NativeIOFile.close(): 파일을 닫고 작업 완료 시 확인되는 프로미스를 반환합니다.
  • NativeIOFile.flush(): 파일의 메모리 내 상태를 저장소 기기와 동기화 (즉, 플러시)하고 작업 완료 시 확인되는 프로미스를 반환합니다.
  • NativeIOFile.getLength(): 파일 길이(바이트)로 확인되는 프로미스를 반환합니다.
  • NativeIOFile.setLength(length): 파일 길이를 바이트 단위로 설정하고 작업이 완료되면 확인되는 프로미스를 반환합니다. 새 길이가 현재 길이보다 작으면 파일의 끝부터 바이트가 삭제됩니다. 그렇지 않으면 파일이 값이 0인 바이트로 확장됩니다.
  • NativeIOFile.read(buffer, offset): 지정된 버퍼를 전송한 결과인 버퍼를 통해 지정된 오프셋에서 파일의 콘텐츠를 읽습니다. 버퍼는 이후 분리된 상태로 유지됩니다. 전송된 버퍼와 성공적으로 읽은 바이트 수와 함께 NativeIOReadResult를 반환합니다.

    NativeIOReadResult는 다음 두 항목으로 구성된 객체입니다.

    • buffer: read()에 전달된 버퍼를 전송한 결과인 ArrayBufferView입니다. 소스 버퍼와 유형 및 길이가 동일합니다.
    • readBytes: buffer로 성공적으로 읽은 바이트 수입니다. 오류가 발생하거나 읽기 범위가 파일의 끝을 넘어서는 경우에는 버퍼 크기보다 작을 수 있습니다. 읽기 범위가 파일의 끝을 넘어서면 0으로 설정됩니다.
  • NativeIOFile.write(buffer, offset): 지정된 버퍼의 콘텐츠를 지정된 오프셋의 파일에 씁니다. 버퍼는 데이터가 기록되기 전에 전송되므로 분리된 상태로 유지됩니다. 전송된 버퍼 및 성공적으로 쓰여진 바이트 수와 함께 NativeIOWriteResult를 반환합니다. 쓰기 범위가 길이를 초과하면 파일이 확장됩니다.

    NativeIOWriteResult는 다음 두 항목으로 구성된 객체입니다.

    • buffer: write()에 전달된 버퍼를 전송한 결과인 ArrayBufferView입니다. 소스 버퍼와 유형 및 길이가 동일합니다.
    • writtenBytes: buffer에 성공적으로 작성된 바이트 수입니다. 오류가 발생하는 경우 버퍼 사이즈보다 작을 수 있습니다.

완성형 예시

위에서 소개한 개념을 보다 명확하게 이해할 수 있도록 Storage Foundation 파일 수명 주기의 여러 단계를 안내하는 두 가지 완전한 예를 소개합니다.

시작하기, 쓰기, 읽기, 마무리

// Open a file (creating it if needed).
const file = await storageFoundation.open('test_file');
try {
  // Request 100 bytes of capacity for this context.
  await storageFoundation.requestCapacity(100);

  const writeBuffer = new Uint8Array([64, 65, 66]);
  // Write the buffer at offset 0. After this operation, `result.buffer`
  // contains the transferred buffer and `result.writtenBytes` is 3,
  // the number of bytes written. `writeBuffer` is left detached.
  let result = await file.write(writeBuffer, 0);

  const readBuffer = new Uint8Array(3);
  // Read at offset 1. `result.buffer` contains the transferred buffer,
  // `result.readBytes` is 2, the number of bytes read. `readBuffer` is left
  // detached.
  result = await file.read(readBuffer, 1);
  // `Uint8Array(3) [65, 66, 0]`
  console.log(result.buffer);
} finally {
  file.close();
}

여는 중, 나열 중, 삭제 중

// Open three different files (creating them if needed).
await storageFoundation.open('sunrise');
await storageFoundation.open('noon');
await storageFoundation.open('sunset');
// List all existing files.
// `["sunset", "sunrise", "noon"]`
await storageFoundation.getAll();
// Delete one of the three files.
await storageFoundation.delete('noon');
// List all remaining existing files.
// `["sunrise", "noon"]`
await storageFoundation.getAll();

데모

아래에 삽입되어 있는 Storage Foundation API 데모로 플레이할 수 있습니다. 파일 생성, 이름 변경, 파일 쓰기, 읽기 작업을 수행하고, 변경 시 업데이트를 요청한 가용 용량을 확인합니다. Glitch에서 데모의 소스 코드를 찾을 수 있습니다.

보안 및 권한

Chromium팀은 사용자 제어, 투명성, 인체공학을 포함하여 강력한 웹 플랫폼 기능에 대한 액세스 제어에 정의된 핵심 원칙을 사용하여 Storage Foundation API를 설계하고 구현했습니다.

Storage Foundation API에 대한 액세스는 웹의 다른 최신 Storage API와 동일한 패턴에 따라 출처 제약이 있습니다. 즉, 출처는 직접 생성한 데이터에만 액세스할 수 있습니다. 또한 안전한 컨텍스트로 제한됩니다.

사용자 제어

저장용량 할당량은 디스크 공간 액세스 권한을 분산하고 악용을 방지하는 데 사용됩니다. 차지할 메모리를 먼저 요청해야 합니다. 다른 Storage API와 마찬가지로 사용자는 브라우저를 통해 Storage Foundation API가 차지한 공간을 비울 수 있습니다.

유용한 링크

감사의 말씀

Storage Foundation API는 Emanuel KrivoyRichard Stotz가 지정하고 구현했습니다. 이 문서는 Pete LePageJoe Medley가 검토했습니다.

Unsplash마커스 스피스케를 통해 히어로 이미지