Files
shoptime/.docs/dispatch-async/01-problem.md
Claude f75860c8dd [문서화] Dispatch 비동기 처리 순서 보장 솔루션 문서 작성
dispatch 비동기 처리 순서 보장 문제와 해결 방법을 체계적으로 정리한 문서를 작성했습니다.

## 작성된 문서

1. README.md - 전체 개요 및 목차
2. 01-problem.md - 문제 상황 및 원인 분석
3. 02-solution-dispatch-helper.md - dispatchHelper.js 솔루션
4. 03-solution-async-utils.md - asyncActionUtils.js 솔루션
5. 04-solution-queue-system.md - 큐 기반 패널 액션 시스템
6. 05-usage-patterns.md - 사용 패턴 및 실전 예제

## 주요 내용

### 문제
- Redux-thunk에서 여러 dispatch 순서가 보장되지 않는 문제
- setTimeout(fn, 0) 임시방편의 한계

### 해결 방법
1. **dispatchHelper.js** (2025-11-05)
   - createSequentialDispatch: Promise 체인 기반 순차 실행
   - createApiThunkWithChain: API 후 dispatch 체이닝
   - withLoadingState: 로딩 상태 자동 관리

2. **asyncActionUtils.js** (2025-11-06)
   - 성공 기준 명확화: HTTP 200-299 + retCode 0/'0'
   - reject 없이 resolve만 사용하여 Promise 체인 보장
   - 타임아웃 지원

3. **큐 기반 패널 액션 시스템** (2025-11-06)
   - queuedPanelActions.js: 패널 액션 큐
   - panelQueueMiddleware.js: 자동 큐 처리
   - 비동기 액션 순차 실행

## 관련 커밋
- 9490d72 [251105] feat: dispatchHelper.js
- 5bd2774 [251106] feat: Queued Panel functions
- f9290a1 [251106] fix: Dispatch Queue implementation
2025-11-10 09:26:40 +00:00

211 lines
5.5 KiB
Markdown

