Skip to content

useStoreManager API ​

useStoreManager 훅은 선언적 스토어 패턴에서 고급 스토어 관리 시나리오를 위한 내부 StoreManager 인스턴스에 대한 저레벨 접근을 제공합니다.

사전 요구사항 ​

기본 스토어 설정과 컨텍스트 생성은 기본 스토어 설정 을 참조하세요.

이 문서는 스토어 설정을 사용한 API 사용법을 보여줍니다:

기본 사용법 ​

스토어 매니저 가져오기 ​

tsx
import { createStoreContext, useStoreValue } from '@context-action/react';

// 설정 가이드에서 확립된 스토어 패턴 사용
const {
  Provider: UserStoreProvider,
  useStore: useUserStore,
  useStoreManager: useUserStoreManager
} = createStoreContext('User', {
  profile: {
    initialValue: { id: '', name: '', email: '', role: 'guest' as const },
    strategy: 'shallow' as const
  },
  preferences: {
    initialValue: { theme: 'light' as const, language: 'en', notifications: true },
    strategy: 'shallow' as const
  },
  session: {
    initialValue: { isAuthenticated: false, permissions: [], lastActivity: 0 },
    strategy: 'shallow' as const
  }
});

function MyComponent() {
  const manager = useUserStoreManager();
  
  // 설정 패턴을 사용하여 스토어 인스턴스를 직접 가져오기
  const profileStore = manager.getStore('profile');
  const preferencesStore = manager.getStore('preferences');
  
  // 여기서 컴포넌트 로직
}

스토어 작업 ​

tsx
function UserManager() {
  const manager = useUserStoreManager();
  const profileStore = useUserStore('profile');
  const profile = useStoreValue(profileStore);
  
  const updateUserName = (newName: string) => {
    const profileStore = manager.getStore('profile');
    const currentProfile = profileStore.getValue();
    profileStore.setValue({ ...currentProfile, name: newName });
  };
  
  const updateUserEmail = (newEmail: string) => {
    const profileStore = manager.getStore('profile');
    profileStore.update(current => ({ ...current, email: newEmail }));
  };
  
  return (
    <div>
      <input 
        value={profile.name}
        onChange={e => updateUserName(e.target.value)}
      />
      <input 
        value={profile.email}
        onChange={e => updateUserEmail(e.target.value)}
      />
    </div>
  );
}

API 참조 ​

manager.getStore(storeName) ​

이름으로 타입이 지정된 스토어 인스턴스를 가져옵니다. 이것은 스토어에 접근하는 주요 메소드입니다.

tsx
const manager = useUserStoreManager();

// 완전한 타입 안전성을 가진 스토어 인스턴스 가져오기
const profileStore = manager.getStore('profile');     // Store<UserProfile>
const preferencesStore = manager.getStore('preferences'); // Store<UserPreferences>
const sessionStore = manager.getStore('session');     // Store<UserSession>

// 스토어 메소드 직접 사용
const currentProfile = profileStore.getValue();
profileStore.setValue(newProfile);
profileStore.update(profile => ({ ...profile, name: 'John' }));

스토어 인스턴스 메소드 ​

스토어 인스턴스를 가져온 후에는 다음 메소드를 사용할 수 있습니다:

tsx
const profileStore = manager.getStore('profile');

// 현재 값 가져오기
const currentProfile = profileStore.getValue();

// 새 값을 직접 설정
profileStore.setValue({ id: '123', name: 'John', email: 'john@example.com', role: 'user' });

// 함수로 업데이트
profileStore.update(current => ({
  ...current,
  name: 'John Doe'
}));

// 변경사항 구독
const unsubscribe = profileStore.subscribe((newValue, previousValue) => {
  console.log('프로필 변경됨:', { newValue, previousValue });
});

// 초기값으로 재설정
profileStore.reset();

매니저 유틸리티 메소드 ​

tsx
const manager = useUserStoreManager();

// 매니저 정보 가져오기
const info = manager.getInfo();
console.log(info); // { name: 'User', storeCount: 3, availableStores: ['profile', 'preferences', 'session'] }

// 모든 스토어 지우기 (고급 사용 사례)
manager.clear();

고급 패턴 ​

벌크 스토어 작업 ​

tsx
function BulkOperations() {
  const manager = useUserStoreManager();
  
  const handleBulkUpdate = async () => {
    // 순서대로 여러 스토어 업데이트
    const profileStore = manager.getStore('profile');
    const preferencesStore = manager.getStore('preferences');
    const sessionStore = manager.getStore('session');
    
    profileStore.setValue({ id: '123', name: 'John Doe', email: 'john@example.com', role: 'user' });
    preferencesStore.update(current => ({ ...current, theme: 'dark' }));
    sessionStore.update(current => ({ 
      ...current, 
      isAuthenticated: true,
      lastActivity: Date.now()
    }));
  };
  
  const handleResetAll = () => {
    // 모든 스토어를 초기값으로 재설정
    const profileStore = manager.getStore('profile');
    const preferencesStore = manager.getStore('preferences');
    const sessionStore = manager.getStore('session');
    
    profileStore.reset();
    preferencesStore.reset();
    sessionStore.reset();
  };
  
  return (
    <div>
      <button onClick={handleBulkUpdate}>모두 업데이트</button>
      <button onClick={handleResetAll}>모두 재설정</button>
    </div>
  );
}

