Skip to content

우선순위 기반 핸들러 실행 ​

적절한 비즈니스 로직 플로우를 위해 핸들러가 올바른 순서로 실행되도록 하는 우선순위 기반 실행.

기본 우선순위 시스템 ​

우선순위 레벨 ​

핸들러는 내림차순 우선순위 순서로 실행됩니다 (높은 숫자 먼저):

typescript
actionRegister.register('authenticate', validateInput, { priority: 100 });  // 1번째 실행
actionRegister.register('authenticate', checkRateLimit, { priority: 90 });  // 2번째 실행  
actionRegister.register('authenticate', performAuth, { priority: 80 });     // 3번째 실행
actionRegister.register('authenticate', logAudit, { priority: 70 });        // 4번째 실행

기본 우선순위 ​

우선순위를 지정하지 않으면 핸들러는 기본적으로 우선순위 50을 가집니다:

typescript
// 이 핸들러들은 우선순위 50을 가지며 등록 순서대로 실행됩니다
actionRegister.register('processData', handlerA);
actionRegister.register('processData', handlerB);
actionRegister.register('processData', handlerC);

우선순위 카테고리 ​

높은 우선순위 (90-100): 시스템 중요 ​

  • 입력 검증
  • 보안 검사
  • 속도 제한
  • 인증
typescript
// 중요한 검사가 먼저 오는 인증 파이프라인
actionRegister.register('authenticate', validateCredentials, { priority: 100 });
actionRegister.register('authenticate', checkSecurityPolicy, { priority: 95 });
actionRegister.register('authenticate', rateLimitCheck, { priority: 90 });

중간 우선순위 (50-89): 비즈니스 로직 ​

  • 데이터 처리
  • 비즈니스 규칙 검증
  • 외부 API 호출
  • 상태 업데이트
typescript
// 비즈니스 로직을 사용한 데이터 처리
actionRegister.register('processOrder', validateBusinessRules, { priority: 80 });
actionRegister.register('processOrder', calculatePricing, { priority: 70 });
actionRegister.register('processOrder', updateInventory, { priority: 60 });
actionRegister.register('processOrder', sendConfirmation, { priority: 50 });

낮은 우선순위 (10-49): 로깅 및 분석 ​

  • 감사 로깅
  • 분석 추적
  • 성능 모니터링
  • 정리 작업
typescript
// 로깅과 분석은 마지막에
actionRegister.register('userAction', processAction, { priority: 80 });
actionRegister.register('userAction', trackAnalytics, { priority: 30 });
actionRegister.register('userAction', auditLog, { priority: 20 });
actionRegister.register('userAction', cleanup, { priority: 10 });

우선순위 실행 예제 ​

예제 1: 인증 플로우 ​

typescript
interface AuthActions extends ActionPayloadMap {
  login: { username: string; password: string };
}

const authRegister = new ActionRegister<AuthActions>();

// 우선순위 100: 중요한 검증
authRegister.register('login', (payload, controller) => {
  if (!payload.username || !payload.password) {
    controller.abort('인증 정보 누락');
    return;
  }
  
  controller.setResult({ step: 'validation', valid: true });
  return { step: 'input-validation', success: true };
}, { priority: 100, id: 'input-validator' });

// 우선순위 90: 보안 검사
authRegister.register('login', async (payload, controller) => {
  const isSuspicious = await checkSuspiciousActivity(payload.username);
  
  if (isSuspicious) {
    controller.abort('의심스러운 활동 감지');
    return;
  }
  
  controller.setResult({ step: 'security', cleared: true });
  return { step: 'security-check', success: true };
}, { priority: 90, id: 'security-checker' });

// 우선순위 80: 속도 제한
authRegister.register('login', (payload, controller) => {
  const rateLimiter = getRateLimiter(payload.username);
  
  if (!rateLimiter.isAllowed()) {
    controller.abort('속도 제한 초과');
    return;
  }
  
  rateLimiter.consume();
  controller.setResult({ step: 'rate-limit', consumed: true });
  return { step: 'rate-limiting', success: true };
}, { priority: 80, id: 'rate-limiter' });

