ilokesto

미들웨어 소개

@ilokesto/state/middleware는 @ilokesto/store middleware를 더 쉽게 붙이기 위한 작은 helper 모음입니다. 각 helper는 등록된 pipe middleware를 반환합니다. 반환값을 pipe.use(...)에 넘기고, middleware가 더 있으면 .use()를 이어 붙인 뒤 .create(initialState)로 plain state에서 store를 만드세요.

Public composition API는 builder-only입니다. pipe는 호출할 수 없으며 middleware helper는 initialState나 기존 Store<T>를 즉시 받는 생성 mode를 제공하지 않습니다.

미들웨어가 실행되는 시점

middleware는 underlying store update pipeline 안에서 최종 state가 적용되기 전에 실행됩니다. reducer adapter도 같은 store middleware pipeline을 사용합니다. dispatched action은 next state로 변환된 뒤 나머지 update 흐름을 계속 통과합니다.

pipe로 조합하기

import { jsonStorage, logger, persist, validate } from '@ilokesto/state/middleware';
import { pipe } from '@ilokesto/state/utils';

const schema = {
  '~standard': {
    version: 1,
    vendor: 'app',
    validate(value: unknown) {
      return typeof value === 'object' && value !== null
        && 'count' in value && typeof value.count === 'number'
        ? { value: { count: value.count } }
        : { issues: [{ message: 'State must contain a numeric count' }] };
    },
  },
} as const;

const decodeCounter = (value: unknown): { count: number } | null => {
  const result = schema['~standard'].validate(value);
  return 'value' in result ? result.value : null;
};

const store = pipe
  .use(validate(schema))
  .use(logger({ collapsed: true, diff: true }))
  .use(persist({ key: 'counter', storage: jsonStorage(() => window.localStorage), decode: decodeCounter }))
  .create({ count: 0 });

await store.persist.rehydrate();

제공되는 미들웨어

Middleware쓰는 상황핵심 주의점
logger개발 중 state update 로그production에서는 비활성화되며 diff는 debug output입니다.
validatesynchronous Standard Schema validationasync schema 결과는 거부됩니다.
debounce빠른 update 지연 및 병합timer 전 read는 이전 state를 볼 수 있습니다.
history동기 변경의 undo와 redodebounce 또는 throttle과 같은 체인을 쓸 수 없습니다.
throttleleading-edge rate limitingwait window 중 update는 드롭됩니다.
devtoolsRedux DevTools inspectionbrowser extension에 의존하며 production에서는 비활성화됩니다.
persist저장소 팩토리를 통한 비동기 영속화명시적으로 복원하고 저장 보장이 필요하면 flush()를 기다립니다.
dispose(store)middleware 소유 resource 해제해당 store가 더 이상 필요 없을 때 호출합니다.

순서 잡는 법

허용된 업데이트만 기록하려면 logger를 validation 뒤에 두세요. 영속화는 실제 commit을 관찰하며 거절된 업데이트는 저장하지 않고 지연된 commit은 발생 시 저장합니다. debounce는 persist보다 먼저 선언하세요. 반대 순서는 계속 MIDDLEWARE_ORDER로 거부됩니다. 복원은 검증 후 debounce나 throttle을 통과하지 않고 즉시 commit합니다.

history()는 성공한 동기 commit만 기록하므로 선언 순서와 관계없이 debounce() 또는 throttle()과 같은 pipe 체인을 쓸 수 없습니다. 이 조합은 MIDDLEWARE_CONFLICT로 거부되며 pipe가 유효하게 만들려고 chain을 재정렬하지 않습니다.

Store 정리

Timer 기반 middleware와 외부 subscription을 가진 middleware는 자신이 준비한 store에 cleanup을 등록합니다. 해당 store가 더 이상 필요 없으면 @ilokesto/state/middleware의 dispose(store)를 호출하세요. 대기 중인 debounce/throttle 작업을 취소하고 DevTools subscription 같은 middleware 소유 resource를 해제합니다. disposal은 consumer를 unsubscribe하거나 Store를 무효화하지 않습니다. 전달한 store에만 적용되고 여러 번 안전하게 호출할 수 있으며, 나중에 새 cleanup이 등록된 경우에만 이후 호출에서 실행합니다.

Disposal은 cleanup 하나가 throw해도 등록된 모든 cleanup을 시도합니다. 하나 이상의 cleanup이 throw하면 dispose는 원래 error 또는 다른 thrown value가 errors에 담긴 AggregateError를 다시 throw합니다.

영속화 정리 후 해당 스토어의 영속화 제어는 종료 상태가 됩니다. 같은 팩토리를 다른 스토어가 사용해도 해당 스토어가 소유한 어댑터 인스턴스만 정리합니다. 저장 완료가 필요하면 정리 전에 store.persist.flush()를 기다리세요. 정리만으로 저장은 보장되지 않습니다.

목차