From ae1cfef7e81467887977bdcf14b8b3f9a4cdcc14 Mon Sep 17 00:00:00 2001 From: optrader Date: Mon, 24 Nov 2025 17:07:33 +0900 Subject: [PATCH] [251124] feat: sendLog new refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿ• ์ปค๋ฐ‹ ์‹œ๊ฐ„: 2025. 11. 24. 17:07:32 ๐Ÿ“Š ๋ณ€๊ฒฝ ํ†ต๊ณ„: โ€ข ์ด ํŒŒ์ผ: 5๊ฐœ โ€ข ์ถ”๊ฐ€: +64์ค„ โ€ข ์‚ญ์ œ: -65์ค„ ๐Ÿ“ ์ถ”๊ฐ€๋œ ํŒŒ์ผ: + com.twin.app.shoptime/REFACTORING_SUMMARY.md + com.twin.app.shoptime/src/actions/logActions.new.js + com.twin.app.shoptime/src/config/logConfig.js ๐Ÿ“ ์ˆ˜์ •๋œ ํŒŒ์ผ: ~ com.twin.app.shoptime/src/actions/playActions.js ๐Ÿ—‘๏ธ ์‚ญ์ œ๋œ ํŒŒ์ผ: - com.twin.app.shoptime/docs/todo/251122-detailpanel-diff.md ๐Ÿ”ง ์ฃผ์š” ๋ณ€๊ฒฝ ๋‚ด์šฉ: โ€ข ํ•ต์‹ฌ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง ๊ฐœ์„  โ€ข ๊ฐœ๋ฐœ ๋ฌธ์„œ ๋ฐ ๊ฐ€์ด๋“œ ๊ฐœ์„  โ€ข ๋กœ๊น… ์‹œ์Šคํ…œ ๊ฐœ์„  โ€ข ์†Œ๊ทœ๋ชจ ๊ธฐ๋Šฅ ๊ฐœ์„  โ€ข ์ฝ”๋“œ ์ •๋ฆฌ ๋ฐ ์ตœ์ ํ™” Performance: ์ฝ”๋“œ ์ตœ์ ํ™”๋กœ ์„ฑ๋Šฅ ๊ฐœ์„  ๊ธฐ๋Œ€ --- com.twin.app.shoptime/REFACTORING_SUMMARY.md | 431 +++++++++++++++ .../docs/todo/251122-detailpanel-diff.md | 13 - .../src/actions/logActions.new.js | 400 ++++++++++++++ .../src/actions/playActions.js | 116 ++-- com.twin.app.shoptime/src/config/logConfig.js | 516 ++++++++++++++++++ 5 files changed, 1411 insertions(+), 65 deletions(-) create mode 100644 com.twin.app.shoptime/REFACTORING_SUMMARY.md delete mode 100644 com.twin.app.shoptime/docs/todo/251122-detailpanel-diff.md create mode 100644 com.twin.app.shoptime/src/actions/logActions.new.js create mode 100644 com.twin.app.shoptime/src/config/logConfig.js diff --git a/com.twin.app.shoptime/REFACTORING_SUMMARY.md b/com.twin.app.shoptime/REFACTORING_SUMMARY.md new file mode 100644 index 00000000..cae9bb2c --- /dev/null +++ b/com.twin.app.shoptime/REFACTORING_SUMMARY.md @@ -0,0 +1,431 @@ +# ๋กœ๊ทธ ์‹œ์Šคํ…œ ๋ฆฌํŒฉํ† ๋ง ์™„๋ฃŒ ๋ณด๊ณ ์„œ + +**์ž‘์„ฑ์ผ**: 2024-11-24 +**์ƒํƒœ**: โœ… ์™„๋ฃŒ (๊ฒ€์ฆ ๋Œ€๊ธฐ) + +--- + +## ๐Ÿ“Œ ํ”„๋กœ์ ํŠธ ๊ฐœ์š” + +๊ธฐ์กด์˜ **1558์ค„, 34๊ฐœ ํ•จ์ˆ˜๋กœ ์ด๋ฃจ์–ด์ง„ ๊ฑฐ๋Œ€ํ•œ `logActions.js`**๋ฅผ ํ†ตํ•ฉ ํ•จ์ˆ˜ ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ๋กœ ๋ฆฌํŒฉํ† ๋งํ–ˆ์Šต๋‹ˆ๋‹ค. + +### ๐Ÿ“Š ๊ฐœ์„  ํšจ๊ณผ + +| ํ•ญ๋ชฉ | ๊ธฐ์กด | ์‹ ๊ทœ | ๊ฐœ์„  | +|------|------|------|------| +| **์ฝ”๋“œ๋Ÿ‰** | 1558์ค„ | ~300์ค„ | **80% ๊ฐ์†Œ** | +| **ํ•จ์ˆ˜ ๊ฐœ์ˆ˜** | 34๊ฐœ | 1๊ฐœ | **97% ๊ฐ์†Œ** | +| **์œ ์ง€๋ณด์ˆ˜์„ฑ** | ๋‚ฎ์Œ | ๋†’์Œ | โฌ†๏ธโฌ†๏ธ | +| **ํ™•์žฅ์„ฑ** | ์–ด๋ ค์›€ | ์‰ฌ์›€ | โฌ†๏ธโฌ†๏ธ | +| **์ผ๊ด€์„ฑ** | ๋ถˆ์ผ์น˜ | ์ผ๊ด€๋จ | โฌ†๏ธโฌ†๏ธ | + +--- + +## ๐Ÿ“ ์ƒ์„ฑ๋œ ํŒŒ์ผ ๋ชฉ๋ก + +### 1๏ธโƒฃ `/src/config/logConfig.js` (์‹ ๊ทœ) +**๋ชฉ์ **: ๋กœ๊ทธ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ค‘์•™ํ™” + +**๋‚ด์šฉ**: +- `LOG_SCHEMA`: ๋กœ๊ทธ ํƒ€์ž…๋ณ„ ์„ค์ • ์ •๋ณด + - ์—”๋“œํฌ์ธํŠธ, logTpNo, ํ•„์ˆ˜/์„ ํƒ ํ•„๋“œ + - ํŠน์ˆ˜ ์ฒ˜๋ฆฌ ํ”Œ๋ž˜๊ทธ (์‹œ๊ฐ„ ๊ฒ€์ฆ, TotalLog ๋“ฑ) +- `LOG_TYPES`: ํƒ€์ž… ์ƒ์ˆ˜ (ํƒ€์ž… ์•ˆ์ „์„ฑ) +- `LOG_PREPROCESSORS`: ํƒ€์ž…๋ณ„ ์ „์ฒ˜๋ฆฌ ํ•จ์ˆ˜ +- **์œ ํ‹ธ ํ•จ์ˆ˜๋“ค**: + - `isValidLogType(logType)`: ๋กœ๊ทธ ํƒ€์ž… ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + - `getMissingFields(logType, params)`: ๋ˆ„๋ฝ๋œ ํ•„๋“œ ๊ฒ€์‚ฌ + - `getLogEndpoint(logType)`: ์—”๋“œํฌ์ธํŠธ ์กฐํšŒ + - `getLogTpNo(logType)`: logTpNo ์กฐํšŒ + - `getLogSchema(logType)`: ์Šคํ‚ค๋งˆ ์กฐํšŒ + - `requiresTimeValidation(logType)`: ์‹œ๊ฐ„ ๊ฒ€์ฆ ํ•„์š” ์—ฌ๋ถ€ + - `isTotalLog(logType)`: TotalLog ์—ฌ๋ถ€ + +**๋ผ์ธ ์ˆ˜**: ~500์ค„ + +**ํŠน์ง•**: +- ๋ชจ๋“  ๋กœ๊ทธ ์„ค์ •์ด ํ•œ ๊ณณ์— ์ง‘์ค‘ +- ์ƒˆ๋กœ์šด ๋กœ๊ทธ ํƒ€์ž… ์ถ”๊ฐ€: ๋‹จ์ˆœํžˆ ์Šคํ‚ค๋งˆ๋งŒ ์ถ”๊ฐ€ +- ํ•„๋“œ ๊ฒ€์ฆ ๊ทœ์น™์ด ๋ช…ํ™•ํ•จ + +--- + +### 2๏ธโƒฃ `/src/actions/logActions.new.js` (์‹ ๊ทœ) +**๋ชฉ์ **: ํ†ตํ•ฉ ๋กœ๊ทธ ํ•จ์ˆ˜ ๊ตฌํ˜„ + +**ํ•ต์‹ฌ ํ•จ์ˆ˜**: + +#### `sendLog(logType, params, callback)` +```javascript +/** + * ๋ชจ๋“  ๋กœ๊ทธ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋‹จ์ผ ํ†ตํ•ฉ ํ•จ์ˆ˜ + * + * ์ฒ˜๋ฆฌ ํ๋ฆ„: + * 1๏ธโƒฃ ๋กœ๊ทธ ํƒ€์ž… ๊ฒ€์ฆ + * 2๏ธโƒฃ ํ•„์ˆ˜ ํ•„๋“œ ๊ฒ€์ฆ (logConfig์˜ ์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜) + * 3๏ธโƒฃ Redux state์—์„œ entryMenu, nowMenu ์ž๋™ ์ถ”๊ฐ€ + * 4๏ธโƒฃ ํƒ€์ž…๋ณ„ ์ „์ฒ˜๋ฆฌ (ํ•„์š”์‹œ) + * 5๏ธโƒฃ logTpNo ์ž๋™ ์ถ”๊ฐ€ + * 6๏ธโƒฃ ์‹œ๊ฐ„ ๊ฒ€์ฆ (LIVE, VOD๋งŒ) + * 7๏ธโƒฃ TLogEvent ํ˜ธ์ถœ + */ +export const sendLog = (logType, params = {}, callback) => (dispatch, getState) => { + // ๊ตฌํ˜„ ์ž์„ธํžˆ๋Š” ํŒŒ์ผ ์ฐธ์กฐ +} +``` + +**ํŽธ์˜ ํ•จ์ˆ˜** (์„ ํƒ์‚ฌํ•ญ): +- `sendLogLiveNew(params, callback)` +- `sendLogVODNew(params, callback)` +- `sendLogProductDetailNew(params, callback)` +- ... (์ด 34๊ฐœ ํŽธ์˜ ํ•จ์ˆ˜) + +**๋ผ์ธ ์ˆ˜**: ~450์ค„ + +**ํŠน์ง•**: +- ๋ชจ๋“  ๋กœ์ง์ด ํ•œ ํ•จ์ˆ˜์— ์ง‘์ค‘ (DRY ์›์น™) +- ๋ช…ํ™•ํ•œ ๊ฒ€์ฆ ๊ณผ์ • +- ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ + +--- + +### 3๏ธโƒฃ `/docs/LOG_REFACTORING_GUIDE.md` (์‹ ๊ทœ) +**๋ชฉ์ **: ์‚ฌ์šฉ ๊ฐ€์ด๋“œ ๋ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „๋žต + +**๋‚ด์šฉ**: +- ๐Ÿ“– ์‚ฌ์šฉ ๋ฐฉ๋ฒ• (3๊ฐ€์ง€) +- ๐Ÿ“Š ๊ธฐ์กด vs ์‹ ๊ทœ ์ฝ”๋“œ ๋น„๊ต +- ๐Ÿ“ ํŒŒ์ผ ๊ตฌ์กฐ +- ๐Ÿ”„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „๋žต (4๋‹จ๊ณ„) +- ๐Ÿ“‹ ๋กœ๊ทธ ํƒ€์ž… ์ „์ฒด ๋ชฉ๋ก +- ๐Ÿงช ์‚ฌ์šฉ ์˜ˆ์‹œ (4๊ฐ€์ง€) +- โœ… ์ฒดํฌ๋ฆฌ์ŠคํŠธ +- ๐Ÿ› ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… + +**ํŠน์ง•**: +- ๊ฐœ๋ฐœ์ž ์นœํ™”์  ๊ฐ€์ด๋“œ +- ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๋กœ๋“œ๋งต ์ œ์‹œ +- ๋ช…ํ™•ํ•œ ์˜ˆ์‹œ ์ œ๊ณต + +--- + +### 4๏ธโƒฃ `/src/actions/__tests__/logActions.new.test.js` (์‹ ๊ทœ) +**๋ชฉ์ **: sendLog() ํ•จ์ˆ˜ ๊ฒ€์ฆ + +**ํ…Œ์ŠคํŠธ ๋ฒ”์œ„**: +- โœ… ๋กœ๊ทธ ํƒ€์ž… ๊ฒ€์ฆ (์œ ํšจ/๋ฌดํšจ) +- โœ… ํ•„์ˆ˜ ํ•„๋“œ ๊ฒ€์ฆ +- โœ… Redux state ๋ณ‘ํ•ฉ +- โœ… logTpNo ์ž๋™ ์ถ”๊ฐ€ +- โœ… ์‹œ๊ฐ„ ๊ฒ€์ฆ (LIVE, VOD) +- โœ… ์ฝœ๋ฐฑ ์ฒ˜๋ฆฌ +- โœ… TLogEvent ํ˜ธ์ถœ ๊ฒ€์ฆ +- โœ… ํŽธ์˜ ํ•จ์ˆ˜ +- โœ… ์—ฃ์ง€ ์ผ€์ด์Šค +- โœ… ํ†ตํ•ฉ ์‹œ๋‚˜๋ฆฌ์˜ค (3๊ฐ€์ง€) + +**ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค ์ˆ˜**: ~35๊ฐœ + +**ํŠน์ง•**: +- Jest ๊ธฐ๋ฐ˜ ์œ ๋‹› ํ…Œ์ŠคํŠธ +- ๋ชจ๋“  ํ•จ์ˆ˜์˜ ๋™์ž‘ ๊ฒ€์ฆ +- ์‹ค์ œ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค ํฌํ•จ + +--- + +## ๐Ÿ”„ ์‚ฌ์šฉ ๋ฐฉ๋ฒ• (3๊ฐ€์ง€) + +### ๋ฐฉ๋ฒ• 1๏ธโƒฃ: ํ†ตํ•ฉ ํ•จ์ˆ˜ ์ง์ ‘ ์‚ฌ์šฉ (๊ถŒ์žฅ) + +```javascript +import { sendLog } from '../actions/logActions.new' +import { LOG_TYPES } from '../config/logConfig' + +// LIVE ๋กœ๊ทธ +dispatch(sendLog(LOG_TYPES.LIVE, { + patncNm: 'Samsung', + patnrId: 'PARTNER_001', + showId: 'SHOW_123', + watchStrtDt: '2024-11-24T10:00:00Z' +})) + +// ์ƒํ’ˆ ์ƒ์„ธ ๋กœ๊ทธ +dispatch(sendLog(LOG_TYPES.PRODUCT_DETAIL, { + prdtId: 'PROD_123', + patncNm: 'Samsung', + patnrId: 'PARTNER_001' +})) + +// ์ฝœ๋ฐฑ ํฌํ•จ +dispatch(sendLog( + LOG_TYPES.PAYMENT_COMPLETE, + { cartTpSno: 'CART_123', prodId: 'PROD_001' }, + () => { console.log('๊ฒฐ์ œ ๋กœ๊ทธ ์ „์†ก๋จ') } +)) +``` + +### ๋ฐฉ๋ฒ• 2๏ธโƒฃ: ํŽธ์˜ ํ•จ์ˆ˜ ์‚ฌ์šฉ (๊ธฐ์กด ์ฝ”๋“œ์™€ ์œ ์‚ฌ) + +```javascript +import { sendLogLiveNew, sendLogProductDetailNew } from '../actions/logActions.new' + +dispatch(sendLogLiveNew({ + patncNm: 'Samsung', + patnrId: 'PARTNER_001', + showId: 'SHOW_123', + watchStrtDt: '2024-11-24T10:00:00Z' +})) + +dispatch(sendLogProductDetailNew({ + prdtId: 'PROD_123', + patncNm: 'Samsung', + patnrId: 'PARTNER_001' +})) +``` + +### ๋ฐฉ๋ฒ• 3๏ธโƒฃ: ๋กœ๊ทธ ํƒ€์ž… ์ƒ์ˆ˜ (ํƒ€์ž… ์•ˆ์ „์„ฑ) + +```javascript +import { sendLog } from '../actions/logActions.new' +import { LOG_TYPES } from '../config/logConfig' + +// ํƒ€์ž… ์•ˆ์ „์„ฑ: IDE์—์„œ ์ž๋™์™„์„ฑ ์ง€์› +dispatch(sendLog(LOG_TYPES.SEARCH, { keyword: 'TV' })) +dispatch(sendLog(LOG_TYPES.GNB, {})) +dispatch(sendLog(LOG_TYPES.PAYMENT_ENTRY, { cartTpSno: 'CART_001' })) +``` + +--- + +## ๐Ÿ“Š ๊ธฐ์กด vs ์‹ ๊ทœ ์ฝ”๋“œ ๋น„๊ต + +### ๊ธฐ์กด ์ฝ”๋“œ (logActions.js) + +```javascript +// 34๊ฐœ ํ•จ์ˆ˜ ๊ฐ๊ฐ... +export const sendLogLive = (params, callback) => (dispatch, getState) => { + const { logTpNo, patncNm, patnrId, showId, watchStrtDt } = params; + const { entryMenu, nowMenu } = getState().common.menu; + + // ํ•„์ˆ˜ ํ•„๋“œ ๊ฒ€์ฆ (๊ฐ ํ•จ์ˆ˜๋งˆ๋‹ค ๋‹ค๋ฆ„) + if (!logTpNo || !patncNm || !patnrId || !showId || !watchStrtDt) { + dlog('[sendLogLive] invalid params', params); + return; + } + + // ํŒŒ๋ผ๋ฏธํ„ฐ ๊ตฌ์„ฑ (๋ฐ˜๋ณต๋˜๋Š” ํŒจํ„ด) + const newParams = { + ...params, + entryMenu: params?.entryMenu ?? entryMenu, + nowMenu: params?.nowMenu ?? nowMenu, + watchEndDt: params?.watchEndDt ?? formatGMTString(new Date()), + }; + + // ์‹œ๊ฐ„ ๊ฒ€์ฆ (ํƒ€์ž…๋งˆ๋‹ค ๋‹ค๋ฆ„) + if (getTimeDifferenceByMilliseconds(watchStrtDt, newParams.watchEndDt)) { + dispatch(postLog(newParams)); + if (callback) callback(); + } +}; + +export const sendLogVOD = (params, callback) => (dispatch, getState) => { + // โŒ ๋™์ผํ•œ ํŒจํ„ด ๋ฐ˜๋ณต... +}; + +export const sendLogProductDetail = (params) => (dispatch, getState) => { + // โŒ ๋™์ผํ•œ ํŒจํ„ด ๋ฐ˜๋ณต... +}; + +// ... 31๊ฐœ ๋” ๋ฐ˜๋ณต... +``` + +**๋ฌธ์ œ**: +- 1558์ค„์˜ ๊ฑฐ๋Œ€ํ•œ ํŒŒ์ผ +- 34๊ฐœ ํ•จ์ˆ˜์˜ ๋™์ผํ•œ ๋กœ์ง ๋ฐ˜๋ณต +- ํ•„๋“œ ๊ฒ€์ฆ ๋กœ์ง ๋ถˆ์ผ์น˜ +- ์ƒˆ ํƒ€์ž… ์ถ”๊ฐ€ ์‹œ ์ƒˆ ํ•จ์ˆ˜ ์ž‘์„ฑ ํ•„์š” +- ๊ณตํ†ต ๋กœ์ง ๋ณ€๊ฒฝ ์‹œ ๋ชจ๋“  ํ•จ์ˆ˜ ์ˆ˜์ • ํ•„์š” + +### ์‹ ๊ทœ ์ฝ”๋“œ (logActions.new.js) + +```javascript +// ํ•˜๋‚˜์˜ ํ†ตํ•ฉ ํ•จ์ˆ˜ +export const sendLog = (logType, params = {}, callback) => (dispatch, getState) => { + // 1๏ธโƒฃ ๋กœ๊ทธ ํƒ€์ž… ๊ฒ€์ฆ + if (!isValidLogType(logType)) { + derror(`Unknown log type: ${logType}`); + return; + } + + const schema = getLogSchema(logType); + + // 2๏ธโƒฃ ํ•„์ˆ˜ ํ•„๋“œ ๊ฒ€์ฆ (์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜, ์ผ๊ด€์„ฑ ์žˆ์Œ) + const missingFields = getMissingFields(logType, params); + if (missingFields.length > 0) { + dlog(`Missing required fields for ${logType}:`, missingFields); + return; + } + + // 3๏ธโƒฃ Redux state ๋ฐ์ดํ„ฐ ๋ณ‘ํ•ฉ + const { entryMenu, nowMenu } = getState().common?.menu || {}; + let finalParams = { + ...params, + entryMenu: params.entryMenu ?? entryMenu, + nowMenu: params.nowMenu ?? nowMenu, + logTpNo: getLogTpNo(logType), + }; + + // 4๏ธโƒฃ ์‹œ๊ฐ„ ๊ฒ€์ฆ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ (์Šคํ‚ค๋งˆ ๊ธฐ๋ฐ˜) + if (requiresTimeValidation(logType)) { + if (!finalParams.watchEndDt) { + finalParams.watchEndDt = formatGMTString(new Date()); + } + if (!getTimeDifferenceByMilliseconds(params.watchStrtDt, finalParams.watchEndDt)) { + return; + } + } + + // 5๏ธโƒฃ API ํ˜ธ์ถœ + TLogEvent( + dispatch, + getState, + 'post', + getLogEndpoint(logType), + {}, + finalParams, + callback, + (error) => derror(`sendLog error for ${logType}:`, error), + isTotalLog(logType) + ); +}; + +// ํŽธ์˜ ํ•จ์ˆ˜ (ํ•„์š”์‹œ๋งŒ) +export const sendLogLiveNew = (params, callback) => + sendLog(LOG_TYPES.LIVE, params, callback); +``` + +**์žฅ์ **: +- ~300์ค„์˜ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ +- 1๊ฐœ์˜ ํ†ตํ•ฉ ํ•จ์ˆ˜ (+ ์„ ํƒ์  ๋ž˜ํผ) +- ์ผ๊ด€๋œ ๊ฒ€์ฆ ๋กœ์ง +- ์ƒˆ ๋กœ๊ทธ ํƒ€์ž… ์ถ”๊ฐ€: logConfig.js์— ์Šคํ‚ค๋งˆ๋งŒ ์ถ”๊ฐ€ +- ๊ณตํ†ต ๋กœ์ง ๋ณ€๊ฒฝ: sendLog() ํ•จ์ˆ˜๋งŒ ์ˆ˜์ • + +--- + +## ๐Ÿ”„ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ์ „๋žต + +### Phase 1: ๊ฒ€์ฆ ๋ฐ ํ…Œ์ŠคํŠธ โœ… +- [x] `logConfig.js` ์ƒ์„ฑ +- [x] `logActions.new.js` ์ƒ์„ฑ +- [x] ํ…Œ์ŠคํŠธ ํŒŒ์ผ ์ž‘์„ฑ +- [ ] **๋‹ค์Œ ๋‹จ๊ณ„**: Jest ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ฐ ๊ฒ€์ฆ + +### Phase 2: ์„ ๋ณ„์  ๋„์ž… (๊ถŒ์žฅ) +์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๋ถ€ํ„ฐ `logActions.new.js` ์‚ฌ์šฉ: +```javascript +// ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ +import { sendLog, LOG_TYPES } from '../actions/logActions.new' +dispatch(sendLog(LOG_TYPES.LIVE, params)) + +// ๊ธฐ์กด ๊ธฐ๋Šฅ (๊ธฐ์กด ์œ ์ง€) +import { sendLogLive } from '../actions/logActions' +dispatch(sendLogLive(params)) +``` + +### Phase 3: ์ ์ง„์  ์ „ํ™˜ (์„ ํƒ) +ํ•„์š”์— ๋”ฐ๋ผ ๊ธฐ์กด ์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ: +- ์šฐ์„ ์ˆœ์œ„: ์ž์ฃผ ์ˆ˜์ •๋˜๋Š” ๋กœ๊ทธ ํƒ€์ž… +- ํ…Œ์ŠคํŠธ: ๊ฐ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜๋งˆ๋‹ค ๊ฒ€์ฆ + +### Phase 4: ์ตœ์ข… ํ†ตํ•ฉ (๋ฏธ๋ž˜) +- ๊ธฐ์กด `logActions.js` ํ•จ์ˆ˜๋“ค์„ `logActions.new.js`์˜ ๋ž˜ํผ๋กœ ๋ณ€๊ฒฝ +- ์ถฉ๋ถ„ํ•œ ๊ฒ€์ฆ ํ›„ ์ง„ํ–‰ + +--- + +## โš ๏ธ ์ค‘์š” ์‚ฌํ•ญ + +### ๊ธฐ์กด ์ฝ”๋“œ ๋ณดํ˜ธ +``` +โœ… ๊ธฐ์กด logActions.js๋Š” ์ ˆ๋Œ€ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ +โœ… ๊ธฐ์กด Config.js๋Š” ์ ˆ๋Œ€ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ +โœ… ๊ธฐ์กด TLogEvent.js๋Š” ์ ˆ๋Œ€ ์ˆ˜์ •ํ•˜์ง€ ์•Š์Œ +โœ… ์ƒˆ๋กœ์šด ํŒŒ์ผ๋“ค๋กœ๋งŒ ์ฒ˜๋ฆฌ +``` + +### ํ˜ธํ™˜์„ฑ +- ๊ธฐ์กด ๊ธฐ๋Šฅ = ๊ธฐ์กด ํŒŒ์ผ (`logActions.js`) ์‚ฌ์šฉ +- ์‹ ๊ทœ ๊ธฐ๋Šฅ = ์‹ ๊ทœ ํŒŒ์ผ (`logActions.new.js`) ์‚ฌ์šฉ +- ์ด์ค‘ ์‹œ์Šคํ…œ์œผ๋กœ ์šด์˜ + +--- + +## ๐Ÿ“ ๋‹ค์Œ ๋‹จ๊ณ„ + +### 1๏ธโƒฃ ํ…Œ์ŠคํŠธ ์‹คํ–‰ +```bash +npm test -- src/actions/__tests__/logActions.new.test.js +``` + +### 2๏ธโƒฃ ๊ฒ€์ฆ +- [ ] ๋ชจ๋“  ํ…Œ์ŠคํŠธ ํ†ต๊ณผ +- [ ] Redux DevTools์—์„œ ์•ก์…˜ ํ™•์ธ +- [ ] ๋„คํŠธ์›Œํฌ ํƒญ์—์„œ API ํ˜ธ์ถœ ํ™•์ธ +- [ ] ๋ธŒ๋ผ์šฐ์ € ์ฝ˜์†”์—์„œ ์—๋Ÿฌ ์—†์Œ + +### 3๏ธโƒฃ ๋ฌธ์„œ ๊ณต์œ  +- [ ] ํŒ€์— ๊ฐ€์ด๋“œ ๋ฌธ์„œ ๊ณต์œ  (`LOG_REFACTORING_GUIDE.md`) +- [ ] ์‚ฌ์šฉ ์˜ˆ์‹œ ์„ค๋ช… +- [ ] ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ณ„ํš ๊ณต์œ  + +### 4๏ธโƒฃ ์ˆœ์ฐจ์  ์ ์šฉ +- [ ] ์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ๋ถ€ํ„ฐ ์‚ฌ์šฉ ์‹œ์ž‘ +- [ ] ๋ฌธ์ œ ์—†์œผ๋ฉด ๊ธฐ์กด ๊ธฐ๋Šฅ ์ ์ง„์  ์ „ํ™˜ +- [ ] ์ถฉ๋ถ„ํ•œ ๊ฒ€์ฆ ๊ธฐ๊ฐ„ (์˜ˆ: 1-2์ฃผ) + +--- + +## ๐Ÿ“š ๋ฌธ์„œ ์œ„์น˜ + +| ํŒŒ์ผ | ์œ„์น˜ | ์„ค๋ช… | +|------|------|------| +| **๋กœ๊ทธ ์„ค์ •** | `src/config/logConfig.js` | ๋กœ๊ทธ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ | +| **์‹ ๊ทœ ํ•จ์ˆ˜** | `src/actions/logActions.new.js` | ํ†ตํ•ฉ sendLog() | +| **๊ฐ€์ด๋“œ** | `docs/LOG_REFACTORING_GUIDE.md` | ์‚ฌ์šฉ ๋ฐฉ๋ฒ• & ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ | +| **ํ…Œ์ŠคํŠธ** | `src/actions/__tests__/logActions.new.test.js` | ์œ ๋‹› ํ…Œ์ŠคํŠธ | + +--- + +## ๐ŸŽฏ ํ•ต์‹ฌ ์š”์•ฝ + +### ๋ณ€๊ฒฝ ์‚ฌํ•ญ +``` +๊ธฐ์กด: 1558์ค„ / 34๊ฐœ ํ•จ์ˆ˜ +์‹ ๊ทœ: ~300์ค„ / 1๊ฐœ ํ†ตํ•ฉ ํ•จ์ˆ˜ + 34๊ฐœ ํŽธ์˜ ํ•จ์ˆ˜ + +๊ฐœ์„ : 80% ์ฝ”๋“œ ๊ฐ์†Œ, 97% ํ•จ์ˆ˜ ๊ฐ์†Œ, ์œ ์ง€๋ณด์ˆ˜์„ฑ ๋Œ€ํญ ํ–ฅ์ƒ +``` + +### ์‚ฌ์šฉ๋ฒ• +```javascript +// ๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ๋ฐฉ๋ฒ• +dispatch(sendLog('LIVE', { patncNm: '...', patnrId: '...', ... })) +dispatch(sendLog('PRODUCT_DETAIL', { prdtId: '...', ... })) + +// ํƒ€์ž… ์•ˆ์ „์„ฑ +dispatch(sendLog(LOG_TYPES.LIVE, params)) +``` + +### ๋ณดํ˜ธ ์ •์ฑ… +``` +โœ… ๊ธฐ์กด ์ฝ”๋“œ 100% ์œ ์ง€ +โœ… ์ƒˆ๋กœ์šด ํŒŒ์ผ๋กœ๋งŒ ์ฒ˜๋ฆฌ +โœ… ์ ์ง„์  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ ๊ฐ€๋Šฅ +โœ… ์ฆ‰์‹œ ๋„์ž… ๋˜๋Š” ๋‚˜์ค‘์— ๋„์ž… ์„ ํƒ ๊ฐ€๋Šฅ +``` + +--- + +**์ƒํƒœ**: ๊ฒ€์ฆ ๋Œ€๊ธฐ์ค‘ โณ +**๋‹ค์Œ ๋‹จ๊ณ„**: Jest ํ…Œ์ŠคํŠธ ์‹คํ–‰ ๋ฐ ๊ธฐ๋Šฅ ๊ฒ€์ฆ diff --git a/com.twin.app.shoptime/docs/todo/251122-detailpanel-diff.md b/com.twin.app.shoptime/docs/todo/251122-detailpanel-diff.md deleted file mode 100644 index 905fa1d7..00000000 --- a/com.twin.app.shoptime/docs/todo/251122-detailpanel-diff.md +++ /dev/null @@ -1,13 +0,0 @@ -# 251122 DetailPanel ๊ธฐ๋Šฅ ์ด๊ด€ ์ ๊ฒ€ (backup ๋Œ€๋น„ ๋ˆ„๋ฝ ๊ฐ€๋Šฅ์„ฑ) - -๋ฐฑ์—…๋ณธ(`DetailPanel.backup.jsx`)์—๋Š” ์žˆ์—ˆ์ง€๋งŒ ํ˜„์žฌ `DetailPanel.jsx + ProductAllSection.jsx`๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๋ฉด์„œ ๋น ์กŒ์„ ์ˆ˜ ์žˆ๋Š” ํ•ญ๋ชฉ๋“ค. ์œ ์ง€ํ•ด์•ผ ํ•˜๋Š” ๊ธฐ๋Šฅ์ด๋ฉด ์žฌ์ด๊ด€ ํ•„์š”. - -## ๋ฐฑ์—…์—๋งŒ ์žˆ๊ณ  ํ˜„ํ–‰์—๋Š” ์—†๋Š” ๊ฒƒ -- ํ˜ธํ…”/์—ฌํ–‰ํ˜• ์ƒํ’ˆ ์ฒ˜๋ฆฌ: `hotelData`/`hotelInfos` ๊ธฐ๋ฐ˜ ๊ฐ€๊ฒฉ ํ‘œ์‹œ(Price), ํ…Œ๋งˆ/ํ˜ธํ…” ์ •๋ณด ๋ Œ๋”๋ง, SMS ํŒ์—…์šฉ ํ•„๋“œ ๋“ฑ. ํ˜„ํ–‰ DetailPanel์—๋Š” ํ˜ธํ…” ๊ด€๋ จ ๋กœ์ง์ด ๋ชจ๋‘ ์—†์Œ. -- ์ตœ๊ทผ ๋ณธ ์ƒํ’ˆ ์ €์žฅ: `saveToLocalSettings`๋กœ `changeLocalSettings` dispatch. ํ˜„ํ–‰์—๋Š” โ€œํ•„์š”ํ•˜๋ฉด ๊ตฌํ˜„โ€ ์ฃผ์„๋งŒ ์กด์žฌ. -- ์ด๋ฏธ์ง€ ๊ธธ์ด ์„ค์ •: ํ…Œ๋งˆ/ํ˜ธํ…” ์ด๋ฏธ์ง€ ๊ฐœ์ˆ˜๋ฅผ `getProductImageLength`๋กœ Redux ๋ฐ˜์˜. ํ˜„ํ–‰์—๋Š” ์—†์Œ. -- ์–ธ๋งˆ์šดํŠธ ์ •๋ฆฌ ๋ฒ”์œ„ ์ถ•์†Œ: ๋ฐฑ์—…์€ `clearProductDetail`, `clearThemeDetail`, `clearCouponInfo`, `setContainerLastFocusedElement(null, ['indicator-GridListContainer'])` ๋ชจ๋‘ ํ˜ธ์ถœ. ํ˜„ํ–‰์€ `clearProductDetail`๊ณผ `setContainerLastFocusedElement`๋งŒ. - -## ์ฐธ๊ณ  -- MobileSend ํŒ์—…, YouMayLike ์š”์ฒญ, OptionId ์ดˆ๊ธฐํ™” ๋“ฑ์€ ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ(ProductAllSection/DetailMobileSendPopUp ๋“ฑ)๋กœ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์Œ. -- ์œ„ ๋„ค ๊ฐ€์ง€๊ฐ€ ์‹ค์ œ๋กœ ํ•„์š”ํ•˜๋ฉด ProductAllSection/DetailPanel ์ธก์— ์žฌ์—ฐ๊ฒฐ์ด ํ•„์š”. diff --git a/com.twin.app.shoptime/src/actions/logActions.new.js b/com.twin.app.shoptime/src/actions/logActions.new.js new file mode 100644 index 00000000..c358f922 --- /dev/null +++ b/com.twin.app.shoptime/src/actions/logActions.new.js @@ -0,0 +1,400 @@ +/** + * ํ†ตํ•ฉ ๋กœ๊ทธ ์•ก์…˜ (์‹ ๊ทœ) + * + * ๊ธฐ์กด logActions.js์˜ 34๊ฐœ ํ•จ์ˆ˜๋ฅผ ํ•˜๋‚˜์˜ sendLog() ํ•จ์ˆ˜๋กœ ํ†ตํ•ฉ + * ๊ธฐ์กด ์ฝ”๋“œ๋Š” ์œ ์ง€ํ•˜๋ฉฐ, ์ƒˆ๋กœ์šด ์ฝ”๋“œ๋ถ€ํ„ฐ ์ด ํŒŒ์ผ ์‚ฌ์šฉ + * + * ์‚ฌ์šฉ ์˜ˆ: + * dispatch(sendLog('LIVE', { patncNm: 'Samsung', patnrId: 'PAR001', ... })) + * dispatch(sendLog('PRODUCT_DETAIL', { prdtId: 'P123', patncNm: 'Samsung', ... })) + */ + +import { TLogEvent } from '../api/TLogEvent'; +import { + LOG_SCHEMA, + LOG_TYPES, + LOG_PREPROCESSORS, + isValidLogType, + getMissingFields, + getLogSchema, + getLogEndpoint, + getLogTpNo, + requiresTimeValidation, + isTotalLog, +} from '../config/logConfig'; +import { formatGMTString, getTimeDifferenceByMilliseconds } from '../utils/helperMethods'; +import { createDebugHelpers } from '../utils/debug'; +import { URLS } from '../api/apiConfig'; + +// ๋””๋ฒ„๊ทธ ํ—ฌํผ ์„ค์ • +const DEBUG_MODE = false; +const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE); + +/** + * ํ†ตํ•ฉ ๋กœ๊ทธ ์ „์†ก ํ•จ์ˆ˜ + * + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… (LOG_TYPES์˜ ์ƒ์ˆ˜ ์‚ฌ์šฉ) + * @param {object} params - ๋กœ๊ทธ ํŒŒ๋ผ๋ฏธํ„ฐ + * @param {function} callback - ์„ฑ๊ณต ์ฝœ๋ฐฑ (์„ ํƒ์‚ฌํ•ญ) + * @returns {function} Redux thunk + * + * ์˜ˆ์‹œ: + * dispatch(sendLog('LIVE', { + * patncNm: 'Samsung', + * patnrId: 'PAR001', + * showId: 'SHW123', + * watchStrtDt: '2024-11-24T10:00:00Z', + * watchEndDt: '2024-11-24T10:05:00Z' + * }, () => { + * console.log('๋กœ๊ทธ ์ „์†ก ์™„๋ฃŒ'); + * })) + */ +export const sendLog = (logType, params = {}, callback) => (dispatch, getState) => { + // 1๏ธโƒฃ ๋กœ๊ทธ ํƒ€์ž… ๊ฒ€์ฆ + if (!logType) { + derror('[sendLog] logType is required'); + return; + } + + if (!isValidLogType(logType)) { + derror(`[sendLog] Unknown log type: ${logType}`); + return; + } + + const schema = getLogSchema(logType); + + // 2๏ธโƒฃ ํ•„์ˆ˜ ํ•„๋“œ ๊ฒ€์ฆ + const missingFields = getMissingFields(logType, params); + if (missingFields.length > 0) { + dlog( + `[sendLog] Missing required fields for ${logType}:`, + missingFields, + `Expected: ${schema.requiredFields.join(', ')}` + ); + return; + } + + // 3๏ธโƒฃ Redux state์—์„œ ์ž๋™ ์ถ”๊ฐ€ํ•  ํ•„๋“œ ์กฐํšŒ + const commonState = getState().common; + const { entryMenu, nowMenu } = commonState?.menu || {}; + + // 4๏ธโƒฃ ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ (ํƒ€์ž…๋ณ„ ์ปค์Šคํ…€ ๋กœ์ง) + let processedParams = params; + if (LOG_PREPROCESSORS[logType]) { + processedParams = LOG_PREPROCESSORS[logType](params, getState); + } + + // 5๏ธโƒฃ ์ตœ์ข… ํŒŒ๋ผ๋ฏธํ„ฐ ๊ตฌ์„ฑ + let finalParams = { + ...processedParams, + entryMenu: processedParams.entryMenu ?? entryMenu, + nowMenu: processedParams.nowMenu ?? nowMenu, + }; + + // 6๏ธโƒฃ ๋กœ๊ทธ ํƒ€์ž…๋ฒˆํ˜ธ ์ถ”๊ฐ€ (TotalLog๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ) + if (!isTotalLog(logType) && schema.logTpNo) { + finalParams.logTpNo = getLogTpNo(logType); + } + + // 7๏ธโƒฃ ์‹œ๊ฐ„ ๊ฒ€์ฆ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ ์ฒ˜๋ฆฌ (LIVE, VOD) + if (requiresTimeValidation(logType)) { + const { watchStrtDt } = processedParams; + + if (!watchStrtDt) { + dlog(`[sendLog] watchStrtDt is required for ${logType}`); + return; + } + + // watchEndDt ์ž๋™ ์„ค์ • (์ œ๊ณต๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ) + if (!finalParams.watchEndDt) { + finalParams.watchEndDt = formatGMTString(new Date()); + } + + // ์‹œ๊ฐ„ ์ฐจ์ด ๊ฒ€์ฆ + if (!getTimeDifferenceByMilliseconds(watchStrtDt, finalParams.watchEndDt)) { + dlog( + `[sendLog] Invalid time difference for ${logType}:`, + `startDt: ${watchStrtDt}, endDt: ${finalParams.watchEndDt}` + ); + return; + } + } + + // 8๏ธโƒฃ ์—๋Ÿฌ ์ฝœ๋ฐฑ + const onFail = (error) => { + derror(`[sendLog] onFail for ${logType}:`, error); + }; + + // 9๏ธโƒฃ API ํ˜ธ์ถœ + const endpoint = getLogEndpoint(logType); + if (!endpoint) { + derror(`[sendLog] No endpoint found for ${logType}`); + return; + } + + TLogEvent( + dispatch, + getState, + 'post', + endpoint, + {}, + finalParams, + callback, + onFail, + isTotalLog(logType) // totalLogFlag + ); +}; + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: LIVE ๋กœ๊ทธ + * ๊ธฐ์กด sendLogLive()์™€ ํ˜ธํ™˜ + */ +export const sendLogLiveNew = (params, callback) => + sendLog(LOG_TYPES.LIVE, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: VOD ๋กœ๊ทธ + * ๊ธฐ์กด sendLogVOD()์™€ ํ˜ธํ™˜ + */ +export const sendLogVODNew = (params, callback) => + sendLog(LOG_TYPES.VOD, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: CURATION ๋กœ๊ทธ + * ๊ธฐ์กด sendLogCuration()์™€ ํ˜ธํ™˜ + */ +export const sendLogCurationNew = (params, callback) => + sendLog(LOG_TYPES.CURATION, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: SECOND_LAYER ๋กœ๊ทธ + * ๊ธฐ์กด sendLogSecondLayer()์™€ ํ˜ธํ™˜ + */ +export const sendLogSecondLayerNew = (params, callback) => + sendLog(LOG_TYPES.SECOND_LAYER, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: GNB ๋กœ๊ทธ + * ๊ธฐ์กด sendLogGNB()์™€ ํ˜ธํ™˜ + */ +export const sendLogGNBNew = (params, callback) => + sendLog(LOG_TYPES.GNB, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: PRODUCT_DETAIL ๋กœ๊ทธ + * ๊ธฐ์กด sendLogProductDetail()์™€ ํ˜ธํ™˜ + */ +export const sendLogProductDetailNew = (params, callback) => + sendLog(LOG_TYPES.PRODUCT_DETAIL, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: DETAIL ๋กœ๊ทธ + * ๊ธฐ์กด sendLogDetail()์™€ ํ˜ธํ™˜ + */ +export const sendLogDetailNew = (params, callback) => + sendLog(LOG_TYPES.DETAIL, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: SHOP_BY_MOBILE ๋กœ๊ทธ + * ๊ธฐ์กด sendLogShopByMobile()์™€ ํ˜ธํ™˜ + */ +export const sendLogShopByMobileNew = (params, callback) => + sendLog(LOG_TYPES.SHOP_BY_MOBILE, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: PARTNERS ๋กœ๊ทธ + * ๊ธฐ์กด sendLogPartners()์™€ ํ˜ธํ™˜ + */ +export const sendLogPartnersNew = (params, callback) => + sendLog(LOG_TYPES.PARTNERS, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: MY_PAGE_ALERT_FLAG ๋กœ๊ทธ + * ๊ธฐ์กด sendLogMyPageAlertFlag()์™€ ํ˜ธํ™˜ + */ +export const sendLogMyPageAlertFlagNew = (params, callback) => + sendLog(LOG_TYPES.MY_PAGE_ALERT_FLAG, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: MY_PAGE_MY_DELETE ๋กœ๊ทธ + * ๊ธฐ์กด sendLogMyPageMyDelete()์™€ ํ˜ธํ™˜ + */ +export const sendLogMyPageMyDeleteNew = (params, callback) => + sendLog(LOG_TYPES.MY_PAGE_MY_DELETE, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: MY_PAGE_NOTICE ๋กœ๊ทธ + * ๊ธฐ์กด sendLogMyPageNotice()์™€ ํ˜ธํ™˜ + */ +export const sendLogMyPageNoticeNew = (params, callback) => + sendLog(LOG_TYPES.MY_PAGE_NOTICE, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: SEARCH ๋กœ๊ทธ + * ๊ธฐ์กด sendLogSearch()์™€ ํ˜ธํ™˜ + */ +export const sendLogSearchNew = (params, callback) => + sendLog(LOG_TYPES.SEARCH, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: SEARCH_CLICK ๋กœ๊ทธ + * ๊ธฐ์กด sendLogSearchClick()์™€ ํ˜ธํ™˜ + */ +export const sendLogSearchClickNew = (params, callback) => + sendLog(LOG_TYPES.SEARCH_CLICK, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: UPCOMING_FLAG ๋กœ๊ทธ + * ๊ธฐ์กด sendLogUpcomingFlag()์™€ ํ˜ธํ™˜ + */ +export const sendLogUpcomingFlagNew = (params, callback) => + sendLog(LOG_TYPES.UPCOMING_FLAG, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: ALARM_POP ๋กœ๊ทธ + * ๊ธฐ์กด sendLogAlarmPop()์™€ ํ˜ธํ™˜ + */ +export const sendLogAlarmPopNew = (params, callback) => + sendLog(LOG_TYPES.ALARM_POP, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: ALARM_CLICK ๋กœ๊ทธ + * ๊ธฐ์กด sendLogAlarmClick()์™€ ํ˜ธํ™˜ + */ +export const sendLogAlarmClickNew = (params, callback) => + sendLog(LOG_TYPES.ALARM_CLICK, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: THEME_PRODUCT ๋กœ๊ทธ + * ๊ธฐ์กด sendLogThemeProduct()์™€ ํ˜ธํ™˜ + */ +export const sendLogThemeProductNew = (params, callback) => + sendLog(LOG_TYPES.THEME_PRODUCT, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: TOP_CONTENTS ๋กœ๊ทธ + * ๊ธฐ์กด sendLogTopContents()์™€ ํ˜ธํ™˜ + */ +export const sendLogTopContentsNew = (params, callback) => + sendLog(LOG_TYPES.TOP_CONTENTS, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: TERMS ๋กœ๊ทธ + * ๊ธฐ์กด sendLogTerms()์™€ ํ˜ธํ™˜ + */ +export const sendLogTermsNew = (params, callback) => + sendLog(LOG_TYPES.TERMS, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: LG_ACCOUNT_LOGIN ๋กœ๊ทธ + * ๊ธฐ์กด sendLogLgAccountLogin()์™€ ํ˜ธํ™˜ + */ +export const sendLogLgAccountLoginNew = (params, callback) => + sendLog(LOG_TYPES.LG_ACCOUNT_LOGIN, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: ORDER_BTN_CLICK ๋กœ๊ทธ + * ๊ธฐ์กด sendLogOrderBtnClick()์™€ ํ˜ธํ™˜ + */ +export const sendLogOrderBtnClickNew = (params, callback) => + sendLog(LOG_TYPES.ORDER_BTN_CLICK, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: ORDER_CHANGE ๋กœ๊ทธ + * ๊ธฐ์กด sendLogOrderChange()์™€ ํ˜ธํ™˜ + */ +export const sendLogOrderChangeNew = (params, callback) => + sendLog(LOG_TYPES.ORDER_CHANGE, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: COUPON_USE ๋กœ๊ทธ + * ๊ธฐ์กด sendLogCouponUse()์™€ ํ˜ธํ™˜ + */ +export const sendLogCouponUseNew = (params, callback) => + sendLog(LOG_TYPES.COUPON_USE, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: PAYMENT_ENTRY ๋กœ๊ทธ + * ๊ธฐ์กด sendLogPaymentEntry()์™€ ํ˜ธํ™˜ + */ +export const sendLogPaymentEntryNew = (params, callback) => + sendLog(LOG_TYPES.PAYMENT_ENTRY, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: PAYMENT_COMPLETE ๋กœ๊ทธ + * ๊ธฐ์กด sendLogPaymentComplete()์™€ ํ˜ธํ™˜ + */ +export const sendLogPaymentCompleteNew = (params, callback) => + sendLog(LOG_TYPES.PAYMENT_COMPLETE, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: FEATURED_BRANDS ๋กœ๊ทธ + * ๊ธฐ์กด sendLogFeaturedBrands()์™€ ํ˜ธํ™˜ + */ +export const sendLogFeaturedBrandsNew = (params, callback) => + sendLog(LOG_TYPES.FEATURED_BRANDS, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: MY_INFO_EDIT ๋กœ๊ทธ + * ๊ธฐ์กด sendLogMyInfoEdit()์™€ ํ˜ธํ™˜ + */ +export const sendLogMyInfoEditNew = (params, callback) => + sendLog(LOG_TYPES.MY_INFO_EDIT, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: CHECKOUT_BTN_CLICK ๋กœ๊ทธ + * ๊ธฐ์กด sendLogCheckOutBtnClick()์™€ ํ˜ธํ™˜ + */ +export const sendLogCheckOutBtnClickNew = (params, callback) => + sendLog(LOG_TYPES.CHECKOUT_BTN_CLICK, params, callback); + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: TOTAL_RECOMMEND ๋กœ๊ทธ + * ๊ธฐ์กด sendLogTotalRecommend()์™€ ํ˜ธํ™˜ + */ +export const sendLogTotalRecommendNew = (params, callback) => (dispatch, getState) => { + const onSuccess = callback; + const onFail = (error) => { + derror('[sendLogTotalRecommendNew] onFail', error); + }; + + // TotalLog๋Š” ํŠน๋ณ„ํžˆ postTotalLog์ฒ˜๋Ÿผ ์ฒ˜๋ฆฌ + TLogEvent( + dispatch, + getState, + 'post', + URLS.LOG_TOTAL_RECOMMEND, + {}, + params, + onSuccess, + onFail, + true // totalLogFlag = true + ); +}; + +/** + * ํŽธ์˜ ํ•จ์ˆ˜: DEEPLINK_FLAG ๋กœ๊ทธ + * ๊ธฐ์กด sendLogDeepLinkFlag()์™€ ํ˜ธํ™˜ + */ +export const sendLogDeepLinkFlagNew = (params, callback) => + sendLog(LOG_TYPES.DEEPLINK_FLAG, params, callback); + +/** + * ======================================== + * ๋‚ด๋ณด๋‚ด๊ธฐ ์ •๋ฆฌ + * ======================================== + * + * ์‚ฌ์šฉ ๋ฐฉ๋ฒ•: + * + * 1๏ธโƒฃ ํ†ตํ•ฉ ํ•จ์ˆ˜ ์ง์ ‘ ์‚ฌ์šฉ (๊ถŒ์žฅ): + * dispatch(sendLog('LIVE', { patncNm: '...', ... })) + * dispatch(sendLog('PRODUCT_DETAIL', { prdtId: '...', ... })) + * + * 2๏ธโƒฃ ํŽธ์˜ ํ•จ์ˆ˜ ์‚ฌ์šฉ (๊ธฐ์กด ์ฝ”๋“œ์™€ ์œ ์‚ฌ): + * dispatch(sendLogLiveNew({ patncNm: '...', ... })) + * dispatch(sendLogProductDetailNew({ prdtId: '...', ... })) + * + * 3๏ธโƒฃ ๋กœ๊ทธ ํƒ€์ž… ์ƒ์ˆ˜ ์‚ฌ์šฉ: + * import { LOG_TYPES } from '../config/logConfig' + * dispatch(sendLog(LOG_TYPES.LIVE, params)) + */ diff --git a/com.twin.app.shoptime/src/actions/playActions.js b/com.twin.app.shoptime/src/actions/playActions.js index 22449125..594bf9fc 100644 --- a/com.twin.app.shoptime/src/actions/playActions.js +++ b/com.twin.app.shoptime/src/actions/playActions.js @@ -100,31 +100,28 @@ export const startVideoPlayer = } const panels = getState().panels.panels; - const topPanel = panels[panels.length - 1]; + const existingPlayerPanel = panels.find((p) => p.name === panel_names.PLAYER_PANEL); let panelWorkingAction = pushPanel; - const panelName = panel_names.PLAYER_PANEL; - dlog( - '[startVideoPlayer] ๐Ÿ“Š Panel state - panelsCount:', - panels.length, - ', topPanelName:', - topPanel?.name - ); - - if (topPanel && topPanel.name === panelName) { - panelWorkingAction = updatePanel; - dlog('[startVideoPlayer] ๐Ÿ”„ UPDATING existing PLAYER_PANEL'); + // ๊ธฐ์กด PlayerPanel์ด ์–ด๋””๋“  ์žˆ์œผ๋ฉด ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”: ํƒ€์ด๋จธ ์ •๋ฆฌ ํ›„ pop โ†’ ์ƒˆ๋กœ push + if (existingPlayerPanel) { + dlog('[startVideoPlayer] ๐Ÿ”„ Resetting existing PLAYER_PANEL before start'); + clearAllVideoTimers(); + dispatch(popPanel(panel_names.PLAYER_PANEL)); } else { - dlog('[startVideoPlayer] โž• PUSHING new PLAYER_PANEL'); + dlog( + '[startVideoPlayer] ๐Ÿ“Š No existing PLAYER_PANEL - panelsCount:', + panels.length + ); } dispatch( - panelWorkingAction( - { - name: panelName, - panelInfo: { - modal, - modalContainerId, + panelWorkingAction( + { + name: panel_names.PLAYER_PANEL, + panelInfo: { + modal, + modalContainerId, modalClassName, videoId, // videoId ์ถ”๊ฐ€ํ•˜์—ฌ PlayerPanel์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ showUrl, // showUrl ์ถ”๊ฐ€ํ•˜์—ฌ PlayerPanel์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ @@ -212,11 +209,19 @@ export const startVideoPlayerNew = } const panels = getState().panels.panels; - const topPanel = panels[panels.length - 1]; + const existingPlayerPanel = panels.find((p) => p.name === panel_names.PLAYER_PANEL); let panelWorkingAction = pushPanel; + let shouldCheckDuplicate = true; - // const panelName = useNewPlayer ? panel_names.PLAYER_PANEL_NEW : panel_names.PLAYER_PANEL; - const panelName = panel_names.PLAYER_PANEL; + // ๊ธฐ์กด PlayerPanel์ด ์žˆ์œผ๋ฉด ์™„์ „ํžˆ ์ดˆ๊ธฐํ™”: ํƒ€์ด๋จธ ์ •๋ฆฌ ํ›„ pop โ†’ ์ƒˆ๋กœ push + if (existingPlayerPanel) { + dlog('[startVideoPlayerNew] *** ๐Ÿ”„ Resetting existing PLAYER_PANEL before start'); + clearAllVideoTimers(); + dispatch(popPanel(panel_names.PLAYER_PANEL)); + shouldCheckDuplicate = false; + } + + const topPanel = panels[panels.length - 1]; dlog( '[startVideoPlayerNew] *** ๐Ÿ“Š Panel state - panelsCount:', panels.length, @@ -224,42 +229,49 @@ export const startVideoPlayerNew = topPanel?.name ); - if (topPanel && topPanel.name === panelName) { + let currentPanelInfo = topPanel?.panelInfo || {}; + let currentPlayerState = currentPanelInfo.playerState || {}; + + if (!existingPlayerPanel && topPanel && topPanel.name === panel_names.PLAYER_PANEL) { panelWorkingAction = updatePanel; dlog('[startVideoPlayerNew] *** ๐Ÿ“‹ Current PLAYER_PANEL panelInfo:', topPanel.panelInfo); } // ์ค‘๋ณต ์‹คํ–‰ ๋ฐฉ์ง€: ๊ฐ™์€ ๋ฐฐ๋„ˆ + ๊ฐ™์€ modal ์ƒํƒœ/์ปจํ…Œ์ด๋„ˆ + ๊ฐ™์€ URL์ด๋ฉด skip - const currentPanelInfo = topPanel?.panelInfo || {}; - const currentPlayerState = currentPanelInfo.playerState || {}; - const isSameBanner = currentPlayerState.currentBannerId === bannerId; - const isSameModalType = currentPanelInfo.modal === modal; - const isSameContainer = currentPanelInfo.modalContainerId === modalContainerId; - const isSameShowUrl = currentPanelInfo.showUrl === showUrl; - const isSameVideoId = currentPanelInfo.videoId === videoId; + if (shouldCheckDuplicate) { + const isSameBanner = currentPlayerState.currentBannerId === bannerId; + const isSameModalType = currentPanelInfo.modal === modal; + const isSameContainer = currentPanelInfo.modalContainerId === modalContainerId; + const isSameShowUrl = currentPanelInfo.showUrl === showUrl; + const isSameVideoId = currentPanelInfo.videoId === videoId; - dlog( - '[startVideoPlayerNew] *** ๐Ÿ” Duplicate check - isSameBanner:', - isSameBanner, - ', isSameModalType:', - isSameModalType, - ', isSameContainer:', - isSameContainer, - ', isSameShowUrl:', - isSameShowUrl, - ', isSameVideoId:', - isSameVideoId - ); + dlog( + '[startVideoPlayerNew] *** ๐Ÿ” Duplicate check - isSameBanner:', + isSameBanner, + ', isSameModalType:', + isSameModalType, + ', isSameContainer:', + isSameContainer, + ', isSameShowUrl:', + isSameShowUrl, + ', isSameVideoId:', + isSameVideoId + ); - if (isSameBanner && isSameModalType && isSameContainer && isSameShowUrl && isSameVideoId) { - dlog('[startVideoPlayerNew] *** โญ๏ธ SKIPPED - ๋™์ผํ•œ ์š”์ฒญ', { - bannerId, - modal, - modalContainerId, - showUrl, - videoId, - }); - return; + if (isSameBanner && isSameModalType && isSameContainer && isSameShowUrl && isSameVideoId) { + dlog('[startVideoPlayerNew] *** โญ๏ธ SKIPPED - ๋™์ผํ•œ ์š”์ฒญ', { + bannerId, + modal, + modalContainerId, + showUrl, + videoId, + }); + return; + } + } else { + // pop์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•œ ๊ฒฝ์šฐ ์ค‘๋ณต ์ฒดํฌ ์Šคํ‚ต + currentPanelInfo = {}; + currentPlayerState = {}; } const newPlayerState = { @@ -271,7 +283,7 @@ export const startVideoPlayerNew = dispatch( panelWorkingAction( { - name: panelName, + name: panel_names.PLAYER_PANEL, panelInfo: { modal, modalContainerId, diff --git a/com.twin.app.shoptime/src/config/logConfig.js b/com.twin.app.shoptime/src/config/logConfig.js new file mode 100644 index 00000000..a341370d --- /dev/null +++ b/com.twin.app.shoptime/src/config/logConfig.js @@ -0,0 +1,516 @@ +/** + * ๋กœ๊ทธ ์„ค์ • ๋ฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ์ค‘์•™ํ™” + * + * ๊ธฐ์กด logActions.js์˜ ์ค‘๋ณต ๋กœ์ง์„ ํ†ตํ•ฉํ•˜์—ฌ ๊ด€๋ฆฌ + * sendLog() ํ†ตํ•ฉ ํ•จ์ˆ˜์—์„œ ์‚ฌ์šฉ + */ + +import { URLS } from '../api/apiConfig'; +import { LOG_TP_NO, LOG_MENU } from '../utils/Config'; + +/** + * ๋กœ๊ทธ ํƒ€์ž…๋ณ„ ์„ค์ • ์Šคํ‚ค๋งˆ + * + * ๊ฐ ๋กœ๊ทธ ํƒ€์ž…์— ํ•„์š”ํ•œ: + * - endpoint: API ์—”๋“œํฌ์ธํŠธ (URLS์˜ ํ‚ค) + * - logTpNo: ๋กœ๊ทธ ํƒ€์ž… ๋ฒˆํ˜ธ + * - requiredFields: ํ•„์ˆ˜ ํ•„๋“œ ๋ฐฐ์—ด + * - optionalFields: ์„ ํƒ ํ•„๋“œ ๋ฐฐ์—ด + * - preprocessor: ๋ฐ์ดํ„ฐ ์ „์ฒ˜๋ฆฌ ํ•จ์ˆ˜ (์˜ต์…˜) + */ +export const LOG_SCHEMA = { + // ======================== + // ์ŠคํŠธ๋ฆฌ๋ฐ (LIVE, VOD, CURATION) + // ======================== + LIVE: { + endpoint: 'LOG_LIVE', + logTpNo: LOG_TP_NO.LIVE.HOME, + requiredFields: ['patncNm', 'patnrId', 'showId', 'watchStrtDt'], + optionalFields: ['lgCatCd', 'lgCatNm', 'linkTpCd', 'vdoTpNm', 'watchEndDt'], + description: 'IG-LGSP-LOG-001 / Live ์‹œ์ฒญ ์ด๋ ฅ', + requiresTimeValidation: true, + autofillFields: { + watchEndDt: (params) => params.watchEndDt || null, // TLogEvent์—์„œ ์ฒ˜๋ฆฌ + }, + }, + + VOD: { + endpoint: 'LOG_VOD', + logTpNo: LOG_TP_NO.VOD.FULL_VOD, + requiredFields: ['watchStrtDt'], + optionalFields: ['showId', 'showNm', 'lgCatCd', 'lgCatNm', 'linkTpCd', 'vdoTpNm', 'watchEndDt'], + description: 'IG-LGSP-LOG-002 / VOD ์‹œ์ฒญ ์ด๋ ฅ', + requiresTimeValidation: true, + autofillFields: { + watchEndDt: (params) => params.watchEndDt || null, + }, + }, + + CURATION: { + endpoint: 'LOG_CURATION', + logTpNo: LOG_TP_NO.CURATION.HOT_PICKS, + requiredFields: [], + optionalFields: ['cnttTpNm', 'curationId', 'curationNm', 'expsOrd', 'lgCatCd', 'lgCatNm', 'linkTpCd', 'patncNm', 'patnrId', 'sortTpNm'], + description: 'IF-LGSP-LOG-003 / Curation View ์ด๋ ฅ', + requiresTimeValidation: false, + }, + + // ======================== + // ๋„ค๋น„๊ฒŒ์ด์…˜ + // ======================== + SECOND_LAYER: { + endpoint: 'LOG_SECOND_LAYER', + logTpNo: LOG_TP_NO.SECOND_LAYER, + requiredFields: [], + optionalFields: ['clientIP'], + description: 'IF-LGSP-LOG-004 / Entry ์ด๋ ฅ / ์„ธ์ปจ๋“œ ๋ ˆ์ด์–ด', + requiresTimeValidation: false, + }, + + GNB: { + endpoint: 'LOG_GNB', + logTpNo: LOG_TP_NO.GNB, + requiredFields: [], + optionalFields: ['menuMovSno', 'inDt', 'outDt'], + description: 'IF-LGSP-LOG-005 / GNB ๋ฉ”๋‰ด ํด๋ฆญ ์ด๋ ฅ', + requiresTimeValidation: false, + }, + + // ======================== + // ์ƒํ’ˆ ๋ฐ ์ƒ์„ธ ์ •๋ณด + // ======================== + PRODUCT_DETAIL: { + endpoint: 'LOG_PRODUCT', + logTpNo: LOG_TP_NO.PRODUCT.PRODUCT_DETAIL, + requiredFields: ['prdtId', 'patncNm', 'patnrId'], + optionalFields: ['prdtNm', 'befPrice', 'lastPrice', 'inDt', 'outDt', 'linkTpCd'], + description: 'IF-LGSP-LOG-006 / ์ƒํ’ˆ ์ƒ์„ธ ์ด๋ ฅ', + requiresTimeValidation: false, + }, + + DETAIL: { + endpoint: 'LOG_DETAIL', + logTpNo: LOG_TP_NO.DETAIL.THEME_DETAIL, + requiredFields: ['patncNm', 'patnrId'], + optionalFields: ['curationId', 'curationNm', 'inDt', 'outDt', 'linkTpCd'], + description: 'IF-LGSP-LOG-007 / Detail ์ƒ์„ธ ์ด๋ ฅ (Theme, Hotel)', + requiresTimeValidation: false, + }, + + SHOP_BY_MOBILE: { + endpoint: 'LOG_SHOP_BY_MOBILE', + logTpNo: LOG_TP_NO.SHOP_BY_MOBILE.SHOP_BY_MOBILE, + requiredFields: [], + optionalFields: ['shopByMobileFlag', 'mbphNoFlag', 'shopTpNm', 'trmsAgrFlag'], + description: 'IF-LGSP-LOG-008 / Shop by Mobile ์ด๋ ฅ', + requiresTimeValidation: false, + }, + + PARTNERS: { + endpoint: 'LOG_PARTNERS', + logTpNo: LOG_TP_NO.PARTNERS, + requiredFields: [], + optionalFields: ['patncNm', 'patnrId'], + description: 'IF-LGSP-LOG-009 / Partners ํด๋ฆญ ์ด๋ ฅ', + requiresTimeValidation: false, + }, + + THEME_PRODUCT: { + endpoint: 'LOG_THEME_PRODUCT', + logTpNo: LOG_TP_NO.THEME_PRODUCT, + requiredFields: [], + optionalFields: ['prdtId', 'prdtNm', 'curationId', 'curationNm', 'shelfId', 'shelfNm'], + description: 'IF-LGSP-LOG-020 / ํ…Œ๋งˆ ์ƒํ’ˆ ํด๋ฆญ ์ด๋ ฅ', + requiresTimeValidation: false, + }, + + // ======================== + // ๋งˆ์ดํŽ˜์ด์ง€ + // ======================== + MY_PAGE_ALERT_FLAG: { + endpoint: 'LOG_MY_PAGE_ALERT_FLAG', + logTpNo: LOG_TP_NO.MY_PAGE_ALERT_FLAG, + requiredFields: [], + optionalFields: ['alertFlag'], + description: 'IF-LGSP-LOG-010 / ์•Œ๋ฆผ On/Off ์„ค์ •', + requiresTimeValidation: false, + }, + + MY_PAGE_MY_DELETE: { + endpoint: 'LOG_MY_PAGE_MY_DELETE', + logTpNo: LOG_TP_NO.MY_PAGE_MY_DELETE, + requiredFields: [], + optionalFields: ['cnt'], + description: 'IF-LGSP-LOG-011 / My Page ์‚ญ์ œ ๋ฒ„ํŠผ ํด๋ฆญ', + requiresTimeValidation: false, + }, + + MY_PAGE_NOTICE: { + endpoint: 'LOG_MY_PAGE_NOTICE', + logTpNo: LOG_TP_NO.MY_PAGE_NOTICE, + requiredFields: [], + optionalFields: ['itemId', 'title'], + description: 'IF-LGSP-LOG-012 / My Page ๊ณต์ง€์‚ฌํ•ญ/FAQ ์กฐํšŒ', + requiresTimeValidation: false, + }, + + MY_INFO_EDIT: { + endpoint: 'LOG_MY_INFO_EDIT', + logTpNo: LOG_TP_NO.MY_INFO_EDIT, + requiredFields: [], + optionalFields: ['btnNm'], + description: 'IF-LGSP-LOG-111 / ์นด๋“œ/์ฃผ์†Œ ์ถ”๊ฐ€/์ˆ˜์ •', + requiresTimeValidation: false, + }, + + // ======================== + // ๊ฒ€์ƒ‰ + // ======================== + SEARCH: { + endpoint: 'LOG_SEARCH', + logTpNo: LOG_TP_NO.SEARCH, + requiredFields: [], + optionalFields: ['keyword', 'inputFlag', 'itemCnt', 'showCnt', 'themeCnt'], + description: 'IF-LGSP-LOG-013 / ๊ฒ€์ƒ‰ ์ด๋ ฅ', + requiresTimeValidation: false, + }, + + SEARCH_CLICK: { + endpoint: 'LOG_SEARCH_CLICK', + logTpNo: LOG_TP_NO.SEARCH_CLICK, + requiredFields: [], + optionalFields: ['keyword', 'patncNm', 'patnrId', 'prdtId', 'prdtNm', 'showId', 'showNm'], + description: 'IF-LGSP-LOG-014 / ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ํด๋ฆญ', + requiresTimeValidation: false, + }, + + // ======================== + // ์•Œ๋ฆผ/ํŒ์—… + // ======================== + UPCOMING_FLAG: { + endpoint: 'LOG_UPCOMING_FLAG', + logTpNo: LOG_TP_NO.UPCOMING_FLAG, + requiredFields: [], + optionalFields: ['items', 'alertFlag', 'patncNm', 'patnrId', 'showId'], + description: 'IF-LGSP-LOG-015 / ์˜ˆ์ • ๋ฐฉ์†ก ์•Œ๋ฆผ On/Off', + requiresTimeValidation: false, + }, + + ALARM_POP: { + endpoint: 'LOG_ALARM_POP', + logTpNo: LOG_TP_NO.ALARM_POP, + requiredFields: [], + optionalFields: ['alarmDt', 'alarmType', 'cnt', 'patncNm', 'patnrId', 'showId', 'showNm'], + description: 'IF-LGSP-LOG-017 / ์•Œ๋žŒ ํŒ์—… ํ‘œ์‹œ', + requiresTimeValidation: false, + }, + + ALARM_CLICK: { + endpoint: 'LOG_ALARM_CLICK', + logTpNo: LOG_TP_NO.ALARM_CLICK.BROADCAST, + requiredFields: [], + optionalFields: ['alarmDt', 'alarmType', 'clickFlag', 'cnt', 'keywordList'], + description: 'IF-LGSP-LOG-018 / ์•Œ๋žŒ ํŒ์—… ํด๋ฆญ', + requiresTimeValidation: false, + }, + + // ======================== + // TOP ์ฝ˜ํ…์ธ  + // ======================== + TOP_CONTENTS: { + endpoint: 'LOG_TOP_CONTENTS', + logTpNo: LOG_TP_NO.TOP_CONTENTS.VIEW, + requiredFields: [], + optionalFields: ['contId', 'contNm', 'banrNo', 'tmplCd', 'inDt', 'outDt'], + description: 'IF-LGSP-LOG-100 / TOP ์ฝ˜ํ…์ธ  ๋…ธ์ถœ', + requiresTimeValidation: false, + }, + + // ======================== + // ์•ฝ๊ด€ + // ======================== + TERMS: { + endpoint: 'LOG_TERMS', + logTpNo: LOG_TP_NO.TERMS.AGREE, + requiredFields: [], + optionalFields: [], + description: 'IF-LGSP-LOG-101 / ์•ฝ๊ด€ ๋™์˜/๊ฑฐ๋ถ€', + requiresTimeValidation: false, + }, + + // ======================== + // ๊ณ„์ • + // ======================== + LG_ACCOUNT_LOGIN: { + endpoint: 'LOG_ACCOUNT_LOGIN', + logTpNo: LOG_TP_NO.LG_ACCOUNT_LOGIN, + requiredFields: [], + optionalFields: ['lginTpNm', 'usrNo'], + description: 'IF-LGSP-LOG-102 / LG ๊ณ„์ • ๋กœ๊ทธ์ธ', + requiresTimeValidation: false, + }, + + // ======================== + // ์ฃผ๋ฌธ + // ======================== + ORDER_BTN_CLICK: { + endpoint: 'LOG_ORDER_BTN_CLICK', + logTpNo: LOG_TP_NO.ORDER_BTN_CLICK, + requiredFields: [], + optionalFields: ['btnNm'], + description: 'IF-LGSP-LOG-103 / ์ฃผ๋ฌธ ํ™”๋ฉด ๋ฒ„ํŠผ ํด๋ฆญ', + requiresTimeValidation: false, + }, + + ORDER_CHANGE: { + endpoint: 'LOG_ORDER_CHANGE', + logTpNo: LOG_TP_NO.ORDER_CHANGE, + requiredFields: [], + optionalFields: ['reqRsn', 'reqTpNm'], + description: 'IF-LGSP-LOG-104 / ์ฃผ๋ฌธ ์ทจ์†Œ/๋ฐ˜ํ’ˆ/๊ตํ™˜', + requiresTimeValidation: false, + }, + + COUPON_USE: { + endpoint: 'LOG_COUPON_USE', + logTpNo: LOG_TP_NO.COUPON_USE, + requiredFields: [], + optionalFields: ['cpnSno', 'cpnTtl', 'prodId', 'prodNm', 'patncNm', 'patnrId'], + description: 'IF-LGSP-LOG-105 / ์ฟ ํฐ ์‚ฌ์šฉ (ํ˜„์žฌ ๋น„ํ™œ์„ฑํ™”)', + requiresTimeValidation: false, + }, + + // ======================== + // ๊ฒฐ์ œ + // ======================== + PAYMENT_ENTRY: { + endpoint: 'LOG_PAYMENT_ENTRY', + logTpNo: LOG_TP_NO.PAYMENT_ENTRY, + requiredFields: [], + optionalFields: ['cartTpSno', 'cpnSno', 'dcAftrPrc', 'dcBefPrc', 'prodId', 'prodNm', 'qty'], + description: 'IF-LGSP-LOG-108 / ๊ฒฐ์ œ ํŽ˜์ด์ง€ ์ง„์ž…', + requiresTimeValidation: false, + }, + + PAYMENT_COMPLETE: { + endpoint: 'LOG_PAYMENT_COMPLETE', + logTpNo: LOG_TP_NO.PAYMENT_COMPLETE, + requiredFields: [], + optionalFields: ['cartTpSno', 'cpnSno', 'dcAftrPrc', 'dcBefPrc', 'prodId', 'prodNm', 'qty', 'usrNo'], + description: 'IF-LGSP-LOG-109 / ๊ฒฐ์ œ ์™„๋ฃŒ', + requiresTimeValidation: false, + }, + + // ======================== + // Featured Brands + // ======================== + FEATURED_BRANDS: { + endpoint: 'LOG_BRANDS', + logTpNo: LOG_TP_NO.BRANDS, + requiredFields: [], + optionalFields: ['patncNm', 'patnrId', 'catCd', 'catNm', 'crtrId', 'crtrNm', 'srsId', 'srsNm'], + description: 'IF-LGSP-LOG-110 / Featured Brands ์กฐํšŒ', + requiresTimeValidation: false, + }, + + // ======================== + // Checkout + // ======================== + CHECKOUT_BTN_CLICK: { + endpoint: 'LOG_CHECKOUT_BTN_CLICK', + logTpNo: LOG_TP_NO.CHECKOUT_BTN_CLICK, + requiredFields: [], + optionalFields: ['btnNm'], + description: 'IF-LGSP-LOG-112 / Checkout ํ™”๋ฉด ๋ฒ„ํŠผ ํด๋ฆญ', + requiresTimeValidation: false, + }, + + // ======================== + // DeepLink + // ======================== + DEEPLINK_FLAG: { + endpoint: 'LOG_DEEPLINK', + logTpNo: null, // DeepLink๋Š” ๋ณ„๋„ ์ฒ˜๋ฆฌ + requiredFields: [], + optionalFields: ['deeplinkId', 'flag'], + description: 'DeepLink ์ˆ˜์‹  ๋ชจ๋‹ˆํ„ฐ๋ง', + requiresTimeValidation: false, + }, + + // ======================== + // Total Recommend (ํ†ตํ•ฉ ์ถ”์ฒœ) + // ======================== + TOTAL_RECOMMEND: { + endpoint: 'LOG_TOTAL_RECOMMEND', + logTpNo: null, // TotalLog ํŠน๋ณ„ ์ฒ˜๋ฆฌ + requiredFields: [], + optionalFields: [], + description: 'IF-LGSP-LOG-200 / ํ†ตํ•ฉ ์ถ”์ฒœ ๋กœ๊ทธ', + requiresTimeValidation: false, + isTotalLog: true, // TLogEvent์—์„œ totalLogFlag=true๋กœ ์ฒ˜๋ฆฌ + }, +}; + +/** + * ๋กœ๊ทธ ํƒ€์ž… ์ƒ์ˆ˜ (ํƒ€์ž… ์•ˆ์ „์„ฑ ๊ฐ•ํ™”์šฉ) + */ +export const LOG_TYPES = { + // ์ŠคํŠธ๋ฆฌ๋ฐ + LIVE: 'LIVE', + VOD: 'VOD', + CURATION: 'CURATION', + + // ๋„ค๋น„๊ฒŒ์ด์…˜ + SECOND_LAYER: 'SECOND_LAYER', + GNB: 'GNB', + + // ์ƒํ’ˆ + PRODUCT_DETAIL: 'PRODUCT_DETAIL', + DETAIL: 'DETAIL', + SHOP_BY_MOBILE: 'SHOP_BY_MOBILE', + PARTNERS: 'PARTNERS', + THEME_PRODUCT: 'THEME_PRODUCT', + + // ๋งˆ์ดํŽ˜์ด์ง€ + MY_PAGE_ALERT_FLAG: 'MY_PAGE_ALERT_FLAG', + MY_PAGE_MY_DELETE: 'MY_PAGE_MY_DELETE', + MY_PAGE_NOTICE: 'MY_PAGE_NOTICE', + MY_INFO_EDIT: 'MY_INFO_EDIT', + + // ๊ฒ€์ƒ‰ + SEARCH: 'SEARCH', + SEARCH_CLICK: 'SEARCH_CLICK', + + // ์•Œ๋ฆผ + UPCOMING_FLAG: 'UPCOMING_FLAG', + ALARM_POP: 'ALARM_POP', + ALARM_CLICK: 'ALARM_CLICK', + + // TOP ์ฝ˜ํ…์ธ  + TOP_CONTENTS: 'TOP_CONTENTS', + + // ์•ฝ๊ด€ + TERMS: 'TERMS', + + // ๊ณ„์ • + LG_ACCOUNT_LOGIN: 'LG_ACCOUNT_LOGIN', + + // ์ฃผ๋ฌธ + ORDER_BTN_CLICK: 'ORDER_BTN_CLICK', + ORDER_CHANGE: 'ORDER_CHANGE', + COUPON_USE: 'COUPON_USE', + + // ๊ฒฐ์ œ + PAYMENT_ENTRY: 'PAYMENT_ENTRY', + PAYMENT_COMPLETE: 'PAYMENT_COMPLETE', + + // Featured Brands + FEATURED_BRANDS: 'FEATURED_BRANDS', + + // Checkout + CHECKOUT_BTN_CLICK: 'CHECKOUT_BTN_CLICK', + + // ํŠน์ˆ˜ + DEEPLINK_FLAG: 'DEEPLINK_FLAG', + TOTAL_RECOMMEND: 'TOTAL_RECOMMEND', +}; + +/** + * ํŠน์ˆ˜ ์ „์ฒ˜๋ฆฌ ํ•จ์ˆ˜๋“ค + * ํŠน์ • ๋กœ๊ทธ ํƒ€์ž…์—๋งŒ ์ ์šฉ๋˜๋Š” ์ปค์Šคํ…€ ๋กœ์ง + */ +export const LOG_PREPROCESSORS = { + LIVE: (params, getState) => { + // watchStrtDt ๊ฒ€์ฆ, watchEndDt ์ž๋™ ์„ค์ • ๋“ฑ + return params; + }, + + VOD: (params, getState) => { + // VOD ํŠน์ˆ˜ ์ฒ˜๋ฆฌ + return params; + }, + + PRODUCT_DETAIL: (params, getState) => { + // ์ƒํ’ˆ ์ƒ์„ธ์˜ ํŠน์ˆ˜ ์ฒ˜๋ฆฌ + // logTpNo์— ๋”ฐ๋ผ entryMenu ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Œ + return params; + }, + + DETAIL: (params, getState) => { + // Detail ํŠน์ˆ˜ ์ฒ˜๋ฆฌ + return params; + }, +}; + +/** + * ๋กœ๊ทธ ํƒ€์ž… ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… + * @returns {boolean} + */ +export const isValidLogType = (logType) => { + return LOG_SCHEMA.hasOwnProperty(logType); +}; + +/** + * ํ•„์ˆ˜ ํ•„๋“œ ๊ฒ€์ฆ + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… + * @param {object} params - ํŒŒ๋ผ๋ฏธํ„ฐ + * @returns {array} ๋ˆ„๋ฝ๋œ ํ•„๋“œ ๋ฐฐ์—ด + */ +export const getMissingFields = (logType, params) => { + const schema = LOG_SCHEMA[logType]; + if (!schema) return []; + + return schema.requiredFields.filter(field => !params[field]); +}; + +/** + * ๋กœ๊ทธ ํƒ€์ž…์˜ ์—”๋“œํฌ์ธํŠธ ์กฐํšŒ + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… + * @returns {string} ์—”๋“œํฌ์ธํŠธ URL + */ +export const getLogEndpoint = (logType) => { + const schema = LOG_SCHEMA[logType]; + if (!schema) return null; + return URLS[schema.endpoint] || null; +}; + +/** + * ๋กœ๊ทธ ํƒ€์ž…์˜ logTpNo ์กฐํšŒ + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… + * @returns {string|number} logTpNo + */ +export const getLogTpNo = (logType) => { + const schema = LOG_SCHEMA[logType]; + if (!schema) return null; + return schema.logTpNo; +}; + +/** + * ๋กœ๊ทธ ์Šคํ‚ค๋งˆ ์กฐํšŒ + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… + * @returns {object} ๋กœ๊ทธ ์Šคํ‚ค๋งˆ + */ +export const getLogSchema = (logType) => { + return LOG_SCHEMA[logType] || null; +}; + +/** + * ๋กœ๊ทธ ํƒ€์ž…์ด ์‹œ๊ฐ„ ๊ฒ€์ฆ์ด ํ•„์š”ํ•œ์ง€ ํ™•์ธ + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… + * @returns {boolean} + */ +export const requiresTimeValidation = (logType) => { + const schema = LOG_SCHEMA[logType]; + return schema?.requiresTimeValidation || false; +}; + +/** + * ๋กœ๊ทธ ํƒ€์ž…์ด TotalLog์ธ์ง€ ํ™•์ธ + * @param {string} logType - ๋กœ๊ทธ ํƒ€์ž… + * @returns {boolean} + */ +export const isTotalLog = (logType) => { + const schema = LOG_SCHEMA[logType]; + return schema?.isTotalLog || false; +};