// 우선순위 70: 인증
authRegister.register('login', async (payload) => {
  const user = await authenticateUser(payload.username, payload.password);
  const token = generateJWT(user);
  
  return { 
    step: 'authentication', 
    success: true,
    user: { id: user.id, username: user.username },
    token 
  };
}, { priority: 70, id: 'authenticator' });

// 우선순위 30: 분석 (낮은 우선순위)
authRegister.register('login', (payload) => {
  analytics.track('login_attempt', {
    username: payload.username,
    timestamp: Date.now()
  });
  
  return { step: 'analytics', tracked: true };
}, { priority: 30, id: 'analytics-tracker' });

// 우선순위 10: 감사 로깅 (가장 낮은 우선순위)
authRegister.register('login', (payload, controller) => {
  const results = controller.getResults();
  const authResult = results.find(r => r.step === 'authentication');
  
  auditLogger.log({
    action: 'login',
    username: payload.username,
    success: authResult?.success || false,
    timestamp: Date.now()
  });
  
  return { step: 'audit', logged: true };
}, { priority: 10, id: 'audit-logger' });

예제 2: 데이터 처리 파이프라인 ​

typescript
interface DataActions extends ActionPayloadMap {
  processData: { data: any; options?: ProcessingOptions };
}

const dataRegister = new ActionRegister<DataActions>();

// 우선순위 100: 입력 살균
dataRegister.register('processData', (payload, controller) => {
  const sanitized = sanitizeInput(payload.data);
  
  controller.modifyPayload(current => ({
    ...current,
    data: sanitized,
    sanitized: true
  }));
  
  return { step: 'sanitization', success: true };
}, { priority: 100, id: 'sanitizer' });

// 우선순위 90: 스키마 검증
dataRegister.register('processData', (payload, controller) => {
  const isValid = validateSchema(payload.data);
  
  if (!isValid) {
    controller.abort('스키마 검증 실패');
    return;
  }
  
  controller.setResult({ step: 'validation', schema: 'valid' });
  return { step: 'schema-validation', success: true };
}, { priority: 90, id: 'schema-validator' });

// 우선순위 80: 비즈니스 규칙
dataRegister.register('processData', (payload, controller) => {
  const violations = checkBusinessRules(payload.data);
  
  if (violations.length > 0) {
    controller.abort(`비즈니스 규칙 위반: ${violations.join(', ')}`);
    return;
  }
  
  return { step: 'business-validation', success: true };
}, { priority: 80, id: 'business-validator' });

// 우선순위 70: 데이터 변환
dataRegister.register('processData', (payload) => {
  const transformed = transformData(payload.data, payload.options);
  
  return { 
    step: 'transformation', 
    success: true,
    data: transformed,
    originalSize: JSON.stringify(payload.data).length,
    transformedSize: JSON.stringify(transformed).length
  };
}, { priority: 70, id: 'transformer' });

// 우선순위 60: 데이터베이스 저장
dataRegister.register('processData', async (payload, controller) => {
  const results = controller.getResults();
  const transformed = results.find(r => r.step === 'transformation')?.data;
  
  const savedId = await database.save(transformed);
  
  return { 
    step: 'persistence', 
    success: true,
    id: savedId,
    timestamp: Date.now()
  };
}, { priority: 60, id: 'persister' });

// 우선순위 20: 성능 모니터링
dataRegister.register('processData', (payload, controller) => {
  const results = controller.getResults();
  const duration = Date.now() - (results[0]?.timestamp || Date.now());
  
  performanceMonitor.track('data_processing', {
    duration,
    dataSize: JSON.stringify(payload.data).length,
    handlersExecuted: results.length
  });
  
  return { step: 'monitoring', tracked: true, duration };
}, { priority: 20, id: 'monitor' });

우선순위 모범 사례 ​

1. 표준 우선순위 범위 사용 ​

