React

[React] useState 로 좋아요 버튼 상태 관리하기

teamnova 2025. 5. 28. 14:16
728x90

 

 

안녕하세요 오늘은 지난 시간에 만들었던 슬라이드 UI 카드에 좋아요 버튼을 구현하고자 합니다. 

 

지난 게시글은 아래 링크에서 확인하실 수 있습니다. 

 

[React] 슬라이드 카드 UI 만들기

안녕하세요. 오늘은 리액트를 사용해 슬라이드 카드 UI를 만들어보겠습니다. 슬라이드 카드는 이미지, 텍스트 등을 한 화면에서 넘기며 보여줄 수 있는 방식으로, 포트폴리오, 프로젝트 소개, 리

stickode.tistory.com

 

 

1. 먼저 좋아요 버튼을 "컴포넌트" 디렉토리에 만들어줍니다. 

components 폴더가 없다면 만들어줍니다 

경로: src/components/LikeButton.js 

import { useState } from "react";

function LikeButton() {
  const [liked, setLiked] = useState(false);

  const toggleLike = () => setLiked(!liked);

  return (
    <button
      onClick={toggleLike}
      style={{
        position: "absolute",
        top: "20px",
        right: "20px",
        fontSize: "24px",
        background: "none",
        border: "none",
        cursor: "pointer",
        color: liked ? "red" : "white",
        zIndex: 5,
        transition: "color 0.3s ease",
      }}
    >
      {liked ? "❤️" : "🤍"}
    </button>
  );
}

export default LikeButton;

 

 

 

 

2. App.js 에서 방금 만든 좋아요 버튼을 import 해준 뒤 적당한 위치에 넣어줍니다 

 

import 

import LikeButton from "./components/LikeButton";

 

 

