🕐 커밋 시간: 2025. 11. 22. 21:21:31 📊 변경 통계: • 총 파일: 10개 • 추가: +76줄 • 삭제: -19줄 📁 추가된 파일: + com.twin.app.shoptime/HOTEL_UI_HANDLING_REPORT.md + com.twin.app.shoptime/HOTEL_UI_VISUAL_GUIDE.md + com.twin.app.shoptime/THEME_PRODUCT_UI_ANALYSIS.md + com.twin.app.shoptime/THEME_PRODUCT_VISUAL_GUIDE.md + com.twin.app.shoptime/THEME_VS_HOTEL_COMPARISON.md + com.twin.app.shoptime/docs/todo/251122-detailpanel-diff.md 📝 수정된 파일: ~ com.twin.app.shoptime/src/api/TAxios.js ~ com.twin.app.shoptime/src/views/DetailPanel/DetailPanel.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx ~ com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.module.less 🔧 함수 변경 내용: 📄 com.twin.app.shoptime/src/views/DetailPanel/ProductAllSection/ProductAllSection.jsx (javascript): ✅ Added: extractProductMeta() 🔄 Modified: SpotlightContainerDecorator() ❌ Deleted: SpotlightContainerDecorator() 📄 com.twin.app.shoptime/HOTEL_UI_HANDLING_REPORT.md (md파일): ✅ Added: useEffect(), dispatch(), ThemeProduct(), setLabel(), Map(), forEach(), filter(), findIndex(), setAmenitiesInfos(), setSelectedIndex() 📄 com.twin.app.shoptime/HOTEL_UI_VISUAL_GUIDE.md (md파일): ✅ Added: DetailPanel(), getThemeCurationDetailInfo(), getThemeHotelDetailInfo(), getMainCategoryDetail(), THeader(), TBody(), ThemeProduct(), Container(), rating(), TButton(), YouMayLike(), MobileSendPopUp(), selectedIndex(), setSelectedIndex(), ellipsis(), amenitiesBox(), c70850(), handleSMSClick(), dispatch(), clearThemeDetail() 📄 com.twin.app.shoptime/THEME_PRODUCT_UI_ANALYSIS.md (md파일): ✅ Added: getThemeCurationDetailInfo(), productData(), Container(), ShowSingleOption(), ShowUnableOption(), useState(), useEffect(), setSelectedImage(), useMemo(), dispatch(), startVideoPlayer(), productDescription(), setTimeout(), useCallback(), isProductSoldOut(), ProductOption(), setSelectedIndex() 📄 com.twin.app.shoptime/THEME_PRODUCT_VISUAL_GUIDE.md (md파일): ✅ Added: ShowProduct(), Container(), optionContainer(), ShowSingleOption(), ProductOption(), ShowUnableOption(), UnableOption(), selectedIndex(), setSelectedIndex(), descriptionClick(), setTabLabel(), setDescription(), dispatch(), handleIndicatorOptions(), handleSMSClick(), handleMobileSendPopupOpen(), setImageSelectedIndex(), StarRating(), ProductTag(), SingleOption() 📄 com.twin.app.shoptime/THEME_VS_HOTEL_COMPARISON.md (md파일): ✅ Added: Product(), UnableOption(), StarRating(), dispatch(), getThemeCurationDetailInfo(), getThemeHotelDetailInfo() 🔧 주요 변경 내용: • API 서비스 레이어 개선 • 개발 문서 및 가이드 개선
12 KiB
12 KiB
DetailPanel - Theme Product vs Hotel Product 비교 분석
📊 전체 개요
| 구분 | Theme Product (쇼/공연) | Hotel Product (숙박) |
|---|---|---|
| 데이터 소스 | themeCurationDetailInfoData[] |
themeCurationHotelDetailData[] |
| 메인 컴포넌트 | ShowProduct | HotelProduct |
| 패널 타입 | panelInfo.type === "theme" |
panelInfo.type === "hotel" |
| 서버 API | getThemeCurationDetailInfo | getThemeHotelDetailInfo |
| Redux 액션 | GET_THEME_CURATION_DETAIL_INFO | GET_THEME_HOTEL_DETAIL_INFO |
🎯 핵심 차이점
1. 데이터 구조
Theme Product
themeCurationDetailInfoData: [
{
prdtId, prdtNm, // 상품 ID, 이름
imgUrls600: [], // 이미지 배열
priceInfo: "299|199|Y|...", // 가격 정보 (파이프 구분)
prdtMediaUrl, // 비디오 URL
showId, showNm, // 쇼 정보
catCd, catNm, // 카테고리
soldoutFlag, // 매진 여부
pmtSuptYn, // 결제 지원
revwGrd, // 평점
}
]
Hotel Product
themeCurationHotelDetailData: [
{
hotelId, hotelNm, // 호텔 ID, 이름
hotelImgUrl, // 호텔 썸네일
imgUrls600: [], // 이미지 배열
qrcodeUrl, // QR 코드
hotelDetailInfo: {
price, // 가격 (단순 숫자)
currencySign, // 통화 기호
revwGrd, // 평점
hotelType, // 호텔 타입
hotelAddr, // 주소
nights, adultsCount, // 숙박일, 성인 수
roomType, // 방 타입
amenities: [], // 편의시설 ID 배열
imgUrls: [] // 이미지
}
}
]
hotelData: {
hotelInfo: { curationId, curationNm, ... },
amenities: [
{ amntId, lgAmntNm, lgAmntImgUrl },
// ...
]
}
2. UI 구조
Theme Product
ShowProduct
├─ ThemeIndicator
│ ├─ [메인 이미지] 또는 [비디오]
│ └─ [썸네일 스크롤]
├─ IndicatorOptions
│ ├─ [설명] [교환정책] [SMS]
│ └─ QR 코드
└─ ShowSingleOption / ShowUnableOption
└─ 구매 옵션 또는 구매불가 메시지
Hotel Product
HotelProduct
├─ ThemeIndicator
│ ├─ [메인 이미지]
│ └─ [썸네일 스크롤]
├─ IndicatorOptions
│ └─ [주소 정보]
└─ optionContainer
├─ [로고 + 별점]
├─ [호텔명 + 타입]
├─ [편의시설 그리드] (최대 10개)
├─ [예약정보 + 가격 + QR]
└─ [SEE MORE] 버튼
3. 가격 처리
Theme Product
// priceInfo = "299|199|Y|discount10|10%"
const befPrice = priceInfo.split("|")[0]; // 원가
const lastPrice = priceInfo.split("|")[1]; // 할인가
const rewdAplyFlag = priceInfo.split("|")[2]; // 보상 적용
const discountRate = priceInfo.split("|")[4]; // 할인율
// 로그 전송
params.befPrice = befPrice;
params.lastPrice = lastPrice;
Hotel Product
// price = "299.99" (단순 숫자)
// currencySign = "$"
// UI 표시
<div>
{hotelInfos[selectedIndex]?.hotelDetailInfo.currencySign}
{hotelInfos[selectedIndex]?.hotelDetailInfo.price}
</div>
// 로그 전송
params.price = selectedHotelInfo.hotelInfo?.hotelDetailInfo?.price;
4. 특수 기능
Theme Product
✅ 비디오 자동 재생
prdtMediaUrl존재 시 첫 이미지 위치에 비디오launchedFromPlayer = false일 때만 자동 재생- 비디오 자막 지원 (
prdtMediaSubtitlUrl)
✅ 상세 정보 팝업
- [DESCRIPTION] 버튼: 상품 설명
- [RETURNS & EXCHANGES] 버튼: 반품/교환 정책
- HTML 마크업 지원 (
dangerouslySetInnerHTML)
✅ 결제 옵션 선택
- 옵션 선택 (이용일자, 좌석 등)
- 수량 선택
- 옵션별 가격 계산
❌ 편의시설 표시 없음
Hotel Product
✅ 편의시설 그리드 표시
- 최대 10개 편의시설 아이콘 + 텍스트
- 같은 카테고리 중복 제거
- 편의시설별 이미지 및 설명
✅ 예약 정보 표시
- 숙박 기간 (nights)
- 성인 수 (adultsCount)
- 방 타입 (roomType)
- 자동 포맷팅 (예: "2 Nights 2 Adults")
✅ 별점 등급 시스템
- 평점 수치 → 문자 등급 변환
- Fair (≤2.4) / Good (2.5
3.4) / Very Good (3.54.4) / Excellent (≥4.5)
❌ 비디오 지원 없음 ❌ 옵션 선택 없음 (숙박 정보는 미리 정해짐)
5. 결제 여부 판단
공통점
// 결제 OS 버전 체크
webOSVersion >= "6.0" // TRUE일 때만 결제 UI 표시
Theme Product
const isBillingProductVisible = (
productInfo[selectedIndex]?.pmtSuptYn === "Y" &&
webOSVersion >= "6.0"
);
// YES → ShowSingleOption (구매 옵션)
// NO → ShowUnableOption (구매불가)
Hotel Product
// 호텔은 pmtSuptYn 체크 없음 (모두 구매불가)
// SMS "SEE MORE" 버튼만 제공
// 모든 호텔 상품이 UnableOption 구조
6. SMS 타입 코드
Theme Product
smsTpCd = "APP00204" // 테마/쇼 상품
Hotel Product
smsTpCd = "APP00205" // 호텔 상품
7. 로그 필드
공통 필드
{
curationId, curationNm, // 테마/큐레이션
patnrId, patncNm, // 파트너
prdtId, prdtNm, // 상품 ID, 이름
revwGrd, // 평점
expsOrd, // 상품 순번
}
Theme Product 추가 필드
{
showId, showNm, // 쇼 정보
catCd, catNm, // 카테고리
befPrice, lastPrice, // 원가, 할인가
rewdAplyFlag, // 보상 적용
tsvFlag, // 오늘의 특가
shopTpNm: "product", // 상품 타입
}
Hotel Product 추가 필드
{
hotelId, // 호텔 ID (prdtId 대체)
price, // 가격 (단순)
shopTpNm: "hotel", // 상품 타입
}
8. 선택 인덱스 처리
Theme Product
// URL 파라미터로 특정 상품 지정
if (panelInfo?.themePrdtId) {
for (let i = 0; i < themeProductInfos.length; i++) {
if (themeProductInfos[i].prdtId === panelInfo?.themePrdtId) {
setSelectedIndex(i); // ← 해당 상품으로 이동
}
}
}
Hotel Product
// URL 파라미터로 특정 호텔 지정
if (panelInfo?.themeHotelId) {
for (let i = 0; i < hotelInfos.length; i++) {
if (hotelInfos[i].hotelId === panelInfo?.themeHotelId) {
setSelectedIndex(i); // ← 해당 호텔로 이동
}
}
}
📐 레이아웃 비교
Theme Product
| 요소 | 크기 |
|---|---|
| ThemeIndicator | 834×930px |
| 메인 이미지 | 600×600px |
| 썸네일 | 150×150px (반복) |
| QR 코드 | 160×160px |
Hotel Product
| 요소 | 크기 |
|---|---|
| ThemeIndicator | 774×930px |
| 메인 이미지 | 600×600px |
| optionContainer | 1026×990px |
| 편의시설 박스 | 138×138px (반복, 최대 10개) |
| QR 코드 | 160×160px |
🔄 컴포넌트 재사용
공유 컴포넌트
ThemeIndicator
├─ Theme Product에서 사용
└─ Hotel Product에서도 사용
(비디오 재생은 X)
IndicatorOptions
├─ Theme Product에서 사용
└─ Hotel Product에서도 사용
(주소 정보만 표시)
StarRating
├─ Theme Product에서 사용
└─ Hotel Product에서도 사용
전용 컴포넌트
Theme Product:
├─ ShowProduct.jsx
├─ ShowSingleOption.jsx
├─ ShowUnableOption.jsx
└─ SingleOption / UnableOption (기존)
Hotel Product:
├─ HotelProduct.jsx
└─ StarRating (호텔 등급용)
📊 정리 테이블
| 기능 | Theme | Hotel |
|---|---|---|
| 비디오 지원 | ✅ | ❌ |
| 자동 재생 | ✅ | ❌ |
| 편의시설 표시 | ❌ | ✅ (최대 10개) |
| 예약 정보 | ❌ | ✅ (숙박일, 성인 수) |
| 등급 변환 | ❌ | ✅ (Fair/Good/Very Good/Excellent) |
| 옵션 선택 | ✅ | ❌ |
| 수량 선택 | ✅ | ❌ |
| 결제 가능 | 조건부 (pmtSuptYn) | ❌ (항상 불가) |
| 상세 팝업 | ✅ (설명/교환) | ❌ |
| 가격 형식 | "299|199|Y|..." |
"299.99" |
| 통화 기호 | 미사용 | ✅ ($, €, ¥ 등) |
| SMS 타입 | APP00204 | APP00205 |
| QR 코드 | ✅ | ✅ |
| 로그 추적 | 상세 (가격, 할인율) | 기본 (가격만) |
🎯 사용 시나리오
Theme Product 사용 예
사용자가 "오페라 공연" 클릭
↓
DetailPanel 로드 (type: "theme")
↓
ShowProduct 렌더링
↓
공연 비디오 자동 재생
↓
사용자가 설명 버튼 클릭
↓
상품 설명 팝업 표시
↓
사용자가 SMS 버튼 클릭
↓
친구에게 공연 정보 전송
Hotel Product 사용 예
사용자가 "두바이 호텔" 클릭
↓
DetailPanel 로드 (type: "hotel")
↓
HotelProduct 렌더링
↓
호텔 사진, 편의시설, 가격 표시
↓
사용자가 화살표로 다른 호텔 선택
↓
별점/편의시설/가격 업데이트
↓
사용자가 SEE MORE 버튼 클릭
↓
호텔 상세 정보 SMS 전송
💡 개발 가이드
새로운 상품 타입 추가 시
-
DetailPanel에 분기 추가
if (panelInfo?.type === "newtype") { dispatch(getNewTypeDetailInfo(...)); } -
Redux Action/Reducer 생성
GET_NEWTYPE_DETAIL_INFO action 추가 state.home.newTypeData 상태 추가 -
메인 컴포넌트 생성 (ShowProduct/HotelProduct 참고)
- ThemeIndicator 재사용 (또는 커스터마이징)
- IndicatorOptions 재사용 (또는 커스터마이징)
- 세부 UI 컴포넌트 작성
-
ThemeProduct에 라우팅 추가
{themeType === "newtype" && ( <NewTypeProduct {...props} /> )}
🔗 관련 파일 구조
src/views/DetailPanel/
├─ DetailPanel.jsx / DetailPanel.backup.jsx
├─ ThemeProduct/
│ ├─ ThemeProduct.jsx (라우팅)
│ ├─ ShowProduct.jsx (테마 상품)
│ ├─ HotelProduct.jsx (호텔 상품)
│ ├─ ShowOptions/
│ │ ├─ ShowSingleOption.jsx
│ │ └─ ShowUnableOption.jsx
│ └─ *.module.less
├─ components/
│ ├─ ProductOption.jsx (래퍼)
│ ├─ indicator/
│ │ ├─ ThemeIndicator.jsx (공유)
│ │ ├─ IndicatorOptions.jsx (공유)
│ │ └─ *.module.less
│ ├─ StarRating.jsx (공유)
│ └─ ...
├─ SingleProduct/
│ └─ SingleOption.jsx (테마용)
├─ UnableProduct/
│ └─ UnableOption.jsx (테마용)
└─ ...
src/actions/
├─ homeActions.js
│ ├─ getThemeCurationDetailInfo()
│ └─ getThemeHotelDetailInfo()
└─ ...
src/reducers/
└─ homeReducer.js
├─ GET_THEME_CURATION_DETAIL_INFO case
└─ GET_THEME_HOTEL_DETAIL_INFO case
✅ 최종 체크리스트
Theme Product
- ✅ 비디오 자동 재생 (조건부)
- ✅ 상세 정보 팝업 (설명/교환)
- ✅ 옵션 선택 (날짜/좌석)
- ✅ 수량 선택
- ✅ 가격 할인율 표시
- ✅ 결제 가능 여부 판단
- ✅ SMS 공유 (모든 상품)
- ✅ 상세 로깅
Hotel Product
- ✅ 호텔 이미지 갤러리
- ✅ 편의시설 그리드 (최대 10개)
- ✅ 별점 등급 변환
- ✅ 예약 정보 표시 (숙박일, 성인 수)
- ✅ 가격 + 통화 기호
- ✅ 주소 정보
- ✅ QR 코드
- ✅ SMS 공유 (SEE MORE)
- ✅ 로깅
이 문서를 통해 Theme Product와 Hotel Product의 차이점을 명확히 이해할 수 있으며, 향후 유사한 상품 타입 추가 시 참고할 수 있습니다.