typescript
// 시스템 중요: 90-100
const PRIORITY_CRITICAL = 100;
const PRIORITY_SECURITY = 95;
const PRIORITY_VALIDATION = 90;

// 비즈니스 로직: 50-89
const PRIORITY_BUSINESS_HIGH = 80;
const PRIORITY_BUSINESS_MEDIUM = 70;
const PRIORITY_BUSINESS_LOW = 60;
const PRIORITY_PROCESSING = 50;

// 모니터링/로깅: 10-49
const PRIORITY_ANALYTICS = 30;
const PRIORITY_AUDIT = 20;
const PRIORITY_CLEANUP = 10;

2. 관련 핸들러 그룹화 ​

typescript
// 인증 그룹 (90-100)
authRegister.register('login', inputValidator, { priority: 100 });
authRegister.register('login', securityChecker, { priority: 95 });
authRegister.register('login', rateLimiter, { priority: 90 });

// 비즈니스 로직 그룹 (70-80)  
authRegister.register('login', authenticator, { priority: 80 });
authRegister.register('login', sessionManager, { priority: 70 });

// 모니터링 그룹 (10-30)
authRegister.register('login', analyticsTracker, { priority: 30 });
authRegister.register('login', auditLogger, { priority: 20 });

3. 우선순위 간격 유지 ​

typescript
// ✅ 좋음 - 기존 핸들러 사이에 핸들러를 삽입할 수 있음
actionRegister.register('process', handlerA, { priority: 100 });
actionRegister.register('process', handlerB, { priority: 80 });  // 20의 간격
actionRegister.register('process', handlerC, { priority: 60 });  // 20의 간격

// ❌ 피해야 할 것 - 새 핸들러를 위한 공간이 없음
actionRegister.register('process', handlerA, { priority: 100 });
actionRegister.register('process', handlerB, { priority: 99 });  // 너무 가까움
actionRegister.register('process', handlerC, { priority: 98 });  // 너무 가까움

4. 우선순위 근거 문서화 ​

typescript
// 주석에 명확한 우선순위 근거
actionRegister.register('uploadFile', validateFile, { 
  priority: 100,  // 모든 처리 전에 검증해야 함
  id: 'file-validator' 
});

actionRegister.register('uploadFile', scanVirus, { 
  priority: 95,   // 검증 후 보안 스캔
  id: 'virus-scanner' 
});

actionRegister.register('uploadFile', processFile, { 
  priority: 80,   // 보안 허가 후에만 처리
  id: 'file-processor' 
});

우선순위 시각화 ​

mermaid
graph TD
    A[액션 디스패치] --> B{우선순위 100}
    B --> C{우선순위 90}
    C --> D{우선순위 80}
    D --> E{우선순위 70}
    E --> F{우선순위 50}
    F --> G{우선순위 30}
    G --> H{우선순위 20}
    H --> I{우선순위 10}
    I --> J[파이프라인 완료]
    
    B --> B1[검증 핸들러]
    C --> C1[보안 핸들러]
    D --> D1[비즈니스 핸들러]
    E --> E1[처리 핸들러]
    F --> F1[업데이트 핸들러]
    G --> G1[분석 핸들러]
    H --> H1[감사 핸들러]
    I --> I1[정리 핸들러]

실제 예제: 우선순위 성능 데모 ​

우선순위 성능 데모에서 우선순위 기반 실행의 실제 구현을 확인하세요:

typescript
// 높은 우선순위 검증 (100) - 높은 숫자 = 높은 우선순위
useActionHandler('executeHighPriority', handler, { priority: 100 });

// 중간 우선순위 비즈니스 로직 (80)
useActionHandler('executeMediumPriority', handler, { priority: 80 });

// 낮은 우선순위 분석 (30)
useActionHandler('executeLowPriority', handler, { priority: 30 });

이 예제는 우선순위가 실제 시나리오에서 실행 순서에 어떻게 영향을 주는지 보여주는 타이밍 측정과 함께 성능 추적을 보여줍니다.

관련 문서 ​

Released under the Apache-2.0 License.