조건부 스토어 업데이트 ​

tsx
function ConditionalUpdates() {
  const manager = useUserStoreManager();
  
  const updateProfileIfValid = (newProfile: UserProfile) => {
    const profileStore = manager.getStore('profile');
    const currentProfile = profileStore.getValue();
    
    // 프로필이 다른 경우에만 업데이트
    if (JSON.stringify(currentProfile) !== JSON.stringify(newProfile)) {
      profileStore.setValue(newProfile);
    }
  };
  
  const updatePreferencesIfAllowed = (newPreferences: UserPreferences) => {
    const profileStore = manager.getStore('profile');
    const preferencesStore = manager.getStore('preferences');
    const profile = profileStore.getValue();
    
    // 사용자가 권한을 가진 경우에만 업데이트
    if (profile.role === 'admin') {
      preferencesStore.setValue(newPreferences);
    }
  };
  
  return (
    <div>
      {/* 컴포넌트 JSX */}
    </div>
  );
}

검증이 있는 스토어 매니저 ​

tsx
function ValidatedStoreManager() {
  const manager = useUserStoreManager();
  
  const updateProfileWithValidation = (updates: Partial<UserProfile>) => {
    const profileStore = manager.getStore('profile');
    const currentProfile = profileStore.getValue();
    const newProfile = { ...currentProfile, ...updates };
    
    // 업데이트 전 검증
    if (isValidProfile(newProfile)) {
      profileStore.setValue(newProfile);
      return { success: true };
    } else {
      return { success: false, error: '유효하지 않은 프로필 데이터' };
    }
  };
  
  const updatePreferencesWithDefaults = (preferences: Partial<UserPreferences>) => {
    const preferencesStore = manager.getStore('preferences');
    preferencesStore.update(current => ({
      // 먼저 기본값 적용
      theme: 'light',
      notifications: true,
      language: 'en',
      // 그 다음 업데이트 적용
      ...current,
      ...preferences
    }));
  };
  
  return (
    <div>
      {/* 컴포넌트 JSX */}
    </div>
  );
}

액션과의 통합 ​

스토어 매니저는 복잡한 비즈니스 로직을 위해 액션 컨텍스트와 완벽하게 작동합니다:

tsx
// 스토어 매니저를 사용한 액션 핸들러
function UserActions() {
  const manager = useUserStoreManager();
  
  useEventActionHandler('updateUserProfile', async (payload) => {
    const profileStore = manager.getStore('profile');
    const preferencesStore = manager.getStore('preferences');
    const currentProfile = profileStore.getValue();
    
    // 비즈니스 로직
    const updatedProfile = await processUserUpdate(currentProfile, payload);
    
    // 스토어 업데이트
    profileStore.setValue(updatedProfile);
    preferencesStore.update(preferences => ({
      ...preferences,
      language: updatedProfile.role === 'admin' ? 'en' : preferences.language
    }));
  });
  
  return null; // 이것은 로직 전용 컴포넌트
}

성능 고려사항 ​

배치 업데이트 ​

tsx
function OptimizedUpdates() {
  const manager = useUserStoreManager();
  
  const handleBatchUpdate = () => {
    // 이러한 업데이트는 순서대로 발생하지만 최적화됨
    React.unstable_batchedUpdates(() => {
      const profileStore = manager.getStore('profile');
      const preferencesStore = manager.getStore('preferences');
      const sessionStore = manager.getStore('session');
      
      profileStore.setValue(newProfile);
      preferencesStore.setValue(newPreferences);
      sessionStore.setValue(newSession);
    });
  };
  
  return (
    <button onClick={handleBatchUpdate}>
      여러 스토어 업데이트
    </button>
  );
}

메모이제이션된 업데이트 ​

tsx
function MemoizedUpdates() {
  const manager = useUserStoreManager();
  
  const updateProfileMemoized = useCallback((updates: Partial<UserProfile>) => {
    const profileStore = manager.getStore('profile');
    profileStore.update(current => ({ ...current, ...updates }));
  }, [manager]);
  
  const resetProfileMemoized = useCallback(() => {
    const profileStore = manager.getStore('profile');
    profileStore.reset();
  }, [manager]);
  
  return (
    <UserForm 
      onUpdate={updateProfileMemoized}
      onReset={resetProfileMemoized}
    />
  );
}

오류 처리 ​

안전한 스토어 작업 ​

