From b87304f51a2989264bf74f49013bc22151ef717f Mon Sep 17 00:00:00 2001 From: dasosann Date: Mon, 7 Sep 2026 15:31:56 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EB=A3=B0=EB=A0=9B=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=94=94=EC=9E=90=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 23 --- .../_components/MyTicketSection.tsx | 0 .../{free => }/_components/Roulette.tsx | 6 +- app/roulette/_components/RouletteCards.tsx | 141 +++++++++++++++++ .../roulette/_components/RouletteHeader.tsx | 8 +- .../_components/RouletteParticipationInfo.tsx | 0 .../RouletteProbabilityBottomSheet.tsx | 145 ++++++++++++++++++ .../_components/ScreenRouletteMain.tsx | 52 +++++++ .../free/_components/RouletteChanceBadge.tsx | 27 ---- .../free/_components/RouletteStatusBar.tsx | 29 ---- ...ouletteMain.tsx => ScreenRouletteFree.tsx} | 40 +++-- app/roulette/free/page.tsx | 4 +- app/roulette/page.tsx | 10 ++ .../_components/ScreenRouletteSpecial.tsx | 79 ++++++++++ .../_components/SpecialRouletteChanceCard.tsx | 99 ++++++++++++ app/roulette/special/page.tsx | 10 ++ public/roulette/free.png | Bin 0 -> 214787 bytes public/roulette/special.png | Bin 0 -> 298059 bytes public/roulette/ticket.png | Bin 0 -> 21883 bytes 19 files changed, 568 insertions(+), 105 deletions(-) delete mode 100644 .vscode/settings.json rename app/roulette/{free => }/_components/MyTicketSection.tsx (100%) rename app/roulette/{free => }/_components/Roulette.tsx (94%) create mode 100644 app/roulette/_components/RouletteCards.tsx rename components/common/PageHeader.tsx => app/roulette/_components/RouletteHeader.tsx (82%) rename app/roulette/{free => }/_components/RouletteParticipationInfo.tsx (100%) create mode 100644 app/roulette/_components/RouletteProbabilityBottomSheet.tsx create mode 100644 app/roulette/_components/ScreenRouletteMain.tsx delete mode 100644 app/roulette/free/_components/RouletteChanceBadge.tsx delete mode 100644 app/roulette/free/_components/RouletteStatusBar.tsx rename app/roulette/free/_components/{ScreenRouletteMain.tsx => ScreenRouletteFree.tsx} (69%) create mode 100644 app/roulette/page.tsx create mode 100644 app/roulette/special/_components/ScreenRouletteSpecial.tsx create mode 100644 app/roulette/special/_components/SpecialRouletteChanceCard.tsx create mode 100644 app/roulette/special/page.tsx create mode 100644 public/roulette/free.png create mode 100644 public/roulette/special.png create mode 100644 public/roulette/ticket.png diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index da82ce0..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "editor.formatOnSave": true, - "editor.defaultFormatter": "esbenp.prettier-vscode", - "[javascript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[javascriptreact]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "[css]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - }, - "css.lint.unknownAtRules": "ignore" -} diff --git a/app/roulette/free/_components/MyTicketSection.tsx b/app/roulette/_components/MyTicketSection.tsx similarity index 100% rename from app/roulette/free/_components/MyTicketSection.tsx rename to app/roulette/_components/MyTicketSection.tsx diff --git a/app/roulette/free/_components/Roulette.tsx b/app/roulette/_components/Roulette.tsx similarity index 94% rename from app/roulette/free/_components/Roulette.tsx rename to app/roulette/_components/Roulette.tsx index bf5660a..bcaac29 100644 --- a/app/roulette/free/_components/Roulette.tsx +++ b/app/roulette/_components/Roulette.tsx @@ -149,9 +149,9 @@ const Roulette = forwardRef( {/* 고정된 그림자: 회전하지 않음 */}
- {/* 룰렛 이미지만 회전 */} + {/* 룰렛 이미지만 회전 (rounded-full과 will-change 추가로 회전 시 네 모서리 돌출 방지) */}
( priority fill sizes="(max-width: 356px) 100vw, 356px" - className="block object-contain" + className="block rounded-full object-contain" />
diff --git a/app/roulette/_components/RouletteCards.tsx b/app/roulette/_components/RouletteCards.tsx new file mode 100644 index 0000000..1d03a50 --- /dev/null +++ b/app/roulette/_components/RouletteCards.tsx @@ -0,0 +1,141 @@ +"use client"; + +import React from "react"; +import Link from "next/link"; +import Image from "next/image"; +import { Check } from "lucide-react"; + +// ========================================== +// 1. 무료 룰렛 카드 +// ========================================== +type FreeRouletteCardProps = { + remainingChances?: number; +}; + +export const FreeRouletteCard = ({ + remainingChances = 1, +}: FreeRouletteCardProps) => { + return ( +
+ {/* Upper Content */} +
+ {/* Left Info */} +
+
+

무료 룰렛

+

+ 매일 1회 참여 가능 +
+ 행운을 확인해 보세요! +

+
+ + {/* Status Checkbox */} +
+
+ +
+ + 오늘 남은 횟수 +
+ + {remainingChances}회 + +
+
+
+ + {/* Right Image Area */} +
+ {/* Shadow Ellipse */} +
+ {/* Free Roulette Graphic */} + 무료 룰렛 +
+
+ + {/* Button */} + + 무료 룰렛 입장 + +
+ ); +}; + +// ========================================== +// 2. 스페셜 룰렛 카드 +// ========================================== +type SpecialRouletteCardProps = { + currentAmount?: number; + targetAmount?: number; +}; + +export const SpecialRouletteCard = ({ + currentAmount = 2000, + targetAmount = 3000, +}: SpecialRouletteCardProps) => { + return ( +
+ {/* Upper Content */} +
+ {/* Left Info */} +
+
+

