Files
shoptime/.docs/dispatch-async/08-troubleshooting.md
Claude bad111e23c [문서 추가] 설정 가이드, 변경 이력, 트러블슈팅 문서 작성
## 추가된 문서

### 06-setup-guide.md - 설정 가이드
- panelQueueMiddleware 등록 상세 가이드
- 미들웨어 등록 순서 설명
- 파일 구조 확인 방법
- 단계별 설정 순서
- 검증 방법 (콘솔 로그, Redux DevTools, State 확인)
- 트러블슈팅 (5가지 일반적인 문제)
- 빠른 체크리스트

### 07-changelog.md - 변경 이력
- 2025-11-10: panelQueueMiddleware 등록 및 문서 개선
- 2025-11-10: 초기 문서 작성
- 2025-11-06: 큐 시스템 구현
- 2025-11-05: dispatch 헬퍼 함수 추가
- 마이그레이션 가이드
- Breaking Changes (없음)
- 알려진 이슈 (모두 해결됨)
- 향후 계획

### 08-troubleshooting.md - 트러블슈팅
- 일반적인 문제 3가지
  - dispatch 순서 미보장
  - Cannot find module 에러
  - 타입 에러
- 큐 시스템 문제 3가지
  - 큐가 처리되지 않음
  - 무한 루프
  - 큐 통계 미업데이트
- API 호출 문제 2가지
  - 성공인데 onFail 호출
  - 타임아웃 미작동
- 성능 문제 2가지
  - 큐 처리 속도 저하
  - 메모리 누수
- 디버깅 팁 5가지
  - 콘솔 로그 활용
  - Redux DevTools 사용
  - 브레이크포인트 설정
  - State 스냅샷
  - 큐 상태 모니터링

## README.md 업데이트
- 목차에 새로운 문서 3개 추가
- "학습 자료" 섹션을 3개 카테고리로 재구성:
  - 시작하기 (설정 가이드, 트러블슈팅)
  - 이해하기 (문제 상황, 3가지 솔루션)
  - 실전 적용 (사용 패턴, 변경 이력)

## 문서 통계
- 총 9개 마크다운 파일
- 약 5,000줄 이상
- 100개 이상의 코드 예제
- 10가지 실전 트러블슈팅 가이드

## 특징
- 초보자도 쉽게 따라할 수 있는 단계별 가이드
- 실제 발생 가능한 문제와 해결 방법
- 변경 이력 및 커밋 히스토리 추적
- 상세한 디버깅 팁
2025-11-10 10:22:23 +00:00

13 KiB

트러블슈팅 가이드

📋 목차

  1. 일반적인 문제
  2. 큐 시스템 문제
  3. API 호출 문제
  4. 성능 문제
  5. 디버깅 팁

일반적인 문제

문제 1: dispatch 순서가 여전히 보장되지 않음

증상

dispatch(action1());
dispatch(action2());
dispatch(action3());
// 실행 순서: action2 → action3 → action1

가능한 원인

  1. 일반 dispatch와 큐 dispatch 혼용

    // ❌ 잘못된 사용
    dispatch(pushPanel({ name: 'PANEL_1' }));  // 일반
    dispatch(pushPanelQueued({ name: 'PANEL_2' }));  // 큐
    
  2. async/await 없이 비동기 처리

    // ❌ 잘못된 사용
    fetchData();  // Promise를 기다리지 않음
    dispatch(action());
    
  3. 헬퍼 함수를 사용하지 않음

    // ❌ 잘못된 사용
    dispatch(asyncAction1());
    dispatch(asyncAction2());  // asyncAction1이 완료되기 전에 실행
    

해결 방법

방법 1: 큐 시스템 사용 (패널 액션인 경우)

// ✅ 올바른 사용
dispatch(enqueueMultiplePanelActions([
  pushPanelQueued({ name: 'PANEL_1' }),
  pushPanelQueued({ name: 'PANEL_2' }),
  pushPanelQueued({ name: 'PANEL_3' })
]));

방법 2: createSequentialDispatch 사용

// ✅ 올바른 사용
dispatch(createSequentialDispatch([
  action1(),
  action2(),
  action3()
]));

방법 3: async/await 사용 (Chrome 68+)

// ✅ 올바른 사용
export const myAction = () => async (dispatch, getState) => {
  await dispatch(action1());
  await dispatch(action2());
  await dispatch(action3());
};

문제 2: "Cannot find module" 에러

증상

