다크 모드 구현을 위한 ThemeProvider
1. 초기 상태 설정 (State Initialization)
const [isDarkMode, setIsDarkMode] = useState(() => {
return localStorage.getItem('theme') === 'dark';
});
2. 테마 변경 효과 적용 (Theme Effect)
useEffect(() => {
document.documentElement.toggleAttribute('dark', isDarkMode);
}, [isDarkMode]);
- isDarkMode 상태가 변경될 때마다 실행
- HTML root 요소(documentElement)에 'dark' 속성을 토글
- CSS에서 이 속성을 기반으로 스타일 변경 가능
3. 테마 토글 함수 및 Context 제공 (Toggle & Provider)
const toggleTheme = () => {
setIsDarkMode(prev => {
const newTheme = !prev;
localStorage.setItem('theme', newTheme ? 'dark' : 'light');
return newTheme;
});
};
return (
<ThemeContext.Provider value={{ isDarkMode, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
- 테마 전환 함수 정의
- 상태 변경 및 localStorage 업데이트
- Context를 통해 하위 컴포넌트에 상태와 함수 제공
//다크모드 테마 관리
export function ThemeProvider({ children }) {
// localStorage에서 테마 설정을 불러와 다크모드 상태 초기화
//지연초기화 후 리턴 값 반영 true/false
const [isDarkMode, setIsDarkMode] = useState(() => {
return localStorage.getItem('theme') === 'dark';
});
useEffect(() => {
document.documentElement.toggleAttribute('dark', isDarkMode);
}, [isDarkMode]);
// 테마 변경 시 localStorage 저장
const toggleTheme = () => {
setIsDarkMode(prev => {
const newTheme = !prev;
localStorage.setItem('theme', newTheme ? 'dark' : 'light');
return newTheme;
});
};
return (
<ThemeContext.Provider value={{ isDarkMode, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
1. Context 연결 및 토글 함수 가져오기
const { toggleTheme } = useContext(ThemeContext);
- ThemeContext에서 toggleTheme 함수만 가져옴
- 다크모드 전환을 위한 함수를 컴포넌트에서 사용 가능하게 함
2.메뉴 아이템 설정
const menuItems = [
// ...
{ icon: '🌓', text: '다크/라이트 테마' },
// ...
];
- 다크모드 전환을 위한 메뉴 아이템 포함
- 아이콘과 텍스트로 구성
3.클릭 핸들러에서 토글 기능 구현
const handleMenuClick = (text) => {
switch(text) {
case '다크/라이트 테마':
toggleTheme(); // 테마 전환 실행
break;
// ...
}
setIsOpen(false); // 메뉴 닫기
};
- 다크/라이트 테마 메뉴 클릭 시 toggleTheme 함수 실행
- 테마 전환 후 드롭다운 메뉴 자동 닫힘
const DropdownMenu = ({isOpen, setIsOpen}) => {
const menuRef = useRef();
//다크모드 연결
const { toggleTheme } = useContext(ThemeContext);
// 메뉴 외부 클릭 시 닫히도록 처리
useEffect(() => {
const handleClickOutside = (event) => {
if (menuRef.current && !menuRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [setIsOpen]);
const menuItems = [
{ icon: '👤', text: 'Google 계정' },
{ icon: '🚪', text: '로그아웃' },
{ icon: '🎬', text: 'YouTube 스튜디오' },
{ icon: '💳', text: '구매 항목 및 멤버십' },
{ icon: '📊', text: 'YouTube의 내 데이터' },
{ icon: '🌓', text: '다크/라이트 테마' },
{ icon: '🌐', text: '언어: 한국어' },
{ icon: '⚙️', text: '설정' },
{ icon: '❓', text: '고객센터' },
];
//버튼 기능 추가
const handleMenuClick = (text) => {
switch(text) {
case '다크/라이트 테마':
toggleTheme(); // 직접 toggleTheme 사용
break;
case 'Google 계정':
console.log('Google 계정으로 이동');
break;
// ... 다른 케이스들
default:
console.log(`${text} 메뉴 클릭됨`);
}
setIsOpen(false);
};
return (
<div className="dropdown-container" ref={menuRef}>
<button
className="dropdown-button"
onClick={() => setIsOpen(!isOpen)}
>
🐱 {/* 고양이 아이콘 */}
</button>
{isOpen && (
<div className="dropdown-menu">
{menuItems.map((item, index) => (
<div key={index} className="menu-item"
onClick={() => handleMenuClick(item.text)}
>
<span className="menu-icon">{item.icon}</span>
<span className="menu-text">{item.text}</span>
</div>
))}
</div>
)}
</div>
);
};
css에서 변수로 색상을 선택하고 html의 상태가 [dark]가 될경우 다르게 적용되도록 동일한 변수 이름을 다른 스타일의 색상으로 적용
:root {
/* 라이트 모드 기본값 */
--b-color1: white;
--menu-item :white;
--t-color1: black;
--t-color2: #606060;
--svg-color: black;
--menu-item-hover :#f2f2f2;
}
html[dark] {
--b-color1: black;
--b-color2:#333333;
--t-color1: white;
--t-color2:#aaaaaa;
--svg-color:white;
--menu-item :#282828;
--menu-item-hover : #3e3e3e;
}
.menu-item:hover {
background-color: var(--menu-item-hover);
}
/* body 기본 스타일 */
body {
background-color: var(--b-color1);
color: var(--t-color1);
margin: 0;
padding: 0;
transition: background-color 0.3s, color 0.3s; /* 부드러운 전환 효과 */
}
'리액트 > 예제' 카테고리의 다른 글
영화 목록 가져오기 사이트만들기(타입스크립트) (0) | 2025.04.11 |
---|---|
리액트 앱에서 에러 처리 및 예외 처리 구현하기 (0) | 2025.04.10 |
컴포넌트 재사용성 - 유튜브 아이템 추가 (0) | 2025.04.09 |
useEffect 예제 - 랜덤 강아지 이미지(무한 새로고침) (0) | 2025.04.09 |
await/async 예제 - 랜덤 강아지 이미지 (0) | 2025.04.08 |