tsx
function SafeStoreManager() {
  const manager = useUserStoreManager();
  
  const safeUpdateStore = <K extends keyof UserStores>(
    storeName: K, 
    value: UserStores[K]
  ) => {
    try {
      const store = manager.getStore(storeName);
      store.setValue(value);
      return { success: true };
    } catch (error) {
      console.error(`스토어 ${String(storeName)} 업데이트 실패:`, error);
      return { success: false, error };
    }
  };
  
  const safeGetStoreValue = <K extends keyof UserStores>(storeName: K) => {
    try {
      const store = manager.getStore(storeName);
      return { success: true, value: store.getValue() };
    } catch (error) {
      console.error(`스토어 ${String(storeName)} 가져오기 실패:`, error);
      return { success: false, error };
    }
  };
  
  return (
    <div>
      {/* 컴포넌트 JSX */}
    </div>
  );
}

TypeScript 지원 ​

스토어 매니저는 완전한 타입 안전성을 제공합니다:

tsx
interface UserProfile {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
}

interface UserPreferences {
  theme: 'light' | 'dark';
  language: 'en' | 'ko' | 'ja' | 'zh';
  notifications: boolean;
}

interface UserSession {
  isAuthenticated: boolean;
  permissions: string[];
  lastActivity: number;
}

const {
  useStoreManager: useUserStoreManager
} = createStoreContext('User', {
  profile: {
    initialValue: { id: '', name: '', email: '', role: 'guest' as const },
    strategy: 'shallow' as const
  },
  preferences: {
    initialValue: { theme: 'light' as const, language: 'en', notifications: true },
    strategy: 'shallow' as const
  },
  session: {
    initialValue: { isAuthenticated: false, permissions: [], lastActivity: 0 },
    strategy: 'shallow' as const
  }
});

function TypeSafeComponent() {
  const manager = useUserStoreManager();
  
  // TypeScript는 각 스토어의 정확한 타입을 알고 있음
  const updateProfile = (profile: UserProfile) => {
    const profileStore = manager.getStore('profile'); // Store<UserProfile> 반환
    profileStore.setValue(profile); // ✅ 타입 안전
    // profileStore.setValue('invalid'); // ❌ TypeScript 오류
  };
  
  const getProfileData = () => {
    const profileStore = manager.getStore('profile'); // Store<UserProfile> 반환
    return profileStore.getValue(); // UserProfile 반환
  };
  
  return <div>{/* 컴포넌트 JSX */}</div>;
}

모범 사례 ​

1. 복잡한 상태에 함수형 업데이트 사용 ​

tsx
const manager = useUserStoreManager();

// ✅ 좋음: 함수형 업데이트
const sessionStore = manager.getStore('session');
sessionStore.update(session => ({
  ...session,
  permissions: session.permissions.includes('read') 
    ? session.permissions 
    : [...session.permissions, 'read']
}));

// ❌ 피해야 함: 현재 상태를 별도로 읽기
const session = sessionStore.getValue();
sessionStore.setValue({
  ...session,
  permissions: [...session.permissions, 'read']
});

2. 성능을 위해 useCallback과 결합 ​

tsx
const manager = useUserStoreManager();

const updateProfileName = useCallback((name: string) => {
  const profileStore = manager.getStore('profile');
  profileStore.update(profile => ({ ...profile, name }));
}, [manager]);

3. 관련 업데이트에 스토어 매니저 사용 ​

tsx
const manager = useUserStoreManager();

const handleUserLogin = useCallback(async (credentials) => {
  const user = await login(credentials);
  
  // 관련 스토어를 함께 업데이트
  const profileStore = manager.getStore('profile');
  const sessionStore = manager.getStore('session');
  
  profileStore.setValue({ id: user.id, name: user.name, email: user.email, role: user.role });
  sessionStore.update(session => ({
    ...session,
    isAuthenticated: true,
    lastActivity: Date.now(),
    permissions: user.permissions
  }));
}, [manager]);

4. useStore 훅보다 직접 스토어 접근을 선호 ​

tsx
// ✅ 좋음: 여러 작업에 매니저 사용
const manager = useUserStoreManager();
const profileStore = manager.getStore('profile');
const preferencesStore = manager.getStore('preferences');

// 효율적으로 여러 작업 수행
profileStore.setValue(newProfile);
preferencesStore.update(preferences => ({ ...preferences, language: 'en' }));

// ❌ 덜 효율적: 여러 훅 호출
const profileStore = useUserStore('profile');
const preferencesStore = useUserStore('preferences');

실제 예제 ​

스토어 매니저 사용 시기 ​

스토어 매니저를 사용해야 하는 경우: ​

  • 여러 스토어 작업: 하나의 함수에서 여러 스토어를 업데이트해야 할 때
  • 고급 스토어 로직: 직접 스토어 접근이 필요한 복잡한 상태 조작
  • 성능 최적화: 배치 작업이나 여러 훅 호출 피하기
  • 액션 핸들러: 여러 스토어에 걸친 비즈니스 로직
  • 커스텀 스토어 유틸리티: 재사용 가능한 스토어 조작 함수 구축

일반 훅을 사용해야 하는 경우: ​

  • 간단한 상태 접근: 단일 스토어 읽기나 업데이트만
  • 컴포넌트 렌더링: 반응형 UI 업데이트를 위한 useStoreValue 사용
  • 기본 작업: 간단한 setValue/getValue 작업

관련 문서 ​

Released under the Apache-2.0 License.