TypeScript와 StorageManager
이 글은 TypeScript와 StorageManager에 대해 설명합니다.
TypeScript와 StorageManager에 대해 실제 예제와 함께 설명합니다.
YouTube Video
TypeScript와 StorageManager
StorageManager란 무엇인가요?
StorageManager는 웹 애플리케이션이 사용 중인 스토리지와 사용 가능한 용량을 추정할 수 있게 해주는 API입니다. 또한 사용자가나 브라우저에 의한 자동 데이터 삭제를 방지하는 지속성 요청(persist()) 기능도 제공합니다. 이를 통해 오프라인 애플리케이션에서 IndexedDB에 대용량 데이터를 저장할 때 충분한 여유 공간이 있는지 또는 데이터가 자동으로 삭제될 수 있는지 제어할 수 있습니다.
기능 감지
먼저 브라우저가 navigator.storage를 지원하는지 확인하세요.
1// TypeScript: feature detection for StorageManager
2function supportsStorageManager(): boolean {
3 return typeof navigator !== "undefined" &&
4 typeof navigator.storage !== "undefined" &&
5 typeof navigator.storage.estimate === "function";
6}
7
8// Example usage
9if (supportsStorageManager()) {
10 console.log("StorageManager is supported.");
11} else {
12 console.warn("StorageManager not supported. Falling back to localStorage or IndexedDB.");
13}- 이 함수는 StorageManager API의 사용 가능 여부를 단계적으로 확인합니다.
- Safari와 같은 일부 브라우저에서
navigator.storage가 존재하지 않기 때문에typeof를 사용하여 안전하게 존재 여부를 확인하는 것이 중요합니다.
스토리지 용량 추정하기 (estimate())
supportsStorageManager()로 기능을 확인한 후, navigator.storage.estimate()에서 사용량(usage)과 할당량(quota)을 가져옵니다.
1// TypeScript: safely get storage estimate
2async function getStorageEstimate(): Promise<{ usage: number; quota: number } | null> {
3 if (!supportsStorageManager()) {
4 console.warn("StorageManager not supported.");
5 return null;
6 }
7
8 const estimate = await navigator.storage.estimate();
9 return { usage: estimate.usage ?? 0, quota: estimate.quota ?? 0 };
10}
11
12// Example usage
13getStorageEstimate().then(result => {
14 if (result) {
15 console.log(`Usage: ${result.usage} bytes / Quota: ${result.quota} bytes`);
16 }
17});- 이 함수는 항상 안전하게 동작하며, 브라우저가 지원하지 않을 경우에는
null을 반환합니다. usage와quota는 추정치이므로 브라우저마다 다를 수 있습니다.
데이터 지속성 요청 (persist())
persist()를 사용하여 중요한 데이터(예: 오프라인 캐시)가 브라우저에 의해 자동으로 삭제되지 않도록 지속성을 요청할 수 있습니다. 하지만 모든 환경에서 항상 성공하는 것은 아닙니다.
1// TypeScript: safely request persistent storage
2async function requestPersistence(): Promise<boolean> {
3 if (!supportsStorageManager()) {
4 console.warn("StorageManager not supported.");
5 return false;
6 }
7
8 if (typeof navigator.storage.persist !== "function") {
9 console.warn("persist() not available in this browser.");
10 return false;
11 }
12
13 try {
14 const granted = await navigator.storage.persist();
15 return Boolean(granted);
16 } catch (err) {
17 console.error("persist() error:", err);
18 return false;
19 }
20}
21
22// Example usage
23requestPersistence().then(granted => {
24 console.log("Persistence granted?", granted);
25});persist()가 성공하면 사용자가 직접 삭제하지 않는 한 데이터가 브라우저에 의해 자동으로 삭제되지 않습니다. 하지만 사용자 동작이나 브라우저 설정에 따라 요청이 거절될 수 있습니다.
데이터 저장 전 사용 가능한 스토리지 확인
대용량 데이터를 저장하기 전에 사용 가능한 공간을 확인하여 쓰기 실패(QuotaExceededError)를 방지하세요.
1// TypeScript: ensure enough space before writing
2async function ensureSpaceAndWrite(neededBytes: number, writeFn: () => Promise<void>): Promise<boolean> {
3 const estimate = await getStorageEstimate();
4 if (!estimate) {
5 console.warn("Cannot check storage space. Proceeding without validation.");
6 await writeFn();
7 return true;
8 }
9
10 const free = estimate.quota - estimate.usage;
11 if (free < neededBytes) {
12 console.warn(`Not enough space. Free: ${free} bytes, needed: ${neededBytes} bytes.`);
13 return false;
14 }
15
16 await writeFn();
17 return true;
18}
19
20// Example usage
21ensureSpaceAndWrite(10 * 1024 * 1024, async () => {
22 console.log("Saving large data...");
23});- 저장 전에 사용 가능한 스토리지를 확인하면 용량 부족으로 인한 쓰기 중단 위험을 줄일 수 있습니다.
TypeScript에서 localStorage 타입 안전하게 다루기
localStorage는 가벼운 설정 데이터나 비슷한 정보를 저장하는 데 편리합니다.
다음 클래스는 제네릭을 사용하여 타입 안정 래퍼를 만듭니다.
1// TypeScript: typed localStorage wrapper
2type Serializer<T> = {
3 serialize: (v: T) => string;
4 deserialize: (s: string) => T;
5};
6
7class TypedLocalStorage<K extends string, V> {
8 constructor(private storage: Storage, private serializer: Serializer<V>) {}
9
10 set(key: K, value: V): void {
11 this.storage.setItem(key, this.serializer.serialize(value));
12 }
13
14 get(key: K): V | null {
15 const raw = this.storage.getItem(key);
16 if (raw === null) return null;
17 try {
18 return this.serializer.deserialize(raw);
19 } catch {
20 return null;
21 }
22 }
23
24 remove(key: K): void {
25 this.storage.removeItem(key);
26 }
27
28 clear(): void {
29 this.storage.clear();
30 }
31}
32
33// Example usage
34const jsonSerializer: Serializer<any> = {
35 serialize: v => JSON.stringify(v),
36 deserialize: s => JSON.parse(s),
37};
38
39const appStorage = new TypedLocalStorage<'theme' | 'token', any>(localStorage, jsonSerializer);
40appStorage.set('theme', { dark: true });
41console.log(appStorage.get('theme'));- 이 클래스는 잘못된 타입으로 데이터를 저장하거나 가져올 위험을 줄여줍니다.
실용적인 고려사항 및 모범 사례
-
브라우저가 StorageManager를 지원하는지 항상 확인하세요 StorageManager는 비교적 새로운 API이므로 사용 전에 항상 존재 여부를 확인해야 합니다.
-
스토리지 한도 및 호환성에 주의하세요
estimate()로 반환되는 값은 브라우저마다 다르며 추정치일 뿐입니다. -
비동기 API를 고려하여 설계하기
estimate()와persist()는 Promise 기반의 비동기 API입니다. UI가 차단되지 않도록 코드를 설계하세요. -
보안 고려사항
localStorage에 액세스 토큰과 같은 민감한 정보를 저장하지 마세요. 가능하다면 HttpOnly 쿠키나 암호화를 사용하는 것을 고려하세요. -
폴백 설계
supportsStorageManager()가 false를 반환하는 경우localStorage또는IndexedDB만 사용하는 로직으로 전환하세요.
요약
supportsStorageManager()로 기능 감지를 구현하면 모든 환경을 안전하게 지원할 수 있고, navigator.storage.estimate()로 사용량과 쿼터를 확인하며, persist()로 지속성을 요청할 수 있습니다. TypeScript에서는 래퍼 함수와 타입 정의를 사용하여 읽기 쉽고 안전한 코드를 작성하세요. 기본 흐름은 존재 여부 확인, 용량 추정, 지속성 요청, 저장 전 용량 확인 순입니다.
위의 기사를 보면서 Visual Studio Code를 사용해 우리 유튜브 채널에서 함께 따라할 수 있습니다. 유튜브 채널도 확인해 주세요.