# 문제 상황: Dispatch 비동기 순서 미보장
## 🔴 핵심 문제
Redux-thunk는 비동기 액션을 지원하지만, **여러 개의 dispatch를 순차적으로 호출할 때 실행 순서가 보장되지 않습니다.**
## 📝 기존 코드의 문제점
### 예제 1: homeActions.js
**파일**: `src/actions/homeActions.js`
```javascript
export const getHomeTerms = (props) => (dispatch, getState) => {
const onSuccess = (response) => {
if (response.data.retCode === 0) {
// 첫 번째 dispatch
dispatch({
type: types.GET_HOME_TERMS,
payload: response.data,
});
// 두 번째 dispatch
dispatch({
type: types.SET_TERMS_ID_MAP,
payload: termsIdMap,
});
// ⚠️ 문제: setTimeout으로 순서 보장 시도
setTimeout(() => {
dispatch(getTermsAgreeYn());
}, 0);
}
};
TAxios(dispatch, getState, "get", URLS.GET_HOME_TERMS, ..., onSuccess, onFail);
};
```
**문제점**:
1. `setTimeout(fn, 0)`은 임시방편일 뿐, 명확한 해결책이 아님
2. 코드 가독성이 떨어짐
3. 타이밍 이슈로 인한 버그 가능성
4. 유지보수가 어려움
### 예제 2: cartActions.js
**파일**: `src/actions/cartActions.js`
```javascript
export const addToCart = (props) => (dispatch, getState) => {
const onSuccess = (response) => {
// 첫 번째 dispatch: 카트에 추가
dispatch({
type: types.ADD_TO_CART,
payload: response.data.data,
});
// 두 번째 dispatch: 카트 정보 재조회
// ⚠️ 문제: 순서가 보장되지 않음
dispatch(getMyInfoCartSearch({ mbrNo }));
};
TAxios(dispatch, getState, "post", URLS.ADD_TO_CART, ..., onSuccess, onFail);
};
```
**문제점**:
1. `getMyInfoCartSearch``ADD_TO_CART`보다 먼저 실행될 수 있음
2. 카트 정보가 업데이트되기 전에 재조회가 실행될 수 있음
3. 순서가 보장되지 않아 UI에 잘못된 데이터가 표시될 수 있음
## 🤔 왜 순서가 보장되지 않을까?
### Redux-thunk의 동작 방식
```javascript
// Redux-thunk는 이렇게 동작합니다
function dispatch(action) {
if (typeof action === 'function') {
// thunk action인 경우
return action(dispatch, getState);
} else {
// plain action인 경우
return next(action);
}
}
```
### 문제 시나리오
```javascript
// 이렇게 작성하면
dispatch({ type: 'ACTION_1' }); // Plain action - 즉시 실행
dispatch(asyncAction()); // Thunk - 비동기 실행
dispatch({ type: 'ACTION_2' }); // Plain action - 즉시 실행
// 실제 실행 순서는
// 1. ACTION_1 (동기)
// 2. ACTION_2 (동기)
// 3. asyncAction의 내부 dispatch들 (비동기)
// 즉, asyncAction이 완료되기 전에 ACTION_2가 실행됩니다!
```
## 🎯 해결해야 할 과제
1. **순서 보장**: 여러 dispatch가 의도한 순서대로 실행되도록
2. **에러 처리**: 중간에 에러가 발생해도 체인이 끊기지 않도록
3. **가독성**: 코드가 직관적이고 유지보수하기 쉽도록
4. **재사용성**: 여러 곳에서 쉽게 사용할 수 있도록
5. **호환성**: 기존 코드와 호환되도록
## 📊 실제 발생 가능한 버그
### 시나리오 1: 카트 추가 후 조회
```javascript
// 의도한 순서
1. ADD_TO_CART dispatch
2. 상태 업데이트
3. getMyInfoCartSearch dispatch
4. 최신 카트 정보 조회
// 실제 실행 순서 (문제)
1. ADD_TO_CART dispatch
2. getMyInfoCartSearch dispatch (너무 빨리 실행!)
3. 이전 카트 정보 조회 (아직 상태 업데이트 안됨)
4. 상태 업데이트
결과: UI에 이전 데이터가 표시됨
```
### 시나리오 2: 패널 열고 닫기
```javascript
// 의도한 순서
1. PUSH_PANEL (검색 패널 열기)
2. UPDATE_PANEL (검색 결과 표시)
3. POP_PANEL (이전 패널 닫기)
// 실제 실행 순서 (문제)
1. PUSH_PANEL
2. POP_PANEL (너무 빨리 실행!)
3. UPDATE_PANEL (이미 닫힌 패널을 업데이트)
결과: 패널이 제대로 표시되지 않음
```
## 🔧 기존 해결 방법과 한계
### 방법 1: setTimeout 사용
```javascript
dispatch(action1());
setTimeout(() => {
dispatch(action2());
}, 0);
```
**한계**:
- 명확한 순서 보장 없음
- 타이밍에 의존적
- 코드 가독성 저하
- 유지보수 어려움
### 방법 2: 콜백 중첩
```javascript
const action1 = (callback) => (dispatch, getState) => {
dispatch({ type: 'ACTION_1' });
if (callback) callback();
};
dispatch(action1(() => {
dispatch(action2(() => {
dispatch(action3());
}));
}));
```
**한계**:
- 콜백 지옥
- 에러 처리 복잡
- 코드 가독성 최악
### 방법 3: async/await
```javascript
export const complexAction = () => async (dispatch, getState) => {
await dispatch(action1());
await dispatch(action2());
await dispatch(action3());
};
```
**한계**:
- Chrome 68 호환성 문제 (프로젝트 요구사항)
- 모든 action이 Promise를 반환해야 함
- 기존 코드 대량 수정 필요
## 🎯 다음 단계
이제 이러한 문제들을 해결하기 위한 3가지 솔루션을 살펴보겠습니다:
1. [dispatchHelper.js](./02-solution-dispatch-helper.md) - Promise 체인 기반 헬퍼 함수
2. [asyncActionUtils.js](./03-solution-async-utils.md) - Promise 기반 비동기 처리 유틸리티
3. [큐 기반 패널 액션 시스템](./04-solution-queue-system.md) - 미들웨어 기반 큐 시스템
---
**다음**: [해결 방법 1: dispatchHelper.js →](./02-solution-dispatch-helper.md)