안녕하세요! 오늘은 React와 styled-components를 사용하여 직접 만든 커스텀 토글 버튼 구현 방법에 대해 공유하려고 합니다. 단순히 켜고 끄는 기능뿐만 아니라, 상태에 따라 "ON"과 "OFF" 텍스트도 변경되는 토글 버튼을 만들어보겠습니다.
⭐ 최종 결과물 ⭐
- 원형 핸들이 왼쪽/오른쪽으로 움직입니다.
- 상태에 따라 배경색이 변경됩니다.
- 상태에 따라 "ON"/"OFF" 텍스트가 표시됩니다.
- 상태에 따라 텍스트 색상이 변경됩니다.
코드 구현
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 텍스트 표시 부분
- 이 토글 버튼의 핵심은 CSS의 ::before 와 ::after 가상 요소를 활용한 것입니다.
- props를 통한 조건부 스타일링
- styled-components의 강력한 기능 중 하나는 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 속성을 통해 체크박스와 연결했습니다. 이렇게 하면 토글 버튼을 클릭했을 때, 체크박스의 상태가 변경되어, 스크린 리더와 같은 보조 기술에서도 올바르게 인식할 수 있습니다.
'인턴' 카테고리의 다른 글
Next.js에서 서버와 클라이언트 간 모바일 감지 연동하기 (0) | 2025.03.27 |
---|---|
Next.js 프로젝트의 효율적인 폴더 구조 개선하기 📂 (0) | 2025.03.26 |
useMemo와 useCallback (0) | 2025.03.25 |
Jotai (0) | 2025.03.25 |
[ Troubleshooting🛠️ ] GNB 자동 스크롤과 페이지 이동 기능 통합 (0) | 2025.03.25 |