웹 애플리케이션 상태 저장 문제 해결: Flowgram.ai의 이중 엔진 솔루션 분석

웹 애플리케이션 상태 저장 문제 해결: Flowgram.ai의 이중 엔진 솔루션 분석

웹 애플리케이션에서 사용자가 편집 중인 데이터가 갑자기 사라지는 문제에 시달리고 계신가요? 사용자가 절반만 완성한 다이어그램이 예기치 않게 새로고침 후 사라지는 경험을 하셨나요? 본 기사에서는 Flowgram.ai가 localStorage 기본 저장소와 HistoryStorage 전문 엔진의 이중 솔루션을 통해 프론트엔드 상태 저장 문제를 어떻게 해결하는지 심층적으로 분석합니다. 이 글을 통해 두 가지 저장 방식의 기술적 구현, 성능 비교 및 Flowgram.ai에서의 최적 활용법을 마스터할 수 있습니다.

상태 저장을 위한 두 가지 기술적 접근법

Flowgram.ai는 다양한 시나리오 요구사항에 대응하기 위해 두 가지 보완적인 상태 저장 솔루션을 설계한 계층형 저장 아키텍처를 채택했습니다:

1. 가벼운 저장소: localStorage 구현

LocalStorageService는 간단한 데이터 영속화에 적합한 기본 키-값 저장 기능을 제공합니다. 핵심 구현은 packages/canvas-engine/core/src/services/storage-service.ts에 위치하며, 주요 코드는 다음과 같습니다:

@injectable()
export class LocalStorageService implements StorageService {
  private storage: LocalStorage;
  private _prefix = '__gedit:';

  setData<T>(key: string, data: T): void {
    this.storage[this.prefix(key)] = JSON.stringify(data);
  }

  getData<T>(key: string, defaultValue?: T): T {
    const result = this.storage[this.prefix(key)];
    if (result === undefined) {
      return defaultValue as any;
    }
    return JSON.parse(result);
  }

  @postConstruct()
  protected init(): void {
    if (typeof window !== 'undefined' && window.localStorage) {
      this.storage = window.localStorage;
    } else {
      this.storage = {};
    }
  }
}

이 구현은 키 이름 충돌을 방지하기 위한 접두사 메커니즘을 통해 브라우저 환경과의 호환성을 처리하며, 서버 사이드 렌더링 시나리오에서도 안전하게 실행될 수 있도록 보장합니다.

2. 전문 엔진: HistoryStorage 아키텍처

복잡한 작업 기록의 경우, Flowgram.ai는 IndexedDB 기반의 HistoryStorage 엔진을 설계했습니다. 전체 구현은 packages/common/history-storage/ 디렉토리에 위치하며, 핵심 구성 요소는 다음과 같습니다:

  • HistoryStorageManager: 기록과 작업의 저장 로직을 조정
  • HistoryDatabase: Dexie 기반 IndexedDB 래퍼
  • 타입 정의: 기록 데이터 구조의 표준화

HistoryStorage 심층 분석

데이터 모델 설계

HistoryStorage는 구조화된 데이터 모델을 채택하여 작업 기록을 HistoryRecordHistoryOperationRecord 두 가지 핵심 엔티티로 분리합니다. packages/common/history-storage/src/types.ts에 정의되어 있습니다:

export interface HistoryRecord {
  uuid: string;
  timestamp: number;
  type: string;
  resourceURI: string;
  resourceJSON: string;
}

export interface HistoryOperationRecord {
  uuid: string;
  type: string;
  timestamp: number;
  label: string;
  uri: string;
  resourceURI: string;
  description: string;
  value: string;
  historyId: string;
}

이 설계는 기록과 작업 세부 정보의 연관 저장을 구현하여 복잡한 되돌리기 및 실행 취소 작업을 지원합니다.

저장 흐름 제어

HistoryStorageManager는 기록 스택 변경 이벤트를 수신하여 자동 영속화를 구현합니다. packages/common/history-storage/src/history-storage-manager.ts의 핵심 로직은 다음과 같습니다:

