๐Ÿฎ ๋ฏธ๋‹ˆ ํ”„๋กœ์ ํŠธ/๐Ÿ… ๋ฝ€๋ชจ๋„๋กœ

๐Ÿ… REACT ๋ฝ€๋ชจ๋„๋กœ ํƒ€์ด๋จธ ๋งŒ๋“ค๊ธฐ (3) ๐Ÿ…

์ง„๋ฐฉ์ด 2025. 2. 3. 11:01

  ๋จผ์ € ๋Œ์•„๊ฐ€๋Š” ๋ฐ”๋Š˜์„ ์ œ์™ธํ•œ Pomodoro ํŽ˜์ด์ง€๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

๊ทธ๋Ÿฌ๊ธฐ ์œ„ํ•ด์„  Background ์ปดํฌ๋„ŒํŠธ์™€ TomatoBtn ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค.


Background ์ปดํฌ๋„ŒํŠธ

// Background.js

import { useNavigate } from "react-router-dom";
import "./Background.css";

export default function Background(props) { 
    let navigate = useNavigate();
    let goToMenu = () => {
        navigate('/'); // "/" ๊ฒฝ๋กœ(ํ™ˆ)๋กœ ์ด๋™
    }
    return (
        <>
        <h1 onClick={goToMenu}>๐Ÿ…{props.title}๐Ÿ…</h1>
        </>
    );
}
  • ๋ฐฐ๊ฒฝ์ƒ‰์„ ์ง€์ •ํ•˜๊ณ  ํด๋ฆญํ•˜๋ฉด ํ™ˆ์œผ๋กœ ๋Œ์•„๊ฐ€๋Š” ์ œ๋ชฉ์„ ํ‘œ์‹œํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. 
  • export default๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค๋ฅธ ํŒŒ์ผ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค. 
  • useNavigate()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ navigate ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ–ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ํŽ˜์ด์ง€ ์ด๋™์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.
  • {props.title}์„ ์‚ฌ์šฉํ•˜์—ฌ title ๊ฐ’์ด ๋™์ ์œผ๋กœ ํ‘œ์‹œ๋œ๋‹ค.  (์ปดํฌ๋„ŒํŠธ์˜ props๋ฅผ ํ™œ์šฉํ•˜์—ฌ title ๊ฐ’์„ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ)
// Background.css

@font-face {
    font-family: 'TmoneyRoundWindExtraBold';
    src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_20-07@1.0/TmoneyRoundWindExtraBold.woff') format('woff');
    font-weight: normal;
    font-style: normal;
}

h1 {
    text-align: center;
    font-family: 'TmoneyRoundWindExtraBold';
    font-weight: bold;
    color: #F35D50;
    font-size: 80px;
    text-shadow: 0px 0px 15px #CF9590;
    cursor: pointer;
}
* {
    background-color: #D6F4D8;
}
  • ํ”ผ๊ทธ๋งˆ์—์„œ๋Š” Baloo ํฐํŠธ๋ฅผ ์‚ฌ์šฉํ–ˆ๋Š”๋ฐ ์ด ํฐํŠธ๊ฐ€ ์ด์ƒํ•˜๊ฒŒ ์ ์šฉ์ด ์•ˆ๋˜์–ด์„œ ๋‹ค๋ฅธ ํฐํŠธ๋กœ ๋Œ€์ฒดํ–ˆ๋‹ค...

TomatoBtn ์ปดํฌ๋„ŒํŠธ

// TomatoBtn.js

import { useState } from "react";
import "./TomatoBtn.css";
import pic1 from "../Pic/Tomato.png";
import pic11 from "../Pic/Tomatoo.png";

