+ {(() => {
+ // STOMP 실시간 데이터는 productCode가 "UNKNOWN"이므로 수량 필드로 단품 여부 유추
+ const isMatchingSingle =
+ order.productCode === "MATCHING_TICKET_1" ||
+ (order.productCode === "UNKNOWN" &&
+ order.matchingTicketQty > 0 &&
+ order.optionTicketQty === 0);
+
+ const isOptionSingle =
+ order.productCode === "OPTION_TICKET_1" ||
+ (order.productCode === "UNKNOWN" &&
+ order.optionTicketQty > 0 &&
+ order.matchingTicketQty === 0);
+
+ if (isMatchingSingle || isOptionSingle) {
+ return (
+
+ );
+ }
+ return null;
+ })()}
diff --git a/app/profile-image/_components/ProfileImageSelection.tsx b/app/profile-image/_components/ProfileImageSelection.tsx
index 790b693..6d5fdf6 100644
--- a/app/profile-image/_components/ProfileImageSelection.tsx
+++ b/app/profile-image/_components/ProfileImageSelection.tsx
@@ -81,17 +81,24 @@ const ProfileImageSelection = ({
if (targetFile.type.startsWith("image/")) {
onFileChange?.(targetFile);
- const reader = new FileReader();
- reader.onloadend = () => {
- onCustomImageChange(reader.result as string);
- };
- reader.readAsDataURL(targetFile);
+
+ // 기존 브라우저 메모리에 캐싱된 Blob URL이 있다면 해제 (메모리 누수 방지)
+ if (customImage && customImage.startsWith("blob:")) {
+ URL.revokeObjectURL(customImage);
+ }
+
+ // FileReader 대신 createObjectURL을 사용하여 빠르고 가볍게 미리보기 생성
+ const objectUrl = URL.createObjectURL(targetFile);
+ onCustomImageChange(objectUrl);
}
} catch (error) {
console.error("Image processing failed:", error);
alert(
"이미지 처리 중 오류가 발생했습니다. 다른 이미지 형식을 사용해 주세요.",
);
+ } finally {
+ // 동일한 파일을 다시 선택해도 onChange가 트리거되도록 값 초기화
+ e.target.value = "";
}
};
diff --git a/app/roulette/_components/Roulette.tsx b/app/roulette/_components/Roulette.tsx
index 9fb4c75..c6b68f5 100644
--- a/app/roulette/_components/Roulette.tsx
+++ b/app/roulette/_components/Roulette.tsx
@@ -41,9 +41,9 @@ const ROULETTE_CONFIG: Record<
imageSrc: "/roulette/special_roulette.png",
items: [
{ id: 1, label: "옵션권 2장" },
- { id: 2, label: "옵션권 5장" },
- { id: 3, label: "뽑기권 1장" },
- { id: 4, label: "풀세트" },
+ { id: 2, label: "뽑기권 1장" },
+ { id: 3, label: "풀세트" },
+ { id: 4, label: "옵션권 5장" },
{ id: 5, label: "뽑기권 5장" },
{ id: 6, label: "뽑기권 10장" },
{ id: 7, label: "1만원권 상품권" },
diff --git a/app/roulette/_components/RouletteResultModal.tsx b/app/roulette/_components/RouletteResultModal.tsx
index f17523d..259f59a 100644
--- a/app/roulette/_components/RouletteResultModal.tsx
+++ b/app/roulette/_components/RouletteResultModal.tsx
@@ -18,31 +18,17 @@ const ITEM_IMAGE_MAP: Record
= {
"옵션권 1장": "/roulette/item/option_ticket_1.png",
"옵션권 2장": "/roulette/item/option_ticket_2.png",
"옵션권 5장": "/roulette/item/option_ticket_5.png",
- 꽝: "/roulette/item/miss.png", // TODO: 꽝 이미지 추가 필요
+ 꽝: "/roulette/item/no-luck.png",
"뽑기권 1장": "/roulette/item/draw_ticket_1.png",
"뽑기권 5장": "/roulette/item/draw_ticket_5.png",
"뽑기권 10장": "/roulette/item/draw_ticket_10.png",
풀세트: "/roulette/item/full_set.png",
- "1만원권 상품권": "/roulette/item/gift_card_10000.png", // TODO: 상품권 이미지 추가 필요
- "2만원권 상품권": "/roulette/item/gift_card_20000.png", // TODO: 상품권 이미지 추가 필요
-};
-
-// 아이템 설명 텍스트
-const ITEM_DESCRIPTION_MAP: Record = {
- "옵션권 1장": "매칭 시 옵션 1개를 선택할 수 있어요",
- "옵션권 2장": "매칭 시 옵션 2개를 선택할 수 있어요",
- "옵션권 5장": "매칭 시 옵션 5개를 선택할 수 있어요",
- 꽝: "아쉽지만 다음 기회에!",
- "뽑기권 1장": "새로운 매칭 기회가 생겼어요",
- "뽑기권 5장": "새로운 매칭 기회 5번이 생겼어요",
- "뽑기권 10장": "새로운 매칭 기회 10번이 생겼어요",
- 풀세트: "뽑기권 1장 + 옵션권 3장 획득!",
- "1만원권 상품권": "1만원 상품권이 지급됩니다",
- "2만원권 상품권": "2만원 상품권이 지급됩니다",
+ "1만원권 상품권": "/roulette/item/gift_card_10000.png",
+ "2만원권 상품권": "/roulette/item/gift_card_20000.png",
};
// ==========================================
-// 컨페티 조각 (좌/우)
+// 컨페티 조각 (장식용)
// ==========================================
const ConfettiLeft = () => (
@@ -84,8 +70,7 @@ export default function RouletteResultModal({
}: RouletteResultModalProps) {
if (!item) return null;
- const imageSrc = ITEM_IMAGE_MAP[item.label] ?? "/roulette/prizes/miss.png";
- const description = ITEM_DESCRIPTION_MAP[item.label] ?? "";
+ const imageSrc = ITEM_IMAGE_MAP[item.label] ?? "/roulette/item/no-luck.png";
return (