this._toDispose.push(
  this.historyManager.historyStack.onChange(event => {
    if (event.type === HistoryStackChangeType.ADD) {
      const [history, operations] = this.historyItemToRecord(event.service, event.value);
      this.db.addHistoryRecord(history, operations).catch(console.error);
    }
    
    if ([HistoryStackChangeType.ADD_OPERATION, HistoryStackChangeType.UPDATE_OPERATION].includes(event.type)) {
      // 스냅샷 업데이트 로직
      this.db.updateHistoryByUUID(historyItem.id, {
        resourceJSON: service.getSnapshot() || ''
      }).catch(console.error);
    }
  })
);

데이터베이스 최적화 전략

HistoryDatabase는 여러 저장 최적화 메커니즘을 구현합니다:

  1. 용량 제어: resourceStorageLimit를 통해 단일 리소스의 최대 기록 수를 제한
  2. 트랜잭션 지원: 기록과 작업의 원자성 저장 보장
  3. 인덱스 설계: 자주 사용되는 쿼리 필드에 인덱스 생성으로 성능 향상

핵심 구현은 packages/common/history-storage/src/history-database.ts의 addHistoryRecord 메서드에 있습니다:

async addHistoryRecord(history: HistoryRecord, operations: HistoryOperationRecord[]) {
  return this.transaction('rw', this.history, this.operation, async () => {
    const count = await this.history.where({ resourceURI: history.resourceURI }).count();
    if (count >= this.resourceStorageLimit) {
      // 제한을 초과할 경우 가장 오래된 기록 삭제
      const limit = count - this.resourceStorageLimit;
      const items = await this.history.where({ resourceURI: history.resourceURI }).limit(limit).toArray();
      // 일괄 삭제 로직
    }
    return Promise.all([this.history.add(history), this.operation.bulkAdd(operations)]);
  });
}

두 가지 솔루션의 기술적 비교

특성 localStorage 솔루션 HistoryStorage 솔루션
저장 용량 일반적으로 5MB 이론적 무제한 (하드디스크 공간 제한)
데이터 구조 간단한 키-값 쌍 구조화된 데이터 모델
쿼리 능력 키 조회만 지원 다중 조건 복잡 쿼리 지원
성능 소규모 데이터 효율적 대규모 데이터에서 더 우수
적용 시나리오 설정, 선호도 설정 작업 기록, 복잡 상태
구현 복잡도 간단 중간-높음

Flowgram.ai에서의 최적 활용법

1. 계층형 저장 전략

Flowgram.ai는 데이터 특성에 따라 적절한 저장 방식을 선택합니다:

  • UI 상태 및 설정: LocalStorageService를 사용하여 저장, 예: packages/plugins/free-container-plugin/src/sub-canvas/components/tips/global-store.ts의 팁 상태 기억
  • 편집 기록 및 스냅샷: HistoryStorage를 사용하여 복잡한 작업의 되돌리기 지원

2. 저장 보안 메커니즘

시스템은 다중 보안 메커니즘을 구현합니다:

  • 데이터 자동 백업: 중요 작업 실시간 기록
  • 버전 제어: 기록 상태의 정확한 복원 지원
  • 예외 처리: 완전한 오류 캐치 및 로그 기록

3. 성능 최적화 제안

실제 사용 시 다음을 권장합니다:

  • 자주 변경되는 대규모 상태에는 HistoryStorage 사용
  • 간단한 설정 항목에는 localStorage 사용
  • 더 이상 필요 없는 기록 데이터 주기적 정리

결론 및 확장

Flowgram.ai의 이중 엔진 저장 솔루션은 웹 애플리케이션 상태 저장 문제에 대한 완전한 해결책을 제공합니다. LocalStorageService는 간단한 시나리오 요구사항을 충족시키며, HistoryStorage 엔진은 복잡한 편집 시나리오에 전문적인 지원을 제공합니다. 개발자는 다음과 같은 방식으로 저장 기능을 확장할 수 있습니다:

  1. HistoryDatabase에 암호화 저장 지원 추가
  2. 클라우드 동기화 어댑터 구현
  3. 저장 전략 동적 전환 메커니즘 개발

전체 구현 코드는 다음을 참조하세요:

  • 기본 저장소: packages/canvas-engine/core/src/services/storage-service.ts
  • 기록 저장소: packages/common/history-storage/

태그: localStorage IndexedDB Dexie state persistence Web Development

7월 29일 08:47에 게시됨