export default function TomatoBtn(props) {
    const [isActive, setIsActive] = useState(false);
    
    const MouseOn = () => {
        setIsActive(true);
    }
    const MouseOff = () => {
        setIsActive(false);
    }

    return (
        <div 
            className="Wrap" 
            onClick={props.onClick} 
            onMouseEnter={MouseOn}
            onMouseLeave={MouseOff}
            role="button"
            tabIndex="0"
        >
            <img src={isActive ? pic11 : pic1} alt="" className="Tomato" />
            <p>{props.name}</p>
        </div>
    );
}
  • isActive ๋ณ€์ˆ˜๋ฅผ ํ†ตํ•ด ์ด๋ฏธ์ง€๋ฅผ ์ด์šฉํ•œ ๋ฒ„ํŠผ์— ๋งˆ์šฐ์Šค๊ฐ€ ์˜ฌ๋ผ์™€ ์žˆ๋Š”์ง€์˜ ์—ฌ๋ถ€๋ฅผ ์ €์žฅํ•œ๋‹ค. ์ดˆ๊ธฐ๊ฐ’์€ false๋กœ ํ•ด๋‘์–ด ๋งˆ์šฐ์Šค๊ฐ€ ์˜ฌ๋ผ์™€ ์žˆ์œผ๋ฉด true๋ฅผ, ์˜ฌ๋ผ์™€์žˆ์ง€ ์•Š์œผ๋ฉด false๋ฅผ ์ €์žฅํ•œ๋‹ค. 
  • <div>๊ฐ€ ๋ฒ„ํŠผ ์—ญํ• ์„ ํ•œ๋‹ค. ์ ‘๊ทผ์„ฑ ํ–ฅ์ƒ์„ ์œ„ํ•ด role="button"์„ ์ถ”๊ฐ€ํ–ˆ์œผ๋ฉฐ, Tap ํ‚ค๋กœ ์ด๋™ํ•˜๊ณ  Enter๋‚˜ Space๋กœ ํด๋ฆญํ•  ์ˆ˜ ์žˆ๋„๋ก tabIndex="0"๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค. (๊ทธ๋Ÿฐ๋ฐ ์™œ ์•ˆ ๋ ๊นŒ...)
  • isActive๊ฐ€ true์ผ ๋•Œ๋Š” pic11์ด, false์ผ ๋•Œ๋Š” pic1์ด ํ‘œ์‹œ๋œ๋‹ค. pic11๊ณผ pic1๋Š” ํ† ๋งˆํ†  ๊ผญ์ง€์ƒ‰๋งŒ ๋‹ค๋ฅธ ๊ฐ™์€ ์ด๋ฏธ์ง€์ด๋‹ค.
  • props.name์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฒ„ํŠผ์˜ ์ด๋ฆ„์„ ๋™์ ์œผ๋กœ ์„ค์ • ๊ฐ€๋Šฅํ•˜๋‹ค.
// TomatoBtn.css

@font-face {
    font-family: 'TmoneyRoundWindExtraBold';
    src: url('https://fastly.jsdelivr.net/gh/projectnoonnu/noonfonts_20-07@1.0/TmoneyRoundWindExtraBold.woff') format('woff');
    font-weight: normal;
    font-style: normal;
}
p {
    font-family: 'TmoneyRoundWindExtraBold';
    color: #FFF674;
    font-size: 40px;
    margin: 0 0 35px;
    background-color: transparent;
    text-align: center;
    cursor: default;
    position: absolute;
    top: 43%;
    transform: translateY(-50%);
    cursor: pointer;
}

.Tomato {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 343px;
    height: 153px;
    margin: 0 0 35px 0;
}
.Wrap {
    display: flex;
    position: relative;
    align-items: center;
    justify-content: center;
    flex-direction: column;
    width: 100%;
    height: auto;
    cursor: pointer;
}
  • p์˜ position: absolute;๋ฅผ ํ†ตํ•ด ๋ถ€๋ชจ ์š”์†Œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ ˆ๋Œ€ ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜๊ณ , ๋ฒ„ํŠผ ์ „์ฒด ์ปจํ…Œ์ด๋„ˆ์˜ Wrap ํด๋ž˜์Šค์˜ position: relative;๋ฅผ ํ†ตํ•ด ์ž์‹ ์š”์†Œ์˜ absolute ์œ„์น˜๋ฅผ ์กฐ์ •ํ•  ๊ธฐ์ค€์ด ๋œ๋‹ค.

  ์ดํ›„ Pomodoro ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค.

Pomodoro ์ปดํฌ๋„ŒํŠธ

// Pomodoro.js

import { useNavigate } from "react-router-dom";
import Background from "../BackGround/Background";
import TomatoBtn from "../TomatoBtn/TomatoBtn";
import ClockPomo from "./ClockPomo";
import "./Pomodoro.css";
import pic1 from "../Pic/TomatoClock.png";

export default function Pomodoro() {
    const navigate = useNavigate();
    const goToStudy = () => {
        navigate('/study');
    }
    const goToTodo = () => {
        navigate('/todolist');
    }
    const goToSetting = () => {
        navigate('/setting');
    }

    return (
        <>
        <Background title='POMODORO' />
        <div className="main">
            <>
                <img src={pic1} alt='picture1' className="img1"/>
                <ClockPomo />
            </>
            <div className="Root">
                <TomatoBtn name='START' onClick={goToStudy} />
                <TomatoBtn name='TODO' onClick={goToTodo} />
                <TomatoBtn name='SETTING' onClick={goToSetting} />
            </div>
        </div>
        </>
    );
}
  • useNavigate()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ navigate ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ๊ฐ ๊ฒฝ๋กœ๋กœ ์ด๋™ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•œ๋‹ค.  
  • ๊ฐ ํŽ˜์ด์ง€๋กœ ์ด๋™ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•˜์—ฌ TomatoBtn์˜ onClick์œผ๋กœ ์ „๋‹ฌํ–ˆ๋‹ค. 
// Pomdoro.css

.Root {
    flex-direction: column;
    margin: 0 0 0 100px;
}
.main {
    display: flex;
}
.img1 {
    width: 687.8px;
    height: 519px;
}