<LikeButton /> 태그 삽입 

   <Routes>
        <Route
          path="/"
          element={
            <div style={{ padding: "50px", textAlign: "center" }}>
              <div className="slider-wrapper">
                <Slider {...settings}>
                  {cardData.map((card, index) => (
                    <div key={index} className="slide">
                      <div
                        className="slide-background"
                        style={{
                          backgroundImage: `url(${card.image})`,
                          height: "400px",
                          backgroundSize: "cover",
                          backgroundPosition: "center",
                          position: "relative",
                          borderRadius: "20px",
                          overflow: "hidden",
                        }}
                      >
                        <div
                          className="text-box"
                          style={{
                            position: "absolute",
                            top: "50%",
                            left: "50%",
                            transform: "translate(-50%, -50%)",
                            backgroundColor: "rgba(0,0,0,0.6)",
                            padding: "30px",
                            width: "80%",
                            maxWidth: "600px",
                            minWidth: "250px",
                            borderRadius: "20px",
                            textAlign: "center",
                            color: "white",
                          }}
                        >
                          <LikeButton
                            liked={likes[index]}
                            onToggle={() => toggleLike(index)}
                          />
                          <h2>{card.title}</h2>
                          <p>{card.description}</p>
                        </div>
                      </div>
                    </div>
                  ))}
                </Slider>
              </div>
            </div>

 

 

슬라이드 카드 내부에 좋아요 하트가 위치하도록 했습니다. 

 

 

3. App. js 에서 카드별 상태 만들기 

위에서 간단하게 좋아요 버튼 (하트) 를 만들어보았다면, 이제 좋아요 상태를 관리해보도록 하겠습니다. 

 

 

먼저, App.js에서 카드별 상태를 만들어줍니다. (function App 내부에 아래 내용을 추가해줍니다)

function App() {
  const [likes, setLikes] = useState(Array(cardData.length).fill(false));

  const toggleLike = (index) => {
    const updatedLikes = [...likes];
    updatedLikes[index] = !updatedLikes[index];
    setLikes(updatedLikes);
  };

 

 

아까 작성했던 <LikeButton /> 태그에 좋아요 상태를 전달해줍니다 

   <LikeButton
        liked={likes[index]}
        onToggle={() => toggleLike(index)}
      />

 

단순히 버튼을 누름으로써 하트 색상만 변경되는 것이 아니라, 이를 나의 "상태"로 보고 좋아요 여부 정보를 전달합니다. 

 

 

LikeButton 코드도 수정해줍니다. 

전달 받은 좋아요 상태, 토글 상태를 props 로 받습니다. 

function LikeButton({ liked, onToggle })  {
  return (
    <button
      onClick={onToggle} 
      style={{
        position: "absolute",
        top: "20px",
        right: "20px",
        fontSize: "24px",
        background: "none",
        border: "none",
        cursor: "pointer",
        color: liked ? "red" : "white",
        zIndex: 5,
        transition: "color 0.3s ease",
      }}
    >
      {liked ? "❤️" : "🤍"}
    </button>
  );
}

export default LikeButton;

 

좋아요 상태관리는 App.js 에서 전부 관리하고 LikeButton 은 전달 받은 대로 ui 를 생성하는 역할을 하기 때문에 상태 관리를 위한 useState 를 사용할 필요가 없습니다. 

App. js 로부터 liked, onToggle 를 전달 받아 ui 로 띄워줍니다. 

 

 

최종적인 전체 코드 입니다 

 

1. App.js 

import "./App.css";
import "slick-carousel/slick/slick.css";
import "slick-carousel/slick/slick-theme.css";
import Slider from "react-slick";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import About from "./pages/about.js";
import Experience from "./pages/experiences.js";
import Projects from "./pages/projects.js";
import LikeButton from "./components/LikeButton.js";
import { useState } from "react";

// 슬라이드 카드 데이터
const cardData = [
  {
    title: "1. Wall Sina",
    description:
      "The innermost wall, where the royal family and nobles reside.",
    image: "https://source.unsplash.com/random/800x400?city",
  },
  {
    title: "2. Wall Rose",
    description: "The middle wall, home to many of the general populace.",
    image: "https://source.unsplash.com/random/800x400?nature",
  },
  {
    title: "3. Wall Maria",
    description:
      "The outermost and largest wall, the first to fall during the Titans' initial attack.",
    image: "https://source.unsplash.com/random/800x400?space",
  },
];

// 커스텀 화살표
function PrevArrow(props) {
  return (
    <button
      onClick={props.onClick}
      style={{
        position: "absolute",
        left: "-60px",
        top: "50%",
        transform: "translateY(-50%)",
        zIndex: 10,
        padding: "12px 18px",
        fontSize: "20px",
        borderRadius: "50%",
        border: "none",
        backgroundColor: "rgba(0, 0, 0, 0.6)",
        color: "white",
        cursor: "pointer",
      }}
    >
      ←
    </button>
  );
}

function NextArrow(props) {
  return (
    <button
      onClick={props.onClick}
      style={{
        position: "absolute",
        right: "-60px",
        top: "50%",
        transform: "translateY(-50%)",
        zIndex: 10,
        padding: "12px 18px",
        fontSize: "20px",
        borderRadius: "50%",
        border: "none",
        backgroundColor: "rgba(0, 0, 0, 0.6)",
        color: "white",
        cursor: "pointer",
      }}
    >
      →
    </button>
  );
}

// App 컴포넌트
function App() {
  const settings = {
    dots: true,
    infinite: true,
    speed: 500,
    slidesToShow: 1,
    slidesToScroll: 1,
    nextArrow: <NextArrow />,
    prevArrow: <PrevArrow />,
  };

  // 메뉴바
  const headerStyle = {
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    padding: "20px 40px",
    backgroundColor: "#0d1b2a",
    color: "white",
  };

  // 화살표
  const navStyle = {
    display: "flex",
    gap: "20px",
    alignItems: "center",
  };

  // 좋아욧
  const [likes, setLikes] = useState(Array(cardData.length).fill(false));

  const toggleLike = (index) => {
    const updatedLikes = [...likes];
    updatedLikes[index] = !updatedLikes[index];
    setLikes(updatedLikes);
  };

  return (
    <Router>
      <header style={headerStyle}>
        <div style={{ fontWeight: "bold", fontSize: "20px" }}>
          <Link to="/" style={{ color: "white", textDecoration: "none" }}>
            Hiii It's mee !
          </Link>
        </div>
        <nav style={navStyle}>
          <Link to="/" style={{ color: "white", textDecoration: "none" }}>
            Home
          </Link>
          <Link to="/about" style={{ color: "white", textDecoration: "none" }}>
            About
          </Link>
          <Link
            to="/experience"
            style={{ color: "white", textDecoration: "none" }}
          >
            Experience
          </Link>
          <Link
            to="/projects"
            style={{ color: "white", textDecoration: "none" }}
          >
            Projects
          </Link>
        </nav>
      </header>

      <Routes>
        <Route
          path="/"
          element={
            <div style={{ padding: "50px", textAlign: "center" }}>
              <div className="slider-wrapper">
                <Slider {...settings}>
                  {cardData.map((card, index) => (
                    <div key={index} className="slide">
                      <div
                        className="slide-background"
                        style={{
                          backgroundImage: `url(${card.image})`,
                          height: "400px",
                          backgroundSize: "cover",
                          backgroundPosition: "center",
                          position: "relative",
                          borderRadius: "20px",
                          overflow: "hidden",
                        }}
                      >
                        <div
                          className="text-box"
                          style={{
                            position: "absolute",
                            top: "50%",
                            left: "50%",
                            transform: "translate(-50%, -50%)",
                            backgroundColor: "rgba(0,0,0,0.6)",
                            padding: "30px",
                            width: "80%",
                            maxWidth: "600px",
                            minWidth: "250px",
                            borderRadius: "20px",
                            textAlign: "center",
                            color: "white",
                          }}
                        >
                          <LikeButton
                            liked={likes[index]}
                            onToggle={() => toggleLike(index)}
                          />
                          <h2>{card.title}</h2>
                          <p>{card.description}</p>
                        </div>
                      </div>
                    </div>
                  ))}
                </Slider>
              </div>
            </div>
          }
        />
        <Route path="/about" element={<About />} />
        <Route path="/experience" element={<Experience />} />
        <Route path="/projects" element={<Projects />} />
      </Routes>
    </Router>
  );
}

export default App;

 

 

 

2. LikeButton.js 

function LikeButton({ liked, onToggle })  {
  return (
    <button
      onClick={onToggle} 
      style={{
        position: "absolute",
        top: "20px",
        right: "20px",
        fontSize: "24px",
        background: "none",
        border: "none",
        cursor: "pointer",
        color: liked ? "red" : "white",
        zIndex: 5,
        transition: "color 0.3s ease",
      }}
    >
      {liked ? "❤️" : "🤍"}
    </button>
  );
}

export default LikeButton;

 

 

다음 시간에는 로컬 스토리지를 활용해 좋아요 상태를 저장해보도록 하겠습니다.

감사합니다 

 

 

시연영상입니다.