리액트/예제

usecontext로 다크 모드 구현

lamarcK 2025. 4. 10. 06:32

다크 모드 구현을 위한 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; /* 부드러운 전환 효과 */
}