restore: .docs 폴더 복원 및 .gitignore 수정
- claude/ 브랜치에서 누락된 .docs 폴더 복원 완료 - dispatch-async 관련 문서 9개 파일 복원 * 01-problem.md, 02-solution-dispatch-helper.md * 03-solution-async-utils.md, 04-solution-queue-system.md * 05-usage-patterns.md, 06-setup-guide.md * 07-changelog.md, 08-troubleshooting.md, README.md - MediaPlayer.v2 관련 문서 4개 파일 복원 * MediaPlayer-v2-README.md, MediaPlayer-v2-Required-Changes.md * MediaPlayer-v2-Risk-Analysis.md, PR-MediaPlayer-v2.md - 기타 분석 문서 2개 파일 복원 * modal-transition-analysis.md, video-player-analysis-and-optimization-plan.md - .gitignore에서 .docs 항목 제거로 문서 추적 가능하도록 수정 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: GLM 4.6 <noreply@z.ai>
This commit is contained in:
@@ -0,0 +1,711 @@
|
||||
# 해결 방법 2: asyncActionUtils.js
|
||||
|
||||
## 📦 개요
|
||||
|
||||
**파일**: `src/utils/asyncActionUtils.js`
|
||||
**작성일**: 2025-11-06
|
||||
**커밋**: `f9290a1 [251106] fix: Dispatch Queue implementation`
|
||||
|
||||
Promise 기반의 비동기 액션 처리와 **상세한 성공/실패 기준**을 제공합니다.
|
||||
|
||||
## 🎯 핵심 개념
|
||||
|
||||
### 프로젝트 특화 성공 기준
|
||||
|
||||
이 프로젝트에서 API 호출 성공은 **2가지 조건**을 모두 만족해야 합니다:
|
||||
|
||||
1. ✅ **HTTP 상태 코드**: 200-299 범위
|
||||
2. ✅ **retCode**: 0 또는 '0'
|
||||
|
||||
```javascript
|
||||
// HTTP 200이지만 retCode가 1인 경우
|
||||
{
|
||||
status: 200, // ✅ HTTP는 성공
|
||||
data: {
|
||||
retCode: 1, // ❌ retCode는 실패
|
||||
message: "권한이 없습니다"
|
||||
}
|
||||
}
|
||||
// → 이것은 실패입니다!
|
||||
```
|
||||
|
||||
### Promise 체인이 끊기지 않는 설계
|
||||
|
||||
**핵심 원칙**: 모든 비동기 작업은 **reject 없이 resolve만 사용**합니다.
|
||||
|
||||
```javascript
|
||||
// ❌ 일반적인 방식 (Promise 체인이 끊김)
|
||||
return new Promise((resolve, reject) => {
|
||||
if (error) {
|
||||
reject(error); // 체인이 끊김!
|
||||
}
|
||||
});
|
||||
|
||||
// ✅ 이 프로젝트의 방식 (체인 유지)
|
||||
return new Promise((resolve) => {
|
||||
if (error) {
|
||||
resolve({
|
||||
success: false,
|
||||
error: { code: 'ERROR', message: '에러 발생' }
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔑 핵심 함수
|
||||
|
||||
1. `isApiSuccess` - API 성공 여부 판단
|
||||
2. `fetchApi` - Promise 기반 fetch 래퍼
|
||||
3. `tAxiosToPromise` - TAxios를 Promise로 변환
|
||||
4. `wrapAsyncAction` - 비동기 액션을 Promise로 래핑
|
||||
5. `withTimeout` - 타임아웃 지원
|
||||
6. `executeParallelAsyncActions` - 병렬 실행
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ isApiSuccess
|
||||
|
||||
### 설명
|
||||
|
||||
API 응답이 성공인지 판단하는 **프로젝트 표준 함수**입니다.
|
||||
|
||||
### 구현
|
||||
|
||||
**파일**: `src/utils/asyncActionUtils.js:21-34`
|
||||
|
||||
```javascript
|
||||
export const isApiSuccess = (response, responseData) => {
|
||||
// 1️⃣ HTTP 상태 코드 확인 (200-299 성공 범위)
|
||||
if (!response.ok || response.status < 200 || response.status >= 300) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 2️⃣ retCode 확인 - 0 또는 '0'이어야 성공
|
||||
if (responseData && responseData.retCode !== undefined) {
|
||||
return responseData.retCode === 0 || responseData.retCode === '0';
|
||||
}
|
||||
|
||||
// retCode가 없는 경우 HTTP 상태 코드만으로 판단
|
||||
return response.ok;
|
||||
};
|
||||
```
|
||||
|
||||
### 사용 예제
|
||||
|
||||
```javascript
|
||||
// 성공 케이스
|
||||
isApiSuccess(
|
||||
{ ok: true, status: 200 },
|
||||
{ retCode: 0, data: { ... } }
|
||||
); // → true
|
||||
|
||||
isApiSuccess(
|
||||
{ ok: true, status: 200 },
|
||||
{ retCode: '0', data: { ... } }
|
||||
); // → true
|
||||
|
||||
// 실패 케이스
|
||||
isApiSuccess(
|
||||
{ ok: true, status: 200 },
|
||||
{ retCode: 1, message: "권한 없음" }
|
||||
); // → false (retCode가 0이 아님)
|
||||
|
||||
isApiSuccess(
|
||||
{ ok: false, status: 500 },
|
||||
{ retCode: 0, data: { ... } }
|
||||
); // → false (HTTP 상태 코드가 500)
|
||||
|
||||
isApiSuccess(
|
||||
{ ok: false, status: 404 },
|
||||
{ retCode: 0 }
|
||||
); // → false (404 에러)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ fetchApi
|
||||
|
||||
### 설명
|
||||
|
||||
**표준 fetch API를 Promise로 래핑**하여 프로젝트 성공 기준에 맞춰 처리합니다.
|
||||
|
||||
### 핵심 특징
|
||||
|
||||
- ✅ 항상 `resolve` 사용 (reject 없음)
|
||||
- ✅ HTTP 상태 + retCode 모두 확인
|
||||
- ✅ JSON 파싱 에러도 처리
|
||||
- ✅ 네트워크 에러도 처리
|
||||
- ✅ 상세한 로깅
|
||||
|
||||
### 구현
|
||||
|
||||
**파일**: `src/utils/asyncActionUtils.js:57-123`
|
||||
|
||||
```javascript
|
||||
export const fetchApi = (url, options = {}) => {
|
||||
console.log('[asyncActionUtils] 🌐 FETCH_API_START', { url, method: options.method || 'GET' });
|
||||
|
||||
return new Promise((resolve) => { // ⚠️ 항상 resolve만 사용!
|
||||
fetch(url, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...options.headers
|
||||
},
|
||||
...options
|
||||
})
|
||||
.then(response => {
|
||||
// JSON 파싱
|
||||
return response.json()
|
||||
.then(responseData => {
|
||||
console.log('[asyncActionUtils] 📊 API_RESPONSE', {
|
||||
status: response.status,
|
||||
ok: response.ok,
|
||||
retCode: responseData.retCode,
|
||||
success: isApiSuccess(response, responseData)
|
||||
});
|
||||
|
||||
// ✅ 성공/실패 여부와 관계없이 항상 resolve
|
||||
resolve({
|
||||
response,
|
||||
data: responseData,
|
||||
success: isApiSuccess(response, responseData),
|
||||
error: !isApiSuccess(response, responseData) ? {
|
||||
code: responseData.retCode || response.status,
|
||||
message: responseData.message || getApiErrorMessage(responseData.retCode || response.status),
|
||||
httpStatus: response.status
|
||||
} : null
|
||||
});
|
||||
})
|
||||
.catch(parseError => {
|
||||
console.error('[asyncActionUtils] ❌ JSON_PARSE_ERROR', parseError);
|
||||
|
||||
// ✅ JSON 파싱 실패도 resolve로 처리
|
||||
resolve({
|
||||
response,
|
||||
data: null,
|
||||
success: false,
|
||||
error: {
|
||||
code: 'PARSE_ERROR',
|
||||
message: '응답 데이터 파싱에 실패했습니다',
|
||||
originalError: parseError
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('[asyncActionUtils] 💥 FETCH_ERROR', error);
|
||||
|
||||
// ✅ 네트워크 에러 등도 resolve로 처리
|
||||
resolve({
|
||||
response: null,
|
||||
data: null,
|
||||
success: false,
|
||||
error: {
|
||||
code: 'NETWORK_ERROR',
|
||||
message: error.message || '네트워크 오류가 발생했습니다',
|
||||
originalError: error
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### 사용 예제
|
||||
|
||||
```javascript
|
||||
import { fetchApi } from '../utils/asyncActionUtils';
|
||||
|
||||
// 기본 사용
|
||||
const result = await fetchApi('/api/products/123', {
|
||||
method: 'GET'
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
console.log('성공:', result.data);
|
||||
// HTTP 200-299 + retCode 0/'0'
|
||||
} else {
|
||||
console.error('실패:', result.error);
|
||||
// error.code, error.message 사용 가능
|
||||
}
|
||||
|
||||
// POST 요청
|
||||
const result = await fetchApi('/api/cart', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ productId: 123 })
|
||||
});
|
||||
|
||||
// 헤더 추가
|
||||
const result = await fetchApi('/api/user', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': 'Bearer token123'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 반환 구조
|
||||
|
||||
```javascript
|
||||
// 성공 시
|
||||
{
|
||||
response: Response, // fetch Response 객체
|
||||
data: { ... }, // 파싱된 JSON 데이터
|
||||
success: true, // 성공 플래그
|
||||
error: null // 에러 없음
|
||||
}
|
||||
|
||||
// 실패 시 (HTTP 에러)
|
||||
{
|
||||
response: Response,
|
||||
data: { retCode: 1, message: "권한 없음" },
|
||||
success: false,
|
||||
error: {
|
||||
code: 1,
|
||||
message: "권한 없음",
|
||||
httpStatus: 200
|
||||
}
|
||||
}
|
||||
|
||||
// 실패 시 (네트워크 에러)
|
||||
{
|
||||
response: null,
|
||||
data: null,
|
||||
success: false,
|
||||
error: {
|
||||
code: 'NETWORK_ERROR',
|
||||
message: '네트워크 오류가 발생했습니다',
|
||||
originalError: Error
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ tAxiosToPromise
|
||||
|
||||
### 설명
|
||||
|
||||
프로젝트에서 사용하는 **TAxios를 Promise로 변환**합니다.
|
||||
|
||||
### 구현
|
||||
|
||||
**파일**: `src/utils/asyncActionUtils.js:138-204`
|
||||
|
||||
```javascript
|
||||
export const tAxiosToPromise = (
|
||||
TAxios,
|
||||
dispatch,
|
||||
getState,
|
||||
method,
|
||||
baseUrl,
|
||||
urlParams,
|
||||
params,
|
||||
options = {}
|
||||
) => {
|
||||
return new Promise((resolve) => {
|
||||
console.log('[asyncActionUtils] 🔄 TAXIOS_TO_PROMISE_START', { method, baseUrl });
|
||||
|
||||
const enhancedOnSuccess = (response) => {
|
||||
console.log('[asyncActionUtils] ✅ TAXIOS_SUCCESS', { retCode: response?.data?.retCode });
|
||||
|
||||
// TAxios 성공 콜백도 성공 기준 적용
|
||||
const isSuccess = response?.data && (
|
||||
response.data.retCode === 0 ||
|
||||
response.data.retCode === '0'
|
||||
);
|
||||
|
||||
resolve({
|
||||
response,
|
||||
data: response.data,
|
||||
success: isSuccess,
|
||||
error: !isSuccess ? {
|
||||
code: response.data?.retCode || 'UNKNOWN_ERROR',
|
||||
message: response.data?.message || getApiErrorMessage(response.data?.retCode || 'UNKNOWN_ERROR')
|
||||
} : null
|
||||
});
|
||||
};
|
||||
|
||||
const enhancedOnFail = (error) => {
|
||||
console.error('[asyncActionUtils] ❌ TAXIOS_FAIL', error);
|
||||
|
||||
resolve({ // ⚠️ reject가 아닌 resolve
|
||||
response: null,
|
||||
data: null,
|
||||
success: false,
|
||||
error: {
|
||||
code: error.retCode || 'TAXIOS_ERROR',
|
||||
message: error.message || 'API 호출에 실패했습니다',
|
||||
originalError: error
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
TAxios(
|
||||
dispatch,
|
||||
getState,
|
||||
method,
|
||||
baseUrl,
|
||||
urlParams,
|
||||
params,
|
||||
enhancedOnSuccess,
|
||||
enhancedOnFail,
|
||||
options.noTokenRefresh || false,
|
||||
options.responseType
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('[asyncActionUtils] 💥 TAXIOS_EXECUTION_ERROR', error);
|
||||
|
||||
resolve({
|
||||
response: null,
|
||||
data: null,
|
||||
success: false,
|
||||
error: {
|
||||
code: 'EXECUTION_ERROR',
|
||||
message: 'API 호출 실행 중 오류가 발생했습니다',
|
||||
originalError: error
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### 사용 예제
|
||||
|
||||
```javascript
|
||||
import { tAxiosToPromise } from '../utils/asyncActionUtils';
|
||||
import { TAxios } from '../utils/TAxios';
|
||||
|
||||
export const getProductDetail = (productId) => async (dispatch, getState) => {
|
||||
const result = await tAxiosToPromise(
|
||||
TAxios,
|
||||
dispatch,
|
||||
getState,
|
||||
'get',
|
||||
URLS.GET_PRODUCT_DETAIL,
|
||||
{},
|
||||
{ productId },
|
||||
{}
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
dispatch({
|
||||
type: types.GET_PRODUCT_DETAIL,
|
||||
payload: result.data.data
|
||||
});
|
||||
} else {
|
||||
console.error('상품 조회 실패:', result.error);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ wrapAsyncAction
|
||||
|
||||
### 설명
|
||||
|
||||
비동기 액션 함수를 Promise로 래핑하여 **표준화된 결과 구조**를 반환합니다.
|
||||
|
||||
### 구현
|
||||
|
||||
**파일**: `src/utils/asyncActionUtils.js:215-270`
|
||||
|
||||
```javascript
|
||||
export const wrapAsyncAction = (asyncAction, context = {}) => {
|
||||
return new Promise((resolve) => {
|
||||
const { dispatch, getState } = context;
|
||||
|
||||
console.log('[asyncActionUtils] 🎯 WRAP_ASYNC_ACTION_START');
|
||||
|
||||
// 성공 콜백 - 항상 resolve 호출
|
||||
const onSuccess = (result) => {
|
||||
console.log('[asyncActionUtils] ✅ WRAP_ASYNC_SUCCESS', result);
|
||||
|
||||
resolve({
|
||||
response: result.response || result,
|
||||
data: result.data || result,
|
||||
success: true,
|
||||
error: null
|
||||
});
|
||||
};
|
||||
|
||||
// 실패 콜백 - 항상 resolve 호출 (reject 하지 않음)
|
||||
const onFail = (error) => {
|
||||
console.error('[asyncActionUtils] ❌ WRAP_ASYNC_FAIL', error);
|
||||
|
||||
resolve({
|
||||
response: null,
|
||||
data: null,
|
||||
success: false,
|
||||
error: {
|
||||
code: error.retCode || error.code || 'ASYNC_ACTION_ERROR',
|
||||
message: error.message || error.errorMessage || '비동기 작업에 실패했습니다',
|
||||
originalError: error
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
// 비동기 액션 실행
|
||||
const result = asyncAction(dispatch, getState, onSuccess, onFail);
|
||||
|
||||
// Promise를 반환하는 경우도 처리
|
||||
if (result && typeof result.then === 'function') {
|
||||
result
|
||||
.then(onSuccess)
|
||||
.catch(onFail);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[asyncActionUtils] 💥 WRAP_ASYNC_EXECUTION_ERROR', error);
|
||||
onFail(error);
|
||||
}
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### 사용 예제
|
||||
|
||||
```javascript
|
||||
import { wrapAsyncAction } from '../utils/asyncActionUtils';
|
||||
|
||||
// 비동기 액션 정의
|
||||
const myAsyncAction = (dispatch, getState, onSuccess, onFail) => {
|
||||
TAxios(dispatch, getState, 'get', URL, {}, {}, onSuccess, onFail);
|
||||
};
|
||||
|
||||
// Promise로 래핑하여 사용
|
||||
const result = await wrapAsyncAction(myAsyncAction, { dispatch, getState });
|
||||
|
||||
if (result.success) {
|
||||
console.log('성공:', result.data);
|
||||
} else {
|
||||
console.error('실패:', result.error.message);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5️⃣ withTimeout
|
||||
|
||||
### 설명
|
||||
|
||||
Promise에 **타임아웃**을 적용합니다.
|
||||
|
||||
### 구현
|
||||
|
||||
**파일**: `src/utils/asyncActionUtils.js:354-373`
|
||||
|
||||
```javascript
|
||||
export const withTimeout = (
|
||||
promise,
|
||||
timeoutMs,
|
||||
timeoutMessage = '작업 시간이 초과되었습니다'
|
||||
) => {
|
||||
return Promise.race([
|
||||
promise,
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
console.error('[asyncActionUtils] ⏰ PROMISE_TIMEOUT', { timeoutMs });
|
||||
resolve({
|
||||
response: null,
|
||||
data: null,
|
||||
success: false,
|
||||
error: {
|
||||
code: 'TIMEOUT',
|
||||
message: timeoutMessage,
|
||||
timeout: timeoutMs
|
||||
}
|
||||
});
|
||||
}, timeoutMs);
|
||||
})
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
### 사용 예제
|
||||
|
||||
```javascript
|
||||
import { withTimeout, fetchApi } from '../utils/asyncActionUtils';
|
||||
|
||||
// 5초 타임아웃
|
||||
const result = await withTimeout(
|
||||
fetchApi('/api/slow-endpoint'),
|
||||
5000,
|
||||
'요청이 시간초과 되었습니다'
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
console.log('성공:', result.data);
|
||||
} else if (result.error.code === 'TIMEOUT') {
|
||||
console.error('타임아웃 발생');
|
||||
} else {
|
||||
console.error('기타 에러:', result.error);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6️⃣ executeParallelAsyncActions
|
||||
|
||||
### 설명
|
||||
|
||||
여러 비동기 액션을 **병렬로 실행**하고 모든 결과를 기다립니다.
|
||||
|
||||
### 구현
|
||||
|
||||
**파일**: `src/utils/asyncActionUtils.js:279-299`
|
||||
|
||||
```javascript
|
||||
export const executeParallelAsyncActions = (asyncActions, context = {}) => {
|
||||
console.log('[asyncActionUtils] 🚀 EXECUTE_PARALLEL_START', { count: asyncActions.length });
|
||||
|
||||
const promises = asyncActions.map(action =>
|
||||
wrapAsyncAction(action, context)
|
||||
);
|
||||
|
||||
return Promise.all(promises)
|
||||
.then(results => {
|
||||
console.log('[asyncActionUtils] ✅ EXECUTE_PARALLEL_SUCCESS', {
|
||||
successCount: results.filter(r => r.success).length,
|
||||
failCount: results.filter(r => !r.success).length
|
||||
});
|
||||
return results;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('[asyncActionUtils] ❌ EXECUTE_PARALLEL_ERROR', error);
|
||||
return [];
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
### 사용 예제
|
||||
|
||||
```javascript
|
||||
import { executeParallelAsyncActions } from '../utils/asyncActionUtils';
|
||||
|
||||
// 3개의 API를 동시에 호출
|
||||
const results = await executeParallelAsyncActions([
|
||||
(dispatch, getState, onSuccess, onFail) => {
|
||||
TAxios(dispatch, getState, 'get', URL1, {}, {}, onSuccess, onFail);
|
||||
},
|
||||
(dispatch, getState, onSuccess, onFail) => {
|
||||
TAxios(dispatch, getState, 'get', URL2, {}, {}, onSuccess, onFail);
|
||||
},
|
||||
(dispatch, getState, onSuccess, onFail) => {
|
||||
TAxios(dispatch, getState, 'get', URL3, {}, {}, onSuccess, onFail);
|
||||
}
|
||||
], { dispatch, getState });
|
||||
|
||||
// 결과 처리
|
||||
results.forEach((result, index) => {
|
||||
if (result.success) {
|
||||
console.log(`API ${index + 1} 성공:`, result.data);
|
||||
} else {
|
||||
console.error(`API ${index + 1} 실패:`, result.error);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 실제 사용 시나리오
|
||||
|
||||
### 시나리오 1: API 호출 후 후속 처리
|
||||
|
||||
```javascript
|
||||
import { tAxiosToPromise } from '../utils/asyncActionUtils';
|
||||
|
||||
export const addToCartAndRefresh = (productId) => async (dispatch, getState) => {
|
||||
// 1. 카트에 추가
|
||||
const addResult = await tAxiosToPromise(
|
||||
TAxios,
|
||||
dispatch,
|
||||
getState,
|
||||
'post',
|
||||
URLS.ADD_TO_CART,
|
||||
{},
|
||||
{ productId },
|
||||
{}
|
||||
);
|
||||
|
||||
if (addResult.success) {
|
||||
// 2. 카트 추가 성공 시 카트 정보 재조회
|
||||
dispatch({ type: types.ADD_TO_CART, payload: addResult.data.data });
|
||||
|
||||
const cartResult = await tAxiosToPromise(
|
||||
TAxios,
|
||||
dispatch,
|
||||
getState,
|
||||
'get',
|
||||
URLS.GET_CART,
|
||||
{},
|
||||
{ mbrNo: addResult.data.data.mbrNo },
|
||||
{}
|
||||
);
|
||||
|
||||
if (cartResult.success) {
|
||||
dispatch({ type: types.GET_CART, payload: cartResult.data.data });
|
||||
}
|
||||
} else {
|
||||
console.error('카트 추가 실패:', addResult.error);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 시나리오 2: 타임아웃이 있는 API 호출
|
||||
|
||||
```javascript
|
||||
import { tAxiosToPromise, withTimeout } from '../utils/asyncActionUtils';
|
||||
|
||||
export const getLargeData = () => async (dispatch, getState) => {
|
||||
const result = await withTimeout(
|
||||
tAxiosToPromise(
|
||||
TAxios,
|
||||
dispatch,
|
||||
getState,
|
||||
'get',
|
||||
URLS.GET_LARGE_DATA,
|
||||
{},
|
||||
{},
|
||||
{}
|
||||
),
|
||||
10000, // 10초 타임아웃
|
||||
'데이터 조회 시간이 초과되었습니다'
|
||||
);
|
||||
|
||||
if (result.success) {
|
||||
dispatch({ type: types.GET_LARGE_DATA, payload: result.data.data });
|
||||
} else if (result.error.code === 'TIMEOUT') {
|
||||
// 타임아웃 처리
|
||||
dispatch({ type: types.SHOW_TIMEOUT_MESSAGE });
|
||||
} else {
|
||||
// 기타 에러 처리
|
||||
console.error('조회 실패:', result.error);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 장점
|
||||
|
||||
1. **성공 기준 명확화**: HTTP + retCode 모두 확인
|
||||
2. **체인 보장**: reject 없이 resolve만 사용하여 Promise 체인 유지
|
||||
3. **상세한 로깅**: 모든 단계에서 로그 출력
|
||||
4. **타임아웃 지원**: 응답 없는 API 처리 가능
|
||||
5. **에러 처리**: 모든 에러를 표준 구조로 반환
|
||||
|
||||
## ⚠️ 주의사항
|
||||
|
||||
1. **Chrome 68 호환**: async/await 사용 가능하지만 주의 필요
|
||||
2. **항상 resolve**: reject 사용하지 않음
|
||||
3. **success 플래그**: 반드시 `result.success` 확인 필요
|
||||
|
||||
---
|
||||
|
||||
**다음**: [해결 방법 3: 큐 기반 패널 액션 시스템 →](./04-solution-queue-system.md)
|
||||
Reference in New Issue
Block a user