Files
shoptime/com.twin.app.shoptime/src/actions/homeActions.js
optrader 18c3ac3ad5 [251209] fix: 백화현상 디버깅-1
🕐 커밋 시간: 2025. 12. 09. 18:18:51

📊 변경 통계:
  • 총 파일: 2개
  • 추가: +28줄
  • 삭제: -4줄

📝 수정된 파일:
  ~ com.twin.app.shoptime/src/actions/homeActions.js
  ~ com.twin.app.shoptime/src/api/TAxios.js

🔧 주요 변경 내용:
  • 핵심 비즈니스 로직 개선
  • API 서비스 레이어 개선
  • 소규모 기능 개선
2025-12-09 18:18:51 +09:00

599 lines
17 KiB
JavaScript

import { URLS } from '../api/apiConfig';
import { TAxios, TAxiosPromise } from '../api/TAxios';
import { types } from './actionTypes';
import { changeAppStatus, getTermsAgreeYn } from './commonActions';
import { collectBannerPositions } from '../utils/domUtils';
import { createDebugHelpers } from '../utils/debug';
import { setHidePopup, setShowPopup } from './commonActions';
import { ACTIVE_POPUP } from '../utils/Config';
// 디버그 헬퍼 설정
const DEBUG_MODE = false;
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);
// 약관 정보 조회 IF-LGSP-005
export const getHomeTerms = (props) => (dispatch, getState) => {
const { trmsTpCdList, mbrNo } = props;
const onSuccess = (response) => {
dlog('getHomeTerms onSuccess ', response.data);
if (response.data.retCode === 0) {
dispatch({
type: types.GET_HOME_TERMS,
payload: response.data,
});
// 약관 ID 매핑을 별도로 생성하여 저장
// Chromium68 호환성을 위해 Optional Chaining 제거
if (response.data && response.data.data && response.data.data.terms) {
const termsIdMap = {};
let hasOptionalTerms = false; // MST00405 존재 여부 확인
response.data.data.terms.forEach((term) => {
if (term.trmsTpCd && term.trmsId) {
termsIdMap[term.trmsTpCd] = term.trmsId;
}
// MST00405 선택약관 존재 여부 확인
if (term.trmsTpCd === 'MST00405') {
hasOptionalTerms = true;
}
});
dispatch({
type: types.SET_TERMS_ID_MAP,
payload: termsIdMap,
});
// 선택약관 존재 여부 상태 설정
// TODO: 테스트용 - 임시로 false 강제 설정
const forceDisableOptionalTerms = false; // 테스트 완료 후 false로 변경
const finalOptionalTermsValue = forceDisableOptionalTerms ? false : hasOptionalTerms;
dispatch({
type: types.SET_OPTIONAL_TERMS_AVAILABILITY,
payload: finalOptionalTermsValue,
});
dlog(
'[optionalTermsAvailable] 실제값:',
hasOptionalTerms,
'강제설정값:',
finalOptionalTermsValue
);
if (process.env.NODE_ENV === 'development') {
dlog('약관 ID 매핑 생성:', termsIdMap);
dlog('선택약관 존재 여부:', hasOptionalTerms);
}
}
setTimeout(() => {
dispatch(getTermsAgreeYn());
}, 0);
}
};
const onFail = (error) => {
derror('getHomeTerms onFail ', error);
// TODO: 임시 디버그용 팝업 (재현 후 제거하세요)
const retCode = error?.data?.retCode ?? error?.retCode ?? 'unknown';
dispatch(
setShowPopup(ACTIVE_POPUP.toast, {
button1Text: `getHomeTerms onFail retCode=${retCode}`,
button2Text: 'OK',
})
);
setTimeout(() => dispatch(setHidePopup()), 1500);
// 약관 미동의(retCode 501)로 GET_HOME_TERMS가 실패하면
// introTermsAgree를 명시적으로 false로 내려 앱이 IntroPanel을 띄우도록 한다.
if (retCode === 501) {
dispatch({
type: types.GET_TERMS_AGREE_YN_SUCCESS,
payload: {
privacyTerms: 'N',
serviceTerms: 'N',
purchaseTerms: 'N',
paymentTerms: 'N',
optionalTerms: 'N',
},
});
}
// 실패 시 로딩 패널을 반드시 내려 백화 상태를 방지
dispatch(
changeAppStatus({
showLoadingPanel: { show: false },
})
);
};
TAxios(
dispatch,
getState,
'get',
URLS.GET_HOME_TERMS,
{ trmsTpCdList, mbrNo },
{},
onSuccess,
onFail
);
};
// 현재 로그인 사용자 기준으로 약관 정보 조회 (인자 없이 호출 가능)
export const fetchCurrentUserHomeTerms = () => (dispatch, getState) => {
const loginUserData = getState().common.appStatus.loginUserData;
if (!loginUserData || !loginUserData.userNumber) {
derror(
'fetchCurrentUserHomeTerms: userNumber (mbrNo) is not available. User might not be logged in.'
);
dispatch({ type: types.GET_TERMS_AGREE_YN_FAILURE });
return;
}
const mbrNo = loginUserData.userNumber;
const trmsTpCdList = 'MST00401, MST00402, MST00405'; // 기본 약관 코드 리스트
const onSuccess = (response) => {
dlog('fetchCurrentUserHomeTerms onSuccess ', response.data);
if (response.data.retCode === 0) {
dispatch({
type: types.GET_HOME_TERMS, // 기존 GET_HOME_TERMS 타입을 재사용
payload: response.data,
});
// 약관 ID 매핑을 별도로 생성하여 저장
// Chromium68 호환성을 위해 Optional Chaining 제거
if (response.data && response.data.data && response.data.data.terms) {
const termsIdMap = {};
let hasOptionalTerms = false; // MST00405 존재 여부 확인
response.data.data.terms.forEach((term) => {
if (term.trmsTpCd && term.trmsId) {
termsIdMap[term.trmsTpCd] = term.trmsId;
}
// MST00405 선택약관 존재 여부 확인
if (term.trmsTpCd === 'MST00405') {
hasOptionalTerms = true;
}
});
dispatch({
type: types.SET_TERMS_ID_MAP,
payload: termsIdMap,
});
// 선택약관 존재 여부 상태 설정
// TODO: 테스트용 - 임시로 false 강제 설정
const forceDisableOptionalTerms = false; // 테스트 완료 후 false로 변경
const finalOptionalTermsValue = forceDisableOptionalTerms ? false : hasOptionalTerms;
dispatch({
type: types.SET_OPTIONAL_TERMS_AVAILABILITY,
payload: finalOptionalTermsValue,
});
dlog(
'[optionalTermsAvailable] 실제값:',
hasOptionalTerms,
'강제설정값:',
finalOptionalTermsValue
);
if (process.env.NODE_ENV === 'development') {
dlog('약관 ID 매핑 생성:', termsIdMap);
dlog('선택약관 존재 여부:', hasOptionalTerms);
}
}
// getHomeTerms와 동일하게 getTermsAgreeYn 후속 처리
setTimeout(() => {
dispatch(getTermsAgreeYn());
}, 0);
} else {
// retCode가 0이 아닌 경우 실패로 처리
dispatch({ type: types.GET_TERMS_AGREE_YN_FAILURE });
}
};
const onFail = (error) => {
derror('fetchCurrentUserHomeTerms onFail ', error);
dispatch({ type: types.GET_TERMS_AGREE_YN_FAILURE });
};
TAxios(
dispatch,
getState,
'get',
URLS.GET_HOME_TERMS, // 동일한 API 엔드포인트 사용
{ trmsTpCdList, mbrNo },
{},
onSuccess,
onFail
);
};
// 기존 TAxios 패턴과 일치하는 안전한 Redux Action
export const fetchCurrentUserHomeTermsSafe = () => async (dispatch, getState) => {
const loginUserData = getState().common.appStatus.loginUserData;
if (!loginUserData || !loginUserData.userNumber) {
derror('fetchCurrentUserHomeTerms: userNumber is not available');
dispatch({ type: types.GET_TERMS_AGREE_YN_FAILURE });
return { success: false, message: '사용자 정보가 없습니다.' };
}
const mbrNo = loginUserData.userNumber;
const trmsTpCdList = 'MST00401, MST00402, MST00405';
dlog('Fetching home terms for user:', mbrNo);
// 안전한 API 호출 (기존 TAxios 패턴과 동일)
const result = await TAxiosPromise(dispatch, getState, 'get', URLS.GET_HOME_TERMS, {
trmsTpCdList,
mbrNo,
});
// 네트워크 에러인 경우
if (!result.success) {
derror('fetchCurrentUserHomeTerms network error:', result.error);
dispatch({ type: types.GET_TERMS_AGREE_YN_FAILURE });
return { success: false, message: '네트워크 오류가 발생했습니다.' };
}
// 기존 TAxios처럼 특별한 retCode들은 TAxios 내부에서 이미 처리됨
// (401, 402, 501, 602, 603, 604 등은 TAxios에서 알아서 처리하고 onSuccess가 호출되지 않음)
dlog('fetchCurrentUserHomeTerms response:', result.data);
// 정상적으로 onSuccess가 호출된 경우에만 여기까지 옴
if (result.data && result.data.retCode === 0) {
dispatch({
type: types.GET_HOME_TERMS,
payload: result.data,
});
// 약관 ID 매핑을 별도로 생성하여 저장
// Chromium68 호환성을 위해 Optional Chaining 제거
if (result.data && result.data.data && result.data.data.terms) {
const termsIdMap = {};
let hasOptionalTerms = false; // MST00405 존재 여부 확인
result.data.data.terms.forEach((term) => {
if (term.trmsTpCd && term.trmsId) {
termsIdMap[term.trmsTpCd] = term.trmsId;
}
// MST00405 선택약관 존재 여부 확인
if (term.trmsTpCd === 'MST00405') {
hasOptionalTerms = true;
}
});
dispatch({
type: types.SET_TERMS_ID_MAP,
payload: termsIdMap,
});
// 선택약관 존재 여부 상태 설정 2025-07-03
// TODO: 테스트용 - 임시로 false 강제 설정
const forceDisableOptionalTerms = false; // 테스트 완료 후 false로 변경
const finalOptionalTermsValue = forceDisableOptionalTerms ? false : hasOptionalTerms;
dispatch({
type: types.SET_OPTIONAL_TERMS_AVAILABILITY,
payload: finalOptionalTermsValue,
});
if (process.env.NODE_ENV === 'development') {
dlog('약관 ID 매핑 생성:', termsIdMap);
dlog(
'선택약관 존재 여부 - 실제값:',
hasOptionalTerms,
'강제설정값:',
finalOptionalTermsValue
);
}
}
// 후속 액션 호출 (기존과 동일)
setTimeout(() => {
dispatch(getTermsAgreeYn());
}, 0);
return { success: true, data: result.data };
} else {
// retCode가 0이 아닌 일반적인 API 에러
// Chromium68 호환성을 위해 Optional Chaining 제거
derror('API returned non-zero retCode:', result.data && result.data.retCode);
dispatch({ type: types.GET_TERMS_AGREE_YN_FAILURE });
return {
success: false,
message: (result.data && result.data.retMsg) || '서버 오류가 발생했습니다.',
};
}
};
// 메뉴 목록 조회 IF-LGSP-044
export const getHomeMenu = () => (dispatch, getState) => {
const onSuccess = (response) => {
// dlog("getHomeMenu onSuccess ", response.data);
dispatch({
type: types.GET_HOME_MENU,
payload: response.data,
});
};
const onFail = (error) => {
derror('getHomeMenu onFail ', error);
};
TAxios(dispatch, getState, 'get', URLS.GET_HOME_MENU, {}, {}, onSuccess, onFail);
};
// 테마 전시 정보 상세 조회 IF-LGSP-060
export const getThemeCurationDetailInfo = (params) => (dispatch, getState) => {
const { patnrId, curationId, bgImgNo } = params;
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
const onSuccess = (response) => {
dlog('getThemeCurationDetailInfo onSuccess', response.data);
dispatch({
type: types.GET_THEME_CURATION_DETAIL_INFO,
payload: response.data.data,
});
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
const onFail = (error) => {
derror('getThemeCurationDetailInfo onFail', error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(
dispatch,
getState,
'get',
URLS.GET_THEME_CURATION_DETAIL_INFO,
{ patnrId, curationId, bgImgNo },
{},
onSuccess,
onFail
);
};
// 테마호텔 정보 상세 조회 IF-LGSP-085
export const getThemeHotelDetailInfo = (params) => (dispatch, getState) => {
const { patnrId, curationId } = params;
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
const onSuccess = (response) => {
dlog('getThemeHotelDetailInfo onSuccess', response.data);
dispatch({
type: types.GET_THEME_HOTEL_DETAIL_INFO,
payload: response.data.data,
});
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
const onFail = (error) => {
derror('getThemeHotelDetailInfo onFail', error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(
dispatch,
getState,
'get',
URLS.GET_THEME_HOTEL_DETAIL_INFO,
{ patnrId, curationId },
{},
onSuccess,
onFail
);
};
// HOME LAYOUT 정보 조회 IF-LGSP-300
export const getHomeLayout = () => (dispatch, getState) => {
const onSuccess = (response) => {
dlog('getHomeLayout onSuccess', response.data);
dispatch({
type: types.GET_HOME_LAYOUT,
payload: response.data.data,
});
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
const onFail = (error) => {
derror('getHomeLayout onFail', error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(dispatch, getState, 'get', URLS.GET_HOME_LAYOUT, {}, {}, onSuccess, onFail);
};
// HOME Main Contents Banner 정보 조회 IF-LGSP-301
export const getHomeMainContents = () => (dispatch, getState) => {
const onSuccess = (response) => {
dlog('getHomeMainContents onSuccess', response.data);
dispatch({
type: types.GET_HOME_MAIN_CONTENTS,
payload: response.data.data,
status: 'fulfilled',
});
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
const onFail = (error) => {
derror('getHomeMainContents onFail', error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(dispatch, getState, 'get', URLS.GET_HOME_MAIN_CONTENTS, {}, {}, onSuccess, onFail);
};
// Theme 전시 정보 조회 : IF-LGSP-045
export const getThemeCurationInfo = () => (dispatch, getState) => {
const onSuccess = (response) => {
dlog('getThemeCurationInfo onSuccess', response.data);
dispatch({
type: types.GET_THEME_CURATION_INFO,
payload: response.data.data,
retCode: response.data.retCode,
});
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
const onFail = (error) => {
derror('getThemeCurationInfo onFail', error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(dispatch, getState, 'get', URLS.GET_THEME_CURATION_INFO, {}, {}, onSuccess, onFail);
};
// 테마 메뉴(=테마 페이지) 선반 조회 : IF-LGSP-095
export const getThemeMenuShelfInfo = (props) => (dispatch, getState) => {
const { curationId } = props;
dispatch(changeAppStatus({ showLoadingPanel: { show: true, type: 'wait' } }));
const onSuccess = (response) => {
dlog('getThemeMenuShelfInfo onSuccess', response.data);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
dispatch({
type: types.GET_THEME_MENU_SHELF_INFO,
payload: response.data.data,
});
};
const onFail = (error) => {
derror('getThemeMenuShelfInfo onFail', error);
dispatch(changeAppStatus({ showLoadingPanel: { show: false } }));
};
TAxios(
dispatch,
getState,
'get',
URLS.GET_THEME_MENU_SHELF_INFO,
{ curationId },
{},
onSuccess,
onFail
);
};
export const clearThemeMenuShelfInfo = () => ({
type: types.CLEAR_THEME_MENU_SHELF_INFO,
});
export const resetHomeInfo = () => ({
type: types.RESET_HOME_INFO,
});
export const updateHomeInfo = (homeInfo) => {
return {
type: types.UPDATE_HOME_INFO,
payload: homeInfo,
};
};
export const clearThemeDetail = () => ({
type: types.CLEAR_THEME_DETAIL,
});
export const setDefaultFocus = (focus) => ({
type: types.SET_DEFAULT_FOCUS,
payload: focus,
});
export const setVideoTransitionLock = (isLocked) => ({
type: types.SET_VIDEO_TRANSITION_LOCK,
payload: Boolean(isLocked),
});
export const checkEnterThroughGNB = (boolean) => ({
type: types.CHECK_ENTER_THROUGH_GNB,
payload: boolean,
});
export const setBannerIndex = (bannerId, index) => {
if (!bannerId) {
dwarn('setBannerIndex called with undefined bannerId');
return { type: 'NO_OP' };
}
return {
type: types.SET_BANNER_INDEX,
payload: { bannerId, index },
};
};
// 🔽 [추가] 새로운 배너 위치 및 영구재생 관련 액션들
/**
* 모든 배너의 위치 정보를 설정합니다.
* @param {Object} positions - 배너별 위치 정보 맵
*/
export const setBannerPositions = (positions) => ({
type: types.SET_BANNER_POSITIONS,
payload: positions,
});
/**
* 특정 배너의 위치 정보를 업데이트합니다.
* @param {string} bannerId - 배너 ID
* @param {Object} position - 위치 정보
*/
export const updateBannerPosition = (bannerId, position) => ({
type: types.UPDATE_BANNER_POSITION,
payload: { bannerId, position },
});
/**
* 영구재생 비디오 정보를 설정합니다.
* @param {Object} videoInfo - 비디오 정보
*/
export const setPersistentVideoInfo = (videoInfo) => ({
type: types.SET_PERSISTENT_VIDEO_INFO,
payload: videoInfo,
});
/**
* 영구재생 비디오 정보를 클리어합니다.
*/
export const clearPersistentVideoInfo = () => ({
type: types.CLEAR_PERSISTENT_VIDEO_INFO,
});
/**
* 모든 배너의 현재 DOM 위치를 수집하여 Redux 스토어에 저장합니다.
* @param {Array<string>} bannerIds - 수집할 배너 ID 배열
*/
export const collectAndSaveBannerPositions = (bannerIds) => async (dispatch) => {
try {
const positions = await collectBannerPositions(bannerIds);
dispatch(setBannerPositions(positions));
if (process.env.NODE_ENV === 'development') {
dlog('[homeActions] 배너 위치 수집 완료:', positions);
}
} catch (error) {
derror('[homeActions] 배너 위치 수집 실패:', error);
}
};