스페셜 룰렛

+

+ 누적 {targetAmount.toLocaleString()}원 이상 +
+ 결제 시 참여 가능 +

+
+ + {/* Status Checkbox */} +
+
+ +
+ + 현재 누적 +
+ + {currentAmount.toLocaleString()}원 + {" "} + / {targetAmount.toLocaleString()}원 +
+
+
+ + {/* Right Image Area */} +
+ {/* Shadow Ellipse */} +
+ {/* Special Roulette Graphic */} + 스페셜 룰렛 +
+
+ + {/* Button */} + + 스페셜 룰렛 입장 + +
+ ); +}; diff --git a/components/common/PageHeader.tsx b/app/roulette/_components/RouletteHeader.tsx similarity index 82% rename from components/common/PageHeader.tsx rename to app/roulette/_components/RouletteHeader.tsx index 81313a0..6236338 100644 --- a/components/common/PageHeader.tsx +++ b/app/roulette/_components/RouletteHeader.tsx @@ -1,14 +1,14 @@ -"use client"; +"use client"; import { BackButton } from "@/components/ui/BackButton"; import React from "react"; -type PageHeaderProps = { +type RouletteHeaderProps = { title?: React.ReactNode; sidebar?: React.ReactNode; }; -const PageHeader = ({ title, sidebar }: PageHeaderProps) => { +const RouletteHeader = ({ title, sidebar }: RouletteHeaderProps) => { return (
@@ -30,4 +30,4 @@ const PageHeader = ({ title, sidebar }: PageHeaderProps) => { ); }; -export default PageHeader; +export default RouletteHeader; diff --git a/app/roulette/free/_components/RouletteParticipationInfo.tsx b/app/roulette/_components/RouletteParticipationInfo.tsx similarity index 100% rename from app/roulette/free/_components/RouletteParticipationInfo.tsx rename to app/roulette/_components/RouletteParticipationInfo.tsx diff --git a/app/roulette/_components/RouletteProbabilityBottomSheet.tsx b/app/roulette/_components/RouletteProbabilityBottomSheet.tsx new file mode 100644 index 0000000..6cad52b --- /dev/null +++ b/app/roulette/_components/RouletteProbabilityBottomSheet.tsx @@ -0,0 +1,145 @@ +"use client"; + +import React, { useState } from "react"; +import { X } from "lucide-react"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/ui/drawer"; +import { cn } from "@/lib/utils"; + +// 컴포넌트 내부에 직접 데이터 정의 +const FREE_ROULETTE_ITEMS = [ + { reward: "옵션권 2장", probability: "39%" }, + { reward: "옵션권 5장", probability: "25%" }, + { reward: "풀세트", probability: "18%" }, + { reward: "뽑기권 5장", probability: "10%" }, +]; + +const SPECIAL_ROULETTE_ITEMS = [ + { reward: "스타벅스 쿠폰", probability: "5%" }, + { reward: "10,000 코인", probability: "15%" }, + { reward: "5,000 코인", probability: "30%" }, + { reward: "1,000 코인", probability: "50%" }, +]; + +export interface RouletteProbabilityBottomSheetProps { + /** 바텀시트를 열 트리거 엘리먼트 */ + trigger: React.ReactNode; + /** 처음에 보여줄 기본 탭 (기본값: "free") */ + defaultTab?: "free" | "special"; + /** DrawerContent 추가 클래스명 */ + className?: string; +} + +export default function RouletteProbabilityBottomSheet({ + trigger, + defaultTab = "free", + className, +}: RouletteProbabilityBottomSheetProps) { + const [activeTab, setActiveTab] = useState<"free" | "special">(defaultTab); + + const currentItems = + activeTab === "free" ? FREE_ROULETTE_ITEMS : SPECIAL_ROULETTE_ITEMS; + + return ( + + {trigger} + + + {/* Header Section (DirectChargeDrawer 계좌이체 모달 헤더 구조 + 중앙 정렬) */} + +
+
+ + 당첨 확률 + + + + +
+ + 룰렛 보상 및 당첨 확률 안내 바텀시트입니다. + + + + {/* Content Section */} +
+ {/* Tabs (Chips) - Frame 2612797 */} +
+ + +
+ + {/* List Container - Frame 1171276922 */} +
+ + {activeTab === "free" ? "무료 룰렛" : "스페셜 룰렛"} + + + {/* Frame 1171276921 */} +
+ {/* Header - Frame 1171276915 */} +
+ 보상 + + 확률 + +
+ + {/* Items List - Frame 1171276920 */} +
+ {currentItems.map((item, index) => ( +
+ + {item.reward} + + + {item.probability} + +
+ ))} +
+
+
+
+ + + ); +} + +export { RouletteProbabilityBottomSheet as RouletteProbabilityDrawer }; diff --git a/app/roulette/_components/ScreenRouletteMain.tsx b/app/roulette/_components/ScreenRouletteMain.tsx new file mode 100644 index 0000000..b5b0cb2 --- /dev/null +++ b/app/roulette/_components/ScreenRouletteMain.tsx @@ -0,0 +1,52 @@ +"use client"; + +import React from "react"; +import RouletteHeader from "./RouletteHeader"; +import RouletteProbabilityBottomSheet from "./RouletteProbabilityBottomSheet"; +import { FreeRouletteCard, SpecialRouletteCard } from "./RouletteCards"; + +/** + * 룰렛 메인 화면 (허브) 컴포넌트 + */ +const ScreenRouletteMain = () => { + return ( +
+ {/* Roulette Header */} +
+ + 확률 안내 + + } + /> + } + /> +
+ + {/* Content */} +
+ {/* Title */} +

+ 오늘의 행운을 확인해보세요! +
+ 매일 1회 무료로 돌릴 수 있어요 +

+ + {/* Cards */} +
+ + +
+
+
+ ); +}; + +export default ScreenRouletteMain; diff --git a/app/roulette/free/_components/RouletteChanceBadge.tsx b/app/roulette/free/_components/RouletteChanceBadge.tsx deleted file mode 100644 index 8b1a983..0000000 --- a/app/roulette/free/_components/RouletteChanceBadge.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { cn } from "@/lib/utils"; -import React from "react"; - -interface RouletteChanceBadgeProps { - remainingCount?: number; - className?: string; -} - -const RouletteChanceBadge = ({ - remainingCount = 2, - className, -}: RouletteChanceBadgeProps) => { - return ( -
- - 남은 기회 {remainingCount}개 - -
- ); -}; - -export default RouletteChanceBadge; diff --git a/app/roulette/free/_components/RouletteStatusBar.tsx b/app/roulette/free/_components/RouletteStatusBar.tsx deleted file mode 100644 index f83087f..0000000 --- a/app/roulette/free/_components/RouletteStatusBar.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from "react"; - -const RouletteStatusBar = () => { - return ( -
-
- - 9:41 - -
- -
-
- - - - -
-
-
-
-
-
-
-
- ); -}; - -export default RouletteStatusBar; diff --git a/app/roulette/free/_components/ScreenRouletteMain.tsx b/app/roulette/free/_components/ScreenRouletteFree.tsx similarity index 69% rename from app/roulette/free/_components/ScreenRouletteMain.tsx rename to app/roulette/free/_components/ScreenRouletteFree.tsx index 354bf37..cef8a60 100644 --- a/app/roulette/free/_components/ScreenRouletteMain.tsx +++ b/app/roulette/free/_components/ScreenRouletteFree.tsx @@ -1,14 +1,15 @@ "use client"; import React, { useRef, useState } from "react"; import { CircleAlert } from "lucide-react"; -import PageHeader from "@/components/common/PageHeader"; +import RouletteHeader from "../../_components/RouletteHeader"; import Button from "@/components/ui/Button"; -import Roulette, { RouletteHandle } from "./Roulette"; +import Roulette, { RouletteHandle } from "../../_components/Roulette"; +import RouletteProbabilityBottomSheet from "../../_components/RouletteProbabilityBottomSheet"; // TODO: 실제 API 연동 시 대체 const MOCK_REMAINING_CHANCES = 1; -const ScreenRouletteMain = () => { +const ScreenRouletteFree = () => { const remainingChances = MOCK_REMAINING_CHANCES; const hasChances = remainingChances > 0; @@ -21,15 +22,22 @@ const ScreenRouletteMain = () => { }; return ( -
+
{/* Top Group: Header, Badge, Tagline */}
- - 확률 안내 - + + 확률 안내 + + } + /> } /> @@ -41,7 +49,7 @@ const ScreenRouletteMain = () => {
{/* Tagline */} -

+

{hasChances ? ( <> 매일 한 번,{" "} @@ -55,13 +63,11 @@ const ScreenRouletteMain = () => {

{/* Center: Roulette Wheel */} -
- console.log("당첨 인덱스:", idx)} - /> -
+ console.log("당첨 인덱스:", idx)} + /> {/* Bottom Group: Spin Button + Notice */}
@@ -83,4 +89,4 @@ const ScreenRouletteMain = () => { ); }; -export default ScreenRouletteMain; +export default ScreenRouletteFree; diff --git a/app/roulette/free/page.tsx b/app/roulette/free/page.tsx index b0dfbbc..495aa16 100644 --- a/app/roulette/free/page.tsx +++ b/app/roulette/free/page.tsx @@ -1,4 +1,4 @@ -import ScreenRouletteMain from "./_components/ScreenRouletteMain"; +import ScreenRouletteFree from "./_components/ScreenRouletteFree"; export const metadata = { title: "이벤트 룰렛", @@ -6,5 +6,5 @@ export const metadata = { }; export default function RoulettePage() { - return ; + return ; } diff --git a/app/roulette/page.tsx b/app/roulette/page.tsx new file mode 100644 index 0000000..4be33e4 --- /dev/null +++ b/app/roulette/page.tsx @@ -0,0 +1,10 @@ +import ScreenRouletteMain from "./_components/ScreenRouletteMain"; + +export const metadata = { + title: "룰렛", + description: "룰렛 이벤트", +}; + +export default function RoulettePage() { + return ; +} diff --git a/app/roulette/special/_components/ScreenRouletteSpecial.tsx b/app/roulette/special/_components/ScreenRouletteSpecial.tsx new file mode 100644 index 0000000..e6ed206 --- /dev/null +++ b/app/roulette/special/_components/ScreenRouletteSpecial.tsx @@ -0,0 +1,79 @@ +"use client"; +import React, { useRef, useState } from "react"; +import { CircleAlert } from "lucide-react"; +import RouletteHeader from "../../_components/RouletteHeader"; +import Button from "@/components/ui/Button"; +import Roulette, { RouletteHandle } from "../../_components/Roulette"; +import RouletteProbabilityBottomSheet from "../../_components/RouletteProbabilityBottomSheet"; +import SpecialRouletteChanceCard from "./SpecialRouletteChanceCard"; + +// TODO: 실제 API 연동 시 대체 +const MOCK_REMAINING_CHANCES = 1; + +const ScreenRouletteSpecial = () => { + const remainingChances = MOCK_REMAINING_CHANCES; + const hasChances = remainingChances > 0; + + const rouletteRef = useRef(null); + const [isSpinning, setIsSpinning] = useState(false); + + const handleSpin = () => { + if (!hasChances || isSpinning) return; + rouletteRef.current?.spin(); + }; + + return ( +
+ {/* Top Group: Header, Badge, Tagline */} +
+ + 확률 안내 + + } + /> + } + /> + + {/* Special Remaining chances card */} +
+ +
+
+ + {/* Center: Roulette Wheel */} + console.log("당첨 인덱스:", idx)} + /> + + {/* Bottom Group: Spin Button + Notice */} +
+ + + {/* Participation notice */} +
+ + 결제 취소 시 지급된 보상이 회수될 수 있어요 +
+
+
+ ); +}; + +export default ScreenRouletteSpecial; diff --git a/app/roulette/special/_components/SpecialRouletteChanceCard.tsx b/app/roulette/special/_components/SpecialRouletteChanceCard.tsx new file mode 100644 index 0000000..5d0e7bb --- /dev/null +++ b/app/roulette/special/_components/SpecialRouletteChanceCard.tsx @@ -0,0 +1,99 @@ +"use client"; + +import React from "react"; +import Image from "next/image"; +import { cn } from "@/lib/utils"; + +export interface SpecialRouletteChanceCardProps { + /** 현재 결제 누적 금액 (기본값: 1000) */ + currentAmount?: number; + /** 다음 룰렛 기회까지의 목표/토탈 금액 (기본값: 3000) */ + targetAmount?: number; + /** 상단 누적 결제 정보 텍스트 (옵션, 기본값: "누적 결제 {currentAmount}원") */ + accumulatedText?: string; + /** 상단 추가 결제 안내 텍스트 (옵션, 기본값: "{남은금액}원 추가 결제 시 1회") */ + additionalText?: string; + /** 우측 하단 목표 금액 텍스트 (옵션, 기본값: "{targetAmount}원") */ + targetText?: string; + className?: string; +} + +export default function SpecialRouletteChanceCard({ + currentAmount = 1000, + targetAmount = 3000, + accumulatedText, + additionalText, + targetText, + className, +}: SpecialRouletteChanceCardProps) { + const diff = Math.max(0, targetAmount - currentAmount); + const percent = Math.min( + 100, + Math.max(0, Math.round((currentAmount / targetAmount) * 100)), + ); + + const displayAccumulated = + accumulatedText ?? `누적 결제 ${currentAmount.toLocaleString()}원`; + const displayAdditional = + additionalText ?? `${diff.toLocaleString()}원 추가 결제 시 1회`; + const displayTarget = targetText ?? `${targetAmount.toLocaleString()}원`; + + return ( +
+ {/* Chances container (상단 티켓 아이콘 + 결제 정보) */} +
+ {/* Ticket Image */} +
+ ticket +
+ + {/* Info container */} +
+ + {displayAccumulated} + + + {displayAdditional} + +
+
+ + {/* Progress bar container (하단 진행도 바 + 목표 금액) */} +
+ {/* Progress bar background */} +
+ {/* Progress bar fill */} +
+ {/* Progress indicator bar */} + {percent > 0 && percent < 100 && ( +
+ )} +
+
+ + {/* Amount text */} + + {displayTarget} + +
+
+ ); +} + +// 편의를 위한 별칭 export +export { SpecialRouletteChanceCard as RemainingChancesContainer }; diff --git a/app/roulette/special/page.tsx b/app/roulette/special/page.tsx new file mode 100644 index 0000000..8463166 --- /dev/null +++ b/app/roulette/special/page.tsx @@ -0,0 +1,10 @@ +import ScreenRouletteSpecial from "./_components/ScreenRouletteSpecial"; + +export const metadata = { + title: "스페셜 룰렛", + description: "스페셜 룰렛 진행", +}; + +export default function SpecialRoulettePage() { + return ; +} diff --git a/public/roulette/free.png b/public/roulette/free.png new file mode 100644 index 0000000000000000000000000000000000000000..cf9e34338d7b028c428d61f4bedd02bb9f793e12 GIT binary patch literal 214787 zcmeEt5D5_>ARtiWWF^%hAfR;rT?lX=E8nd%*FSCm7g;@b z2nZ&He-~ugr}V0iMM!sb8F7gEY2rU04=^@jN@5TYO<$2-%wRvBQ9Yz|Jv5xHJ-p4_ ztRSR6+c=u#H>F!ZKuZV7Ns4LuK>mFNbT}?)kwztS{#rdL|B^5V{zS6}chKjp-WlT!!ztrylXv`UG6<*;QF3$ynE!wK ze@g{Dx`qGQja54Q-UbCKWScqy=HDA!n1?-a2GOD;7?;Y5{WC19HbNEV|JA0_%^m48R)HM7r_hH5c4GVcGj_ns?r;h^n&ky3 zA++}sN~^&t+pOa(>j_Tgs;3Ih+9nkp-DP~fe#Iu;N~-$Z6wk|(W@7{C?dvA!NvLWmR#L10L6Lx z4G-b)_r13f>t%i}NY*ce5-)KR(&JOP9-~Fc;K-JyQm9Iq`J`dO$-U%I7nzF!DZnyq5iUBUqHFWg`&Y{WoJNF`CTl@#OII*l#u32RTl{)4YW zi;->AIv;-@Kuz&a3dx5SeY(uYD9x$fcF55+g-ZEe%;Vc`imA<*mqQ+)1`^vGr);UZ zMER2hP!zUG4ZQ9fg}eivIwX{O#nzE=T)2oBuGE5dkL8ezEyw5_Ja1W$gfsh68lzc1 zhaXKaz4~Lu|Ka#6#9GM2(f||hbhHEHdSD4?K7*$5`#nCF{r!N=@q5P8-pRI+C5fwRO{j*5 zx6Rv;%ZdL^-Jv;tP=SAUDpjmp^?Y*}^yjA0#Ku)jGUbn1nnc$7IhM76@ZQ-B?cxP( zin4V-lJ2l`PqqFQ0B_nfG=VXGye)9vX*_hE|H~yP5JBnooFeZ9*lKY{4UN((FMG}-4_gOPf6D_UcEb+QFN(d+YT9KyK38S;vbLn`sQsdj^C&PoE zxc%B+ahTM{;bfafEKqT#+aQL*AmH&Jo%G;zKVRPkAI5=fmPY;9o}kmLgYn*;mfngI z|M_)*IKsyPi`oX@Vles$!o8)Rg#ZI-t5vq5%sO+}VR(^-#6tIE#bPEaqUEtMNGUaL zgx7|3!X$jZl~W{_kz$YmCDx2y6n6$O_Ep)Vi-{R14O0E3^wd}=<(x~6lMZp}XyqRL z!HXO_!-SDg^bWGa4hOM?L5Dc=I*Q^i>6<0(VP z(+yjDS~qxhHoDzy{LWbXFAg3;|1)(+h^x%|kF7tVZ8uZ0wckI?wq$Wshx!A^&VWO& zaS96M4=}OgtfDZ0L7b^8;b@(2Kb{+_R)h7Sw5r9<)i2Zf+ypTXGtqxhm3VYva#otR(Addfns$L)lLW0^nJzU5Y1rF5My;hlC^9{6H_^0bEE}2Cuy;A#(G@K@Gd^(@GQAUBJpMr@%9vL68>H5_+#}hqmuj4SCPKk z&iQk_m-Hove_$hk;6M4|%)~?cg8X0!|7=MX-{6_G^dYp=4Wlz4zD}*ElEa>~v6r@i zO6`!w7mW+#a4_(VQUQOY(4XcZJKcuCRm!W?x*(G()8af*eTj>$ga&$6cO1fE-y^AC zXr2`>0Tx%)0kxthY zOXP$VqeF*sgXJlOAJ{qwyV{$O(_xrP`i;|`et5lhbwW?ueX5!ht*Iuc$Dl#Gv!@OPZ<9)))1nmDL*bQ_nL8@GvdB!Mle)CGP%S>X7o%hNT?U-y zsl9~p`KPcm;(E7|Po!v5b+w6;5qh;rmuBaBb2*Edx|6qgS}itC2T!#wwB{~LE!28* z=yUtjFZ6g&=p%u8FE+8!O99XO6j`71C98a;>PIem{7*-nJCL&5!|_{)VdEl^u1zov z0^&QOyC!8eN9kYojxnpXZFnGf+I+dT`4C{GCFO_Q6FRS}wfDOZbNSI1S`+XPrm&lp-)=CYjE z^tQsqR~xdEVF}R{c{b<}n+9hR%g^vp>aVx5quWHLw?ZPOV1Y?SwS(~hmp{@Xi~5Ee zEEQ!ez5p@h9HA1nQ0#MOz~GYacAP@?$@UX|9Ex@&=oHTjc`#1c)i~`<4~yt`>8rfPOK#$GxA
{/* Tagline */} -

+

{hasChances ? ( <> 매일 한 번,{" "}