Error: Cannot find module '../utils/dispatchHelper'
Error: Cannot find module '../middleware/panelQueueMiddleware'

원인

  • 파일이 존재하지 않음
  • import 경로가 잘못됨
  • 빌드가 필요함

해결 방법

Step 1: 파일 존재 확인

# 프로젝트 루트에서 실행
ls -la com.twin.app.shoptime/src/utils/dispatchHelper.js
ls -la com.twin.app.shoptime/src/middleware/panelQueueMiddleware.js
ls -la com.twin.app.shoptime/src/utils/asyncActionUtils.js

Step 2: 최신 코드 pull

git fetch origin
git pull origin <branch-name>

Step 3: node_modules 재설치

cd com.twin.app.shoptime
rm -rf node_modules package-lock.json
npm install

Step 4: 빌드 재실행

npm run build
# 또는
npm start

문제 3: 타입 에러 (types is not defined)

증상

ReferenceError: types is not defined
TypeError: Cannot read property 'ENQUEUE_PANEL_ACTION' of undefined

원인

actionTypes.js에 필요한 타입이 정의되지 않음

해결 방법

Step 1: actionTypes.js 확인

// src/actions/actionTypes.js
export const types = {
  // ... 기존 타입들 ...

  // 큐 관련 타입들 (필수!)
  ENQUEUE_PANEL_ACTION: 'ENQUEUE_PANEL_ACTION',
  PROCESS_PANEL_QUEUE: 'PROCESS_PANEL_QUEUE',
  CLEAR_PANEL_QUEUE: 'CLEAR_PANEL_QUEUE',
  SET_QUEUE_PROCESSING: 'SET_QUEUE_PROCESSING',

  // 비동기 액션 타입들 (필수!)
  ENQUEUE_ASYNC_PANEL_ACTION: 'ENQUEUE_ASYNC_PANEL_ACTION',
  COMPLETE_ASYNC_PANEL_ACTION: 'COMPLETE_ASYNC_PANEL_ACTION',
  FAIL_ASYNC_PANEL_ACTION: 'FAIL_ASYNC_PANEL_ACTION',
};

Step 2: import 확인

import { types } from '../actions/actionTypes';

큐 시스템 문제

문제 4: 큐가 처리되지 않음

증상

dispatch(pushPanelQueued({ name: panel_names.SEARCH_PANEL }));
// 아무 일도 일어나지 않음
// 콘솔 로그도 없음

원인

panelQueueMiddleware가 등록되지 않음 (가장 흔한 문제!)

해결 방법

Step 1: store.js 확인

// src/store/store.js
import panelQueueMiddleware from '../middleware/panelQueueMiddleware';

export const store = createStore(
  rootReducer,
  applyMiddleware(
    thunk,
    panelHistoryMiddleware,
    autoCloseMiddleware,
    panelQueueMiddleware  // ← 이것이 있는지 확인!
  )
);

Step 2: import 경로 확인

// ✅ 올바른 import
import panelQueueMiddleware from '../middleware/panelQueueMiddleware';

// ❌ 잘못된 import
import { panelQueueMiddleware } from '../middleware/panelQueueMiddleware';
// default export이므로 중괄호 없이 import해야 함

Step 3: 앱 재시작

# 개발 서버 재시작
npm start

Step 4: 브라우저 캐시 삭제

  • Chrome: Ctrl+Shift+R (Windows) 또는 Cmd+Shift+R (Mac)

문제 5: 큐가 무한 루프에 빠짐

증상

[panelQueueMiddleware] 🚀 ACTION_ENQUEUED
[panelQueueMiddleware] ⚡ STARTING_QUEUE_PROCESS
[panelReducer] 🟡 PROCESS_PANEL_QUEUE
[panelReducer] 🟡 PROCESS_PANEL_QUEUE
[panelReducer] 🟡 PROCESS_PANEL_QUEUE
... (무한 반복)

원인

  1. 큐 처리 중에 다시 큐에 액션 추가
  2. isProcessingQueue 플래그가 제대로 설정되지 않음

해결 방법

방법 1: 큐 액션 내부에서 일반 dispatch 사용

// ❌ 잘못된 사용 (무한 루프 발생)
export const myAction = () => (dispatch) => {
  dispatch(pushPanelQueued({ name: 'PANEL_1' }));
  dispatch(pushPanelQueued({ name: 'PANEL_2' }));  // 큐 처리 중 큐 추가
};

