π° λ΄μ€λ₯Ό 보λ λ
μΌλ§ μ μ μ΄λ° λ΄μ€λ₯Ό λ³΄κ³ , μΈμ κ° μλΆμ¬(?)μ κ°μ§ μ격μ μ¦λͺ ν μ μλ 무μΈκ°κ° μκΈ°λ κ² μλκΉ νλ μμμ ν΄λ΄€μ΄μ.
λ§μΉ¨ μ§μΈμ΄ λ°±μ μ λ§κ³ λμ μ±μΌλ‘ μλ°©μ μ’ μ¦λͺ μλ₯Ό λ°μλ€κ³ ν κ² λ μ¬λΌμ, μ΄κ²λ μ¦λͺ μ μ±/μΉμ ννλ‘ ν¨λ¬λν΄μ λ§λ€λ©΄ μ¬λ―Έμμ κ² κ°μμ£ !
π¨ μ¦λͺ μ λ§λ€κΈ°
βοΈ μ₯μμ₯μ λμμΈ
λ€ λ§λ€κ³ λμ 보λ μκ°λ³΄λ€ λ§μ μκ°μ λ³΄λΈ κ² κ°μ§λ§, μ²μμλ μ΅λν λΉ λ₯΄κ² λ§λ€κ³ μΆμκΈ° λλ¬Έμ μ¦λͺ μ? κ·Έλ₯ μ΄λ―Έμ§λ‘ λμΆ© κ·Έλ €μ λ£κ³ νμ΄μ§ μ€μ μ λ ¬λ§ μμΌμ 보μ¬μ£Όμ! νμ΅λλ€.
μ λ λ°±μ μ λͺ» λ§κ³ μκΈ° λλ¬Έμ...! μΈν°λ·μμ κ΅΄λ¬λ€λλ μλ°©μ μ’ μ¦λͺ μμ μ€ν¬λ¦°μ·(μμ 첨λΆν μ΄λ―Έμ§μ κ°μ΄ ν μ€νΈμ© μ±μ μ€ν¬λ¦°μ·μ μ¬μ©νμ΅λλ€)μ κ΅¬ν΄ μμ λκ³ λΉμ·ν λλμΌλ‘ νλ κ·Έλ €λ΄€μ΅λλ€... π
βοΈ μ΄λ―Έμ§ νμνκΈ°
μ΄λ―Έμ§λ₯Ό νμνκ³ , νλ©΄ μ€μμ μ λ ¬μμΌ λ΄€μ΄μ. ν λΉμ΄ μμ΄μ λ§μ΄ μΈλ‘μ 보μ΄λ€μ.
width: 300px;border-radius: 8px;box-shadow: 0px 16px 36px rgba(0, 0, 0, 0.05);
@media (max-width: 400px) { width: 285px;}
css
- λͺ¨μ리λ₯Ό λ₯κΈκ² ν©λλ€.
- μ 체κ°μ μν΄ μ½κ°μ κ·Έλ¦Όμλ₯Ό λ£μ΄ λ΄€μ΄μ.
- λμ΄λ₯Ό μ€μ ν΄ μ€¬μ΄μ. μ‘°κ·Έλ§ λͺ¨λ°μΌ νλ©΄μμλ μμ°μ€λ½κ² λ³΄μΌ μ μλλ‘ λ―Έλμ΄ μΏΌλ¦¬λ‘ μ²λ¦¬λ ν΄μ€λλ€.
π μ΄λͺ¨μ§ νμ£½ ν°λ¨λ¦¬κΈ°
μ λ μ¦λͺ
μ νλ©΄μ λ€μ΄κ°μλ§μ μ΄λͺ¨μ§κ° μμμ Έ λ΄λ¦¬κΈΈ μνμ΄μ!
μ΄λ€ λΌμ΄λΈλ¬λ¦¬κ° μλ νκ³ κΉνλΈμμ μ¬λ¬ κ°μ λͺ¨λμ λΉκ΅ν΄ λ΄€μλλ°μ.
js-confettiκ° κ°μ₯ κΉλν κ² κ°μμ΄μ. μ²μμλ react-rewardsλ₯Ό μ¬μ©νλ €κ³ νμλλ°, μ΄ λͺ¨λμ μ€νλ νμ£½μΌλ‘ μ¬μ©νλ κ²λ³΄λ€λ μ¬μ©μμ μΈν°λ μ
μ λ°λ₯Έ 보μμ΄λ μ²λ²μ λ΄λ¦¬λ μν (μ€μ λ‘ λ©μλ μ΄λ¦μ΄ rewardMe
, punishMe
λ€μ γ·γ·)μ΄ λ μ μ ν΄ λ³΄μμ£ . ~~μ¬μ€ μ€ν¬λ‘€ λ²κ·Έκ° νλ μμλλ° κ³ μΉκΈ° μ΄λ €μμ ν¨μ€νμ΅λλ€.~~
export const Card: React.FC<CardProps> = () => { useEffect(() => { const confetti = new JSConfetti(); confetti.addConfetti({ emojis: ['π°π·', 'πΈ', 'π΅', 'π'], emojiSize: 256, confettiNumber: 30, confettiRadius: 6, }); }, []);
return ( <CardContainer> <CardImage src="/images/card.svg" /> </CardContainer> );};
tsx
μ΄λ κ² μ²μ ν λ²λ§ JSConfetti
ν΄λμ€λ₯Ό λ§λ€λ©΄, Canvas μ리먼νΈλ₯Ό νμ¬ document
μ μΆκ°νλ©΄μ λͺ¨λμ΄ μ΄κΈ°νλ©λλ€. κ·Έ λ€λ‘ addConfetti
λ©μλλ₯Ό νΈμΆν λλ§λ€ νμ£½μ΄ ν°μ§μ£ !
μ κ·Έλ°λ° μ λ Next.jsλ₯Ό μ¬μ©νκ³ μμλλ°μ. μ λ κ² μ½λλ₯Ό μ§λ μλ²μ¬μ΄λμμ λ λλ§λ λ new JSConfetti()
κ° μ€νλλ©΄μ ReferenceError: document is not defined
μλ¬κ° λ°μνλ λ¬Έμ κ° μμμ΄μ.
useEffect(() => { if (typeof document === 'undefined') { return; }
// client only const confetti = new JSConfetti(); confetti.addConfetti({ emojis: ['π°π·', 'πΈ', 'π΅', 'π'], emojiSize: 256, confettiNumber: 30, confettiRadius: 6, });}, []);
tsx
Node νκ²½μμλ document
globalμ΄ μ μΈλμ§ μμκΈ° λλ¬Έμ μκΈ°λ κ²μ΄μ£ ! ν΄λΌμ΄μΈνΈ μ¬μ΄λμμλ§ μ€ννκΈ° μν΄μλ μ μ½λμ²λΌ typeof document
λ‘ λΆκΈ°ν μ μκ² λ€μ.
μμ λ°°κ²½λ λ£μ΄μ€λλ€. μ΄μ λλμ΄ μ’ μ¦λͺ μ κ°μ λλμ΄ λ©λλ€!
const Container = styled.div` width: 100%; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; background-image: url('/images/gradient.webp'); background-size: cover;`;
tsx
π¨ λλ₯Ό λλ§λ€ λ μ°κΈ°
κ·Έλ°λ° μμ§ λκ° μ°λ ν λλμ΄ λ€μ΄μ, λ νλ©΄ μ’μκΉ μκ°νμλλ°μ. μΈμ¦μλ₯Ό ν΄λ¦ν λλ§λ€ 25λ§μ(μ¬λμ§μκΈ μ‘μ)μ© μΉ΄μ΄νΈκ° μ¬λΌκ°λ(λμ΄ μ°νλ?) κ² λ³΄μ΄λ©΄ μ΄λ¨κΉ ν΄μ κ·Έλ₯ κ·Έλ κ² νκΈ°λ‘ νμ΅λλ€. λ³ λ»μ΄ μλ λμμ μλμ§λ§ 보μλ λΆλ€κ»μ μ²μ 3μ΄ λμμ΄λΌλ κ·Έλ₯ μ¬λ―Έμμ΄μ λͺ λ² λλ₯Ό κ² κ°μμ΄μ.
βοΈ μ»€μ€ν
ν
useCount
ꡬν
ν΄λ¦μ λκ΅°κ°μ λ
Έλ ₯μ΄μ£ ! μ¬μ΄νΈλ₯Ό μλ‘κ³ μΉ¨νλ λ°λμ μνκ° λͺ¨λ λ μκ°λ©΄ λ무 μ¬νκΈ° λλ¬Έμ μ΄λ²μλ κ·Έ κ°μ localStorage
μ μ μ₯νκΈ°λ‘ νμ΅λλ€.
export const useCount = (): [number, () => void] => { const [count, setCount] = useState<number>(() => { if (typeof localStorage === 'undefined') { return 0; }
const storedNumber = parseInt(localStorage.getItem('@count')); return storedNumber || 0; });
const updateCount = useCallback(() => { const nextCount = count + 1; localStorage.setItem('@count', nextCount.toString()); setCount(nextCount); }, [count]);
return [count, updateCount];};
ts
localStorage
λ λ¬Έμμ΄ ν€μ λ¬Έμμ΄ κ°μ 맀νν΄ μ£ΌκΈ° λλ¬Έμ, νμ¬ count
μνλ₯Ό μ
λ°μ΄νΈν λλ§λ€ κ·Έ κ°μ String
μΌλ‘ λ°κΏ μ μ₯νλλ‘ νμ΄μ. μ²μμ μ μ₯λ μνκ° μλμ§ νμΈνκ³ , κ°μ Έμ¬ λλ κ·Έ κ°μμ νμ±νκ³ μ!
π ν΄λ¦ μμ λ°λΌ μ°μ λ νμνκΈ°
λ§λ ν μ μ΄λ κ² μ¬μ©ν μ μμ΄μ!
const [clicks, updateClicks] = useCount();const message = useMemo(() => { if (!clicks) { return 'μΈμ¦μλ₯Ό λλ¬ λμ μ°μ΄ 보μΈμ.'; } return `${convert(',$.3s', STIMULUS * clicks)}μ μ°μ΄λ΄μ
¨λ€μ.`;}, [clicks]);
...
<Card onClick={updateClicks} />
tsx
μ¬κΈ°μ convert
ν¨μλ uckμ΄λΌλ λͺ¨λμ κ²μ
λλ€. μ«μλ₯Ό μμ°μ€λ½κ² νκΈλ‘ μ½μ΄μ£Όλ μν μ ν©λλ€.
,$.3s
λ¬Έμμ΄μconvert
ν¨μμ 쑰건μ λνλ λλ€(μ λ§ν¬ README μ°Έκ³ ).,
: μ² μ리λ§λ€ μ½€λ§(,
)λ₯Ό νμν κ²$
: μμ±λ νκΈ λ¬Έμμ΄μμ
λ¨μλ₯Ό λΆμΌ κ².3
: precision μ μ€μ (μ¬κΈ°μ ν¬κ² μ€μνμ§ μμμ ν¨μ€νκ³ μμ κ·Έλλ‘ λΆμλ κ² κ°μμ)s
: λ¨μλ§λ€ 곡백 ν μΉΈ μΆκ°(space
μs
λ₯Ό μλνμ λ―)
- 2λ
μ λ μ§λ λΌμ΄λΈλ¬λ¦¬μ΄κΈ°λ νμ§λ§, μ¬μ©νλ©΄μ κ΅³μ΄ λ°λ‘ λ
μ ν¬λ§·μ λ§λ€ νμκ° μμμκΉ νλ μμ¬μμ΄ μμμ΅λλ€(μ΄κ² λ§κ³ λ κ°μ₯ κΉλνκΈ°λ νκ³ μ’μμ΄μ). λ¬Όλ‘ μ½λκ° μ§§μμ§κΈ΄ νμ§λ§
uck
μ λͺ¨λ₯΄λ μ¬λμ΄ νλμ μκΈ°λ μ’ μ΄λ ΅μ§ μλ μΆλ€μ. μλμ²λΌ κ·Έλ₯ μ΅μ μ λ€λ£¨λ Objectλ₯Ό μ λ¬ν μ μμλ€λ©΄ ν¨μ¬ μ μ½νκΈ°λ νκ³ TypeScript λͺ¨λμ μ₯μ μ λμ± νμ©ν μ μμμ κ² κ°μμ.
convert(value, { suffix: 'μ', precision: 3, addComma: true, addSpace: true,});
ts
β μμ±
ν΄λ¦ν λλ μ΄λͺ¨μ§ νμ£½μ ν°λ¨λ¦¬κ² νκ³ , μ½κ°μ μ λλ©μ΄μ κ³Ό μ€νμΌμ λνμ μλ κ°μ κ²°κ³Όλ¬Όμ΄ λμμ΄μ!
μλΆμ¬μ μ€μ€λ‘ κ°μ§λ κ²μ λλ€.
μ½μ΄μ£Όμ μ κ°μ¬ν©λλ€. π
μλΉμ€λ https://pride-stimulus.vercel.appμμ, μ½λλ https://github.com/junhoyeo/pride-stimulusμμ νμΈνμ€ μ μμ΅λλ€!