🕐 커밋 시간: 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 서비스 레이어 개선 • 개발 문서 및 가이드 개선
499 lines
12 KiB
Markdown
499 lines
12 KiB
Markdown
# 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
|
||
```javascript
|
||
themeCurationDetailInfoData: [
|
||
{
|
||
prdtId, prdtNm, // 상품 ID, 이름
|
||
imgUrls600: [], // 이미지 배열
|
||
priceInfo: "299|199|Y|...", // 가격 정보 (파이프 구분)
|
||
prdtMediaUrl, // 비디오 URL
|
||
showId, showNm, // 쇼 정보
|
||
catCd, catNm, // 카테고리
|
||
soldoutFlag, // 매진 여부
|
||
pmtSuptYn, // 결제 지원
|
||
revwGrd, // 평점
|
||
}
|
||
]
|
||
```
|
||
|
||
#### Hotel Product
|
||
```javascript
|
||
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
|
||
```javascript
|
||
// 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
|
||
```javascript
|
||
// 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.5~4.4) / Excellent (≥4.5)
|
||
|
||
❌ **비디오 지원 없음**
|
||
❌ **옵션 선택 없음** (숙박 정보는 미리 정해짐)
|
||
|
||
---
|
||
|
||
### 5. 결제 여부 판단
|
||
|
||
#### 공통점
|
||
```javascript
|
||
// 결제 OS 버전 체크
|
||
webOSVersion >= "6.0" // TRUE일 때만 결제 UI 표시
|
||
```
|
||
|
||
#### Theme Product
|
||
```javascript
|
||
const isBillingProductVisible = (
|
||
productInfo[selectedIndex]?.pmtSuptYn === "Y" &&
|
||
webOSVersion >= "6.0"
|
||
);
|
||
|
||
// YES → ShowSingleOption (구매 옵션)
|
||
// NO → ShowUnableOption (구매불가)
|
||
```
|
||
|
||
#### Hotel Product
|
||
```javascript
|
||
// 호텔은 pmtSuptYn 체크 없음 (모두 구매불가)
|
||
// SMS "SEE MORE" 버튼만 제공
|
||
|
||
// 모든 호텔 상품이 UnableOption 구조
|
||
```
|
||
|
||
---
|
||
|
||
### 6. SMS 타입 코드
|
||
|
||
#### Theme Product
|
||
```javascript
|
||
smsTpCd = "APP00204" // 테마/쇼 상품
|
||
```
|
||
|
||
#### Hotel Product
|
||
```javascript
|
||
smsTpCd = "APP00205" // 호텔 상품
|
||
```
|
||
|
||
---
|
||
|
||
### 7. 로그 필드
|
||
|
||
#### 공통 필드
|
||
```javascript
|
||
{
|
||
curationId, curationNm, // 테마/큐레이션
|
||
patnrId, patncNm, // 파트너
|
||
prdtId, prdtNm, // 상품 ID, 이름
|
||
revwGrd, // 평점
|
||
expsOrd, // 상품 순번
|
||
}
|
||
```
|
||
|
||
#### Theme Product 추가 필드
|
||
```javascript
|
||
{
|
||
showId, showNm, // 쇼 정보
|
||
catCd, catNm, // 카테고리
|
||
befPrice, lastPrice, // 원가, 할인가
|
||
rewdAplyFlag, // 보상 적용
|
||
tsvFlag, // 오늘의 특가
|
||
shopTpNm: "product", // 상품 타입
|
||
}
|
||
```
|
||
|
||
#### Hotel Product 추가 필드
|
||
```javascript
|
||
{
|
||
hotelId, // 호텔 ID (prdtId 대체)
|
||
price, // 가격 (단순)
|
||
shopTpNm: "hotel", // 상품 타입
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 8. 선택 인덱스 처리
|
||
|
||
#### Theme Product
|
||
```javascript
|
||
// URL 파라미터로 특정 상품 지정
|
||
if (panelInfo?.themePrdtId) {
|
||
for (let i = 0; i < themeProductInfos.length; i++) {
|
||
if (themeProductInfos[i].prdtId === panelInfo?.themePrdtId) {
|
||
setSelectedIndex(i); // ← 해당 상품으로 이동
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Hotel Product
|
||
```javascript
|
||
// 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 전송
|
||
```
|
||
|
||
---
|
||
|
||
## 💡 개발 가이드
|
||
|
||
### 새로운 상품 타입 추가 시
|
||
1. **DetailPanel에 분기 추가**
|
||
```javascript
|
||
if (panelInfo?.type === "newtype") {
|
||
dispatch(getNewTypeDetailInfo(...));
|
||
}
|
||
```
|
||
|
||
2. **Redux Action/Reducer 생성**
|
||
```javascript
|
||
GET_NEWTYPE_DETAIL_INFO action 추가
|
||
state.home.newTypeData 상태 추가
|
||
```
|
||
|
||
3. **메인 컴포넌트 생성** (ShowProduct/HotelProduct 참고)
|
||
- ThemeIndicator 재사용 (또는 커스터마이징)
|
||
- IndicatorOptions 재사용 (또는 커스터마이징)
|
||
- 세부 UI 컴포넌트 작성
|
||
|
||
4. **ThemeProduct에 라우팅 추가**
|
||
```javascript
|
||
{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의 차이점을 명확히 이해할 수 있으며, 향후 유사한 상품 타입 추가 시 참고할 수 있습니다.
|