인턴

React와 styled-components로 만드는 커스텀 토글 버튼

choijming21 2025. 3. 25. 18:42

 안녕하세요! 오늘은 React와 styled-components를 사용하여 직접 만든 커스텀 토글 버튼 구현 방법에 대해 공유하려고 합니다. 단순히 켜고 끄는 기능뿐만 아니라, 상태에 따라 "ON"과 "OFF" 텍스트도 변경되는 토글 버튼을 만들어보겠습니다.

 

 

 

⭐ 최종 결과물 ⭐ 

  1. 원형 핸들이 왼쪽/오른쪽으로 움직입니다.
  2. 상태에 따라 배경색이 변경됩니다.
  3. 상태에 따라 "ON"/"OFF" 텍스트가 표시됩니다.
  4. 상태에 따라 텍스트 색상이 변경됩니다.

토그버튼 실행

 

 

 

 

 

 

코드 구현

1. 컴포넌트 구조 (ToggleButton.tsx)

import React, { useState } from "react";
import * as S from "./toggleButton.style";

const ToggleButton = () => {
  // 토글 상태를 관리하는 state
  const [isChecked, setIsChecked] = useState(false);

  // 토글 상태 변경 핸들러
  const handleChange = () => {
    setIsChecked(!isChecked);
  };

  return (
    <div>
      {/* 실제 체크박스 (화면에 보이지 않음) */}
      <input
        type="checkbox"
        id="chk1"
        checked={isChecked}
        onChange={handleChange}
        style={{ display: "none" }}
      />
      {/* 커스텀 토글 버튼 UI */}
      <S.ToggleContainer htmlFor="chk1" checked={isChecked}></S.ToggleContainer>
    </div>
  );
};

export default ToggleButton;

 

 

2. 스타일 구현 (toggleButton.style.ts)

import styled from "styled-components";

// TypeScript 타입 정의
type ToggleTypes = {
  checked?: boolean;
};

export const ToggleContainer = styled.label<ToggleTypes>`
  display: block;
  position: relative;
  width: 100px;
  height: 45px;
  background-color: ${(props) =>
    props.checked ? "rgba(109, 104, 107)" : "#d3d3d3"};
  border-radius: 60px;
  transition: background 0.4s;

  &::after {
    content: "";
    position: absolute;
    left: ${(props) => (props.checked ? "57.5px" : "2.5px")};
    top: 22px;
    width: 40px;
    height: 40px;
    border-radius: 100%;
    background-color: #fff;
    transform: translateY(-50%);
    box-shadow: 1px 3px 4px rgba(0, 0, 0, 0.1);
    transition: all 0.4s;
  }

  &::before {
    content: ${(props) => (props.checked ? '"ON"' : '"OFF"')};
    color: ${(props) => (props.checked ? "black" : "white")};
    font-size: 24px;
    font-family: Arial, Helvetica, sans-serif;
    position: absolute;
    left: ${(props) => (props.checked ? "15px" : "45px")};
    top: 50%;
    transform: translateY(-50%);
    transition: all 0.4s;
  }
`;

 

 

 

 

 

 

 

 

코드 상세 설명

  • 상태 관리: useState 훅을 사용하여 토글 버튼의 상태(ON / OFF)를 관리합니다.
const [isChecked, setIsChecked] = useState(false);
  • 상태 변경 핸들러: 토글 버튼 클릭 시 상태를 변경하는 함수입니다.
const handleChange = () => {
  setIsChecked(!isChecked);
};
  • 숨겨진 체크박스: 실제 체크박스는 화면에 보이지 않지만, 접근성과 폼 제출 시 값 전달을 위해 존재합니다.
<input
  type="checkbox"
  id="chk1"
  checked={isChecked}
  onChange={handleChange}
  style={{ display: "none" }}
/>
  • 커스텀 토글 UI: styled-components로 스타일링 된 라벨 컴포넌트로, 체크 박스와 연결되어 있습니다.