// ✅ 올바른 사용
export const myAction = () => (dispatch) => {
  dispatch(enqueueMultiplePanelActions([
    pushPanelQueued({ name: 'PANEL_1' }),
    pushPanelQueued({ name: 'PANEL_2' })
  ]));
};

방법 2: 리듀서 로직 확인

// panelReducer.js에서 확인
case types.PROCESS_PANEL_QUEUE: {
  // 이미 처리 중이면 무시
  if (state.isProcessingQueue || state.panelActionQueue.length === 0) {
    return state;  // ← 이 로직이 있는지 확인
  }
  // ...
}

문제 6: 큐 통계가 업데이트되지 않음

증상

store.getState().panels.queueStats
// { totalProcessed: 0, failedCount: 0, averageProcessingTime: 0 }
// 항상 0으로 유지됨

원인

큐 처리가 정상적으로 완료되지 않음

해결 방법

Step 1: 콘솔 로그 확인

[panelReducer] ✅ QUEUE_ITEM_PROCESSED  ← 이 로그가 보이는지 확인

Step 2: 에러 발생 확인

store.getState().panels.queueError
// null이어야 정상

Step 3: 큐 처리 완료 여부 확인

store.getState().panels.isProcessingQueue
// false여야 정상 (처리 완료)

API 호출 문제

문제 7: API 성공인데 onFail이 호출됨

증상

// API 호출
// HTTP 200, retCode: 0
// 그런데 onFail이 호출됨

원인

프로젝트 성공 기준을 이해하지 못함

프로젝트 성공 기준

HTTP 200-299 + retCode 0/'0' 둘 다 만족해야 성공!

// ✅ 성공 케이스
{ status: 200, data: { retCode: 0, data: {...} } }
{ status: 200, data: { retCode: '0', data: {...} } }

// ❌ 실패 케이스
{ status: 200, data: { retCode: 1, message: '에러' } }  // retCode가 0이 아님
{ status: 500, data: { retCode: 0, data: {...} } }  // HTTP 에러

해결 방법

방법 1: isApiSuccess 사용

import { isApiSuccess } from '../utils/asyncActionUtils';

const response = { status: 200 };
const responseData = { retCode: 1, message: '에러' };

if (isApiSuccess(response, responseData)) {
  // 성공 처리
} else {
  // 실패 처리 (retCode가 1이므로 실패!)
}

방법 2: asyncActionUtils 사용

import { tAxiosToPromise } from '../utils/asyncActionUtils';

const result = await tAxiosToPromise(...);

if (result.success) {
  // HTTP 200-299 + retCode 0/'0'
  console.log(result.data);
} else {
  // 실패
  console.error(result.error);
}

문제 8: API 타임아웃이 작동하지 않음

증상

dispatch(enqueueAsyncPanelAction({
  asyncAction: (d, gs, onS, onF) => { /* 느린 API */ },
  timeout: 5000  // 5초
}));
// 10초가 지나도 타임아웃되지 않음

원인

  1. withTimeout이 적용되지 않음
  2. 타임아웃 값이 잘못 설정됨

해결 방법

방법 1: enqueueAsyncPanelAction 사용 시

// ✅ timeout 옵션 사용
dispatch(enqueueAsyncPanelAction({
  asyncAction: (dispatch, getState, onSuccess, onFail) => {
    TAxios(dispatch, getState, 'get', URL, {}, {}, onSuccess, onFail);
  },
  timeout: 5000,  // 5초 (ms 단위)
  onFail: (error) => {
    if (error.code === 'TIMEOUT') {
      console.error('타임아웃 발생!');
    }
  }
}));

방법 2: withTimeout 직접 사용

import { withTimeout, fetchApi } from '../utils/asyncActionUtils';

const result = await withTimeout(
  fetchApi('/api/slow-endpoint'),
  5000,  // 5초
  '요청 시간이 초과되었습니다'
);

if (result.error?.code === 'TIMEOUT') {
  console.error('타임아웃!');
}

성능 문제

문제 9: 큐 처리가 너무 느림

증상

// 100개의 패널 액션을 큐에 추가
// 처리하는데 10초 이상 소요

원인

  1. 각 액션이 복잡한 로직 수행
  2. 동기적으로 처리되어 병목 발생

해결 방법

방법 1: 불필요한 액션 제거

// ❌ 잘못된 사용
for (let i = 0; i < 100; i++) {
  dispatch(pushPanelQueued({ name: `PANEL_${i}` }));
}

