조건부 Await 패턴
useWaitForRefs의 핵심 동작으로, 조건에 따라 대기하거나 즉시 반환하는 패턴입니다.
전제조건
조건부 await 패턴을 구현하기 전에 적절한 Context-Action 프레임워크 설정이 필요합니다:
필수 설정 가이드
Import
typescript
import { createRefContext } from '@context-action/react';
import { createActionContext, ActionPayloadMap } from '@context-action/react';
import { createStoreContext, useStoreValue } from '@context-action/react';RefContext 설정
typescript
// UI 요소 참조 정의
interface UIRefs {
targetElement: HTMLElement;
setupModal: HTMLDialogElement;
dataContainer: HTMLElement;
betaPanel: HTMLElement;
standardPanel: HTMLElement;
authModal: HTMLDialogElement;
welcomeScreen: HTMLElement;
coreInterface: HTMLElement;
advancedControls: HTMLElement;
animationCanvas: HTMLCanvasElement;
sidebar: HTMLElement;
toolbar: HTMLElement;
statusBar: HTMLElement;
}
const {
Provider: UIRefProvider,
useRefHandler: useUIRef,
useWaitForRefs: useWaitForRefs
} = createRefContext<UIRefs>('UI');Action Context 설정
typescript
// 조건부 await 작업을 위한 액션
interface ConditionalActions extends ActionPayloadMap {
handleClick: void;
handleAction: void;
smartWaitHandler: void;
featureBasedWait: void;
progressiveWait: void;
}
const {
Provider: ConditionalActionProvider,
useActionDispatch: useConditionalDispatch,
useActionHandler: useConditionalHandler
} = createActionContext<ConditionalActions>('Conditional');Store 설정
typescript
// 조건부 로직을 위한 상태 스토어
const {
Provider: AppStateProvider,
useStore: useAppStateStore
} = createStoreContext('AppState', {
isReady: { initialValue: false },
needsSetup: { initialValue: true },
dataLoaded: { initialValue: false }
});
const {
Provider: UserStateProvider,
useStore: useUserStateStore
} = createStoreContext('UserState', {
isLoggedIn: { initialValue: false }
});
const {
Provider: FeatureProvider,
useStore: useFeatureStore
} = createStoreContext('Feature', {
betaUI: { initialValue: false }
});
const {
Provider: CapabilityProvider,
useStore: useCapabilityStore
} = createStoreContext('Capability', {
hasAdvancedFeatures: { initialValue: false },
hasAnimations: { initialValue: true }
});
const {
Provider: PreferencesProvider,
useStore: usePreferencesStore
} = createStoreContext('Preferences', {
showSidebar: { initialValue: true },
showToolbar: { initialValue: true },
showStatusBar: { initialValue: false }
});Provider 설정
typescript
function App() {
return (
<UIRefProvider>
<ConditionalActionProvider>
<AppStateProvider>
<UserStateProvider>
<FeatureProvider>
<CapabilityProvider>
<PreferencesProvider>
<YourComponent />
</PreferencesProvider>
</CapabilityProvider>
</FeatureProvider>
</UserStateProvider>
</AppStateProvider>
</ConditionalActionProvider>
</UIRefProvider>
);
}기본 패턴
typescript
function ConditionalAwaitComponent() {
const waitForRefs = useWaitForRefs();
const performAction = useCallback(async () => {
// 조건에 따라 대기하거나 즉시 반환
await waitForRefs('targetElement');
// 예상 동작:
// - 마운트되지 않은 경우: 요소가 마운트될 때까지 대기
// - 마운트된 경우: 즉시 반환
console.log('요소를 이제 안전하게 사용할 수 있습니다');
}, [waitForRefs]);
return (
<div>
<button onClick={performAction}>액션 수행</button>
<div ref={useUIRef('targetElement')}>타겟 요소</div>
</div>
);
}사용 사례
액션 핸들러와 함께 사용하는 간단한 대기
typescript
function SimpleWaitComponent() {
const waitForRefs = useWaitForRefs();
useConditionalHandler('handleClick', useCallback(async () => {
await waitForRefs('targetElement');
console.log('요소를 이제 사용할 수 있습니다');
}, [waitForRefs]));
const dispatch = useConditionalDispatch();
return (
<div>
<button onClick={() => dispatch('handleClick')}>간단한 대기</button>
<div ref={useUIRef('targetElement')}>타겟 요소</div>
</div>
);
}스토어 접근을 이용한 조건부 로직
typescript
function ConditionalLogicComponent() {
const waitForRefs = useWaitForRefs();
const appStateStore = useAppStateStore('isReady');
useConditionalHandler('handleAction', useCallback(async () => {
const currentState = appStateStore.getValue();
if (!currentState) {
await waitForRefs('targetElement');
}
// 액션 진행
console.log('액션이 완료되었습니다');
}, [waitForRefs, appStateStore]));
const dispatch = useConditionalDispatch();
return (
<div>
<button onClick={() => dispatch('handleAction')}>조건부 액션</button>
<div ref={useUIRef('targetElement')}>타겟 요소</div>
</div>
);
}고급 조건부 패턴
상태 기반 조건부 대기
typescript
function StateBasedComponent() {
const waitForRefs = useWaitForRefs();
const appStateStore = useAppStateStore('needsSetup');
const userStateStore = useUserStateStore('isLoggedIn');
const dataLoadedStore = useAppStateStore('dataLoaded');
useConditionalHandler('smartWaitHandler', useCallback(async () => {
const appNeedsSetup = appStateStore.getValue();
const userLoggedIn = userStateStore.getValue();
const dataLoaded = dataLoadedStore.getValue();
// 조건이 필요한 경우에만 대기
if (appNeedsSetup && !userLoggedIn) {
await waitForRefs('setupModal');
}
if (userLoggedIn && !dataLoaded) {
await waitForRefs('dataContainer');
}
// 작업 진행
console.log('스마트 대기 작업이 완료되었습니다');
}, [waitForRefs, appStateStore, userStateStore, dataLoadedStore]));
const dispatch = useConditionalDispatch();
return (
<div>
<button onClick={() => dispatch('smartWaitHandler')}>스마트 대기</button>
<div ref={useUIRef('setupModal')}>설정 모달</div>
<div ref={useUIRef('dataContainer')}>데이터 컨테이너</div>
</div>
);
}기능 플래그 조건부 대기
typescript
function FeatureFlagComponent() {
const waitForRefs = useWaitForRefs();
const featureStore = useFeatureStore('betaUI');
useConditionalHandler('featureBasedWait', useCallback(async () => {
const betaUIEnabled = featureStore.getValue();
if (betaUIEnabled) {
// 베타 UI 요소 대기
await waitForRefs('betaPanel');
} else {
// 표준 UI 요소 대기
await waitForRefs('standardPanel');
}
// 조건부 대기 후 공통 로직
console.log('인터페이스가 초기화되었습니다');
}, [waitForRefs, featureStore]));
const dispatch = useConditionalDispatch();
const betaUI = useStoreValue(featureStore);
return (
<div>
<button onClick={() => dispatch('featureBasedWait')}>인터페이스 초기화</button>
{betaUI ? (
<div ref={useUIRef('betaPanel')}>베타 패널</div>
) : (
<div ref={useUIRef('standardPanel')}>표준 패널</div>
)}
</div>
);
}점진적 향상 패턴
typescript
function ProgressiveEnhancementComponent() {
const waitForRefs = useWaitForRefs();
const advancedFeaturesStore = useCapabilityStore('hasAdvancedFeatures');
const animationsStore = useCapabilityStore('hasAnimations');
useConditionalHandler('progressiveWait', useCallback(async () => {
// 필수 요소는 항상 대기
await waitForRefs('coreInterface');
const hasAdvanced = advancedFeaturesStore.getValue();
const hasAnimations = animationsStore.getValue();
// 향상된 기능을 조건부로 대기
if (hasAdvanced) {
await waitForRefs('advancedControls');
}
if (hasAnimations) {
await waitForRefs('animationCanvas');
}
// 사용 가능한 기능으로 초기화
console.log('점진적 향상이 완료되었습니다', { hasAdvanced, hasAnimations });
}, [waitForRefs, advancedFeaturesStore, animationsStore]));
const dispatch = useConditionalDispatch();
const hasAdvanced = useStoreValue(advancedFeaturesStore);
const hasAnimations = useStoreValue(animationsStore);
return (
<div>
<button onClick={() => dispatch('progressiveWait')}>점진적 초기화</button>
<div ref={useUIRef('coreInterface')}>핵심 인터페이스</div>
{hasAdvanced && <div ref={useUIRef('advancedControls')}>고급 컨트롤</div>}
{hasAnimations && <canvas ref={useUIRef('animationCanvas')}>애니메이션 캔버스</canvas>}
</div>
);
}조건부 Await를 이용한 에러 처리
typescript
function ErrorHandlingComponent() {
const waitForRefs = useWaitForRefs();
const authModalRef = useUIRef('authModal');
// 설정 스토어 설정 (예시를 위한 단순화)
const [config] = useState({
requiresAuth: true,
showWelcome: true
});
const safeConditionalWait = useCallback(async () => {
try {
if (config.requiresAuth) {
await waitForRefs('authModal');
const authElement = authModalRef.target;
if (!authElement) {
throw new Error('인증 모달을 사용할 수 없습니다');
}
}
if (config.showWelcome) {
await waitForRefs('welcomeScreen');
}
// 메인 로직 계속
console.log('안전한 조건부 대기가 완료되었습니다');
} catch (error) {
console.warn('조건부 대기 실패, 대안 사용:', error);
// 대기 없이 대안 로직
console.log('대안 모드가 초기화되었습니다');
}
}, [waitForRefs, config, authModalRef]);
return (
<div>
<button onClick={safeConditionalWait}>안전한 대기</button>
{config.requiresAuth && <dialog ref={authModalRef}>인증 모달</dialog>}
{config.showWelcome && <div ref={useUIRef('welcomeScreen')}>환영 화면</div>}
</div>
);
}성능 최적화
배치 조건부 대기
typescript
function BatchConditionalComponent() {
const waitForRefs = useWaitForRefs();
const sidebarStore = usePreferencesStore('showSidebar');
const toolbarStore = usePreferencesStore('showToolbar');
const statusBarStore = usePreferencesStore('showStatusBar');
const batchConditionalWait = useCallback(async () => {
const showSidebar = sidebarStore.getValue();
const showToolbar = toolbarStore.getValue();
const showStatusBar = statusBarStore.getValue();
const waitPromises: Promise<void>[] = [];
// 조건부 대기 배열 구성
if (showSidebar) {
waitPromises.push(waitForRefs('sidebar'));
}
if (showToolbar) {
waitPromises.push(waitForRefs('toolbar'));
}
if (showStatusBar) {
waitPromises.push(waitForRefs('statusBar'));
}
// 모든 필요 요소를 병렬로 대기
if (waitPromises.length > 0) {
await Promise.all(waitPromises);
}
// 레이아웃 초기화
console.log('선호도와 함께 레이아웃이 초기화되었습니다', {
showSidebar,
showToolbar,
showStatusBar
});
}, [waitForRefs, sidebarStore, toolbarStore, statusBarStore]);
const showSidebar = useStoreValue(sidebarStore);
const showToolbar = useStoreValue(toolbarStore);
const showStatusBar = useStoreValue(statusBarStore);
return (
<div>
<button onClick={batchConditionalWait}>레이아웃 초기화</button>
{showSidebar && <div ref={useUIRef('sidebar')}>사이드바</div>}
{showToolbar && <div ref={useUIRef('toolbar')}>툴바</div>}
{showStatusBar && <div ref={useUIRef('statusBar')}>상태바</div>}
</div>
);
}주요 이점
- 자동 감지: 수동 검사가 필요하지 않음
- 성능: 요소가 이미 마운트된 경우 지연 없음
- 신뢰성: await 후 요소 사용 가능성 보장
- 유연성: 모든 조건부 로직과 결합 가능
- 효율성: 필요할 때만 대기
일반적인 패턴
- 기능 토글: 활성화된 기능에 따라 대기
- 사용자 권한: 사용자 기능에 따라 대기
- 디바이스 기능: 디바이스 특징에 따라 대기
- 네트워크 상태: 연결 상태에 따라 대기
- 점진적 로딩: 필요에 따라 컴포넌트 대기