<S.ToggleContainer htmlFor="chk1" checked={isChecked}></S.ToggleContainer>

 

 

 

 

 

스타일 부분

  • TypeScript 타입 정의
type ToggleTypes = {
  checked?: boolean;
};
  • 토글 컨테이너 기본 스타일
export const ToggleContainer = styled.label<ToggleTypes>`
  display: block;  // 블록 레벨 요소로 만들어 너비/높이 설정 가능
  position: relative;  // 내부 가상 요소의 기준점
  width: 100px;  // 토글 버튼 너비
  height: 45px;  // 토글 버튼 높이
  background-color: ${(props) =>
    props.checked ? "rgba(109, 104, 107)" : "#d3d3d3"};  // 상태에 따른 배경색 변경
  border-radius: 60px;  // 둥근 모서리
  transition: background 0.4s;  // 배경색 전환 애니메이션
  • 토글 핸들(::after 가상 요소)
&::after {
  content: "";  // 빈 콘텐츠 (시각적 요소만 필요)
  position: absolute;  // 컨테이너 내에서 절대 위치
  left: ${(props) => (props.checked ? "57.5px" : "2.5px")};  // 상태에 따라 위치 이동
  top: 22px;  // 상단에서부터의 거리
  width: 40px;  // 핸들 너비
  height: 40px;  // 핸들 높이
  border-radius: 100%;  // 원형 모양
  background-color: #fff;  // 흰색 배경
  transform: translateY(-50%);  // 수직 중앙 정렬
  box-shadow: 1px 3px 4px rgba(0, 0, 0, 0.1);  // 그림자 효과
  transition: all 0.4s;  // 모든 변화에 애니메이션 적용
}
  • 텍스트 표시(::before 가상 요소)
&::before {
  content: ${(props) => (props.checked ? '"ON"' : '"OFF"')};  // 상태에 따른 텍스트
  color: ${(props) => (props.checked ? "black" : "white")};  // 상태에 따른 텍스트 색상
  font-size: 24px;  // 글꼴 크기
  font-family: Arial, Helvetica, sans-serif;  // 글꼴 패밀리
  position: absolute;  // 컨테이너 내에서 절대 위치
  left: ${(props) => (props.checked ? "15px" : "45px")};  // 상태에 따라 위치 이동
  top: 50%;  // 상단에서 50% 위치
  transform: translateY(-50%);  // 수직 중앙 정렬
  transition: all 0.4s;  // 모든 변화에 애니메이션 적용
}

 

 

 

 

 

 

 

핵심 포인트 해설

  • 가상 요소 활용: 가상요소를 사용하면 HTML 마크업을 최소화하면서도 복잡한 UI를 구현할 수 있습니다.
    • 이 토글 버튼의 핵심은 CSS의 ::before ::after 가상 요소를 활용한 것입니다.
      • ::after: 토글 버튼의 동그란 핸들(손잡이) 부분
      • ::befor: ON/OFF 텍스트 표시 부분
  • props를 통한 조건부 스타일링
    • styled-components의 강력한 기능 중 하나는 props를 기반으로 스타일링을 동적으로 변경할 수 있다는 점입니다.
      • 배경색
      • 핸들 위치
      • 텍스트 내용
      • 텍스트 위치
      • 텍스트 색상
background-color: ${(props) =>
  props.checked ? "rgba(109, 104, 107)" : "#d3d3d3"};

 

  • 트렌지션 효과
    • transition 속서을 사용하여 상태 변경 시 부드러운 애니메이션 효과를 적용했습니다.
transition: background 0.4s;  // 배경색만 변경
transition: all 0.4s;  // 모든 속성 변경

 

  • 접근성 고려
    • 실제 체크박스를 숨기고 커스텀 UI를 사용하더라도,<label> 요소의 htmlFor 속성을 통해 체크박스와 연결했습니다. 이렇게 하면 토글 버튼을 클릭했을 때, 체크박스의 상태가 변경되어, 스크린 리더와 같은 보조 기술에서도 올바르게 인식할 수 있습니다.