// ✅ 올바른 사용 - 필요한 것만
dispatch(pushPanelQueued({ name: 'MAIN_PANEL' }));

방법 2: 배치 처리

// 한 번에 여러 액션 추가
dispatch(enqueueMultiplePanelActions(
  panels.map(panel => pushPanelQueued(panel))
));

방법 3: 병렬 처리가 필요하면 큐 사용 안함

// 순서가 중요하지 않은 경우
dispatch(createParallelDispatch([
  fetchData1(),
  fetchData2(),
  fetchData3()
]));

문제 10: 메모리 누수

증상

// 오랜 시간 앱 사용 후
store.getState().panels.completedAsyncActions.length
// → 10000개 이상

원인

완료된 비동기 액션 ID가 계속 누적됨

해결 방법

방법 1: 주기적으로 클리어

// 일정 시간마다 완료된 액션 정리
setInterval(() => {
  const state = store.getState().panels;

  if (state.completedAsyncActions.length > 1000) {
    // 클리어 액션 dispatch
    dispatch({ type: types.CLEAR_COMPLETED_ASYNC_ACTIONS });
  }
}, 60000);  // 1분마다

방법 2: 리듀서에 최대 개수 제한 추가

// panelReducer.js
case types.COMPLETE_ASYNC_PANEL_ACTION: {
  const newCompleted = [...state.completedAsyncActions, action.payload.actionId];

  // 최근 100개만 유지
  const trimmed = newCompleted.slice(-100);

  return {
    ...state,
    completedAsyncActions: trimmed
  };
}

디버깅 팁

Tip 1: 콘솔 로그 활용

모든 헬퍼 함수와 미들웨어는 상세한 로그를 출력합니다:

// 큐 관련 로그
[panelQueueMiddleware] 🚀 ACTION_ENQUEUED
[panelQueueMiddleware]  STARTING_QUEUE_PROCESS
[panelReducer] 🟡 PROCESS_PANEL_QUEUE
[panelReducer] 🟡 PROCESSING_QUEUE_ITEM
[panelReducer]  QUEUE_ITEM_PROCESSED

// 비동기 액션 로그
[queuedPanelActions] 🔄 ENQUEUE_ASYNC_PANEL_ACTION
[queuedPanelActions]  EXECUTING_ASYNC_ACTION
[queuedPanelActions]  ASYNC_ACTION_SUCCESS

// asyncActionUtils 로그
[asyncActionUtils] 🌐 FETCH_API_START
[asyncActionUtils] 📊 API_RESPONSE
[asyncActionUtils]  TAXIOS_SUCCESS

Tip 2: Redux DevTools 사용

  1. Chrome 확장 프로그램 설치: Redux DevTools
  2. 개발자 도구 → Redux 탭
  3. 액션 히스토리 확인
  4. State diff 확인

Tip 3: 브레이크포인트 설정

// 디버깅용 브레이크포인트
export const myAction = () => (dispatch, getState) => {
  debugger;  // ← 여기서 멈춤

  const state = getState();
  console.log('Current state:', state);

  dispatch(action1());

  debugger;  // ← 여기서 다시 멈춤
};

Tip 4: State 스냅샷

// 콘솔에서 실행
const snapshot = JSON.parse(JSON.stringify(store.getState()));
console.log(snapshot);

// 특정 부분만
const panelsSnapshot = JSON.parse(JSON.stringify(store.getState().panels));
console.log(panelsSnapshot);

Tip 5: 큐 상태 모니터링

// 콘솔에서 실행
window.monitorQueue = setInterval(() => {
  const state = store.getState().panels;
  console.log('Queue status:', {
    queueLength: state.panelActionQueue.length,
    isProcessing: state.isProcessingQueue,
    stats: state.queueStats
  });
}, 1000);

// 중지
clearInterval(window.monitorQueue);

도움이 필요하신가요?

체크리스트

문제 해결 전에 다음을 확인하세요:

  • panelQueueMiddleware가 store.js에 등록되어 있는가?
  • 필요한 파일들이 모두 존재하는가?
  • actionTypes.js에 필요한 타입들이 정의되어 있는가?
  • 콘솔 로그를 확인했는가?
  • Redux DevTools로 액션 흐름을 확인했는가?
  • 앱을 재시작했는가?
  • 브라우저 캐시를 삭제했는가?

추가 리소스


작성일: 2025-11-10 최종 수정일: 2025-11-10