본문 바로가기

Project Log/personal website

DarkTheme(다크모드) 적용하기 1편 (feat.useContext)

이번에 개인 웹사이트를 개편하면서 dark-theme도 한 번 적용해보려고 한다. 별 다른 이유는 없고 그냥 깐지나보여서ㅎㅎ

light/dark모드를 오가기 위해서 먼저 toggle button이 필요하다.

 

Toggle이라는 이름의 컴포넌트를 만들까 했지만 토글 버튼을 사용할 경우가 theme switching 해주는 경우 말고는 없는 것 같아 그냥 아예 ThemeToggle이라는 이름으로 컴포넌트를 만들었다.


import React, { useState } from 'react';
import styles from './ThemeToggle.scss';

const ThemeToggle = () => {
  const [lightMode, setLightMode] = useState(true);
  const [darkMode, setDarkMode] = useState(false);

  const handleSwitchToggle = () => {
    if (!lightMode) {
      setLightMode(true);
      setDarkMode(false);
    } else {
      setLightMode(false);
      setDarkMode(true);
    }
  };

  return (
    <button
      className={lightMode ? 'theme-toggle light' : 'theme-toggle dark'}
      onClick={handleSwitchToggle}
    >
      <div className='toggle-ball'}></div>
    </button>
  );
};

export default ThemeToggle;

처음에는 정말 간단하게 버튼이 토글될때마다 ThemeToggle컴포넌트 안에서 state가 lightModedarkMode로 번갈아 가면서 바뀌로도록 만들어보았다.

 

그런데 토글될때마다 바뀌는 theme에 대해서 다른 컴포넌트도 알아야 하는데 어떡함??

 

그래서 완전 상위 컴포넌트에서 theme state를 관리하고 이를 내려주는 방식으로 할까 하다가 그러면 하위 컴포넌트마다 theme state를 prop으로 넘겨주고 받아서 다시 넘겨주고 받아서 다시 넘겨주고 X반복반복 해야해서 넘나뤼 귀찮은 것이다. 그래서 useContext를 사용하기로하였다.

 

먼저 src/context/theme.js 이라는 파일을 만들어 주고 아래와 같이 context를 작성한다.

import React from 'react';
import { createContext, useState } from 'react';

const ThemeContext = createContext();

const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider
      value={{ theme, toggleTheme: () => setTheme(theme === 'light' ? 'dark' : 'light') }}
    >
      {children}
    </ThemeContext.Provider>
  );
};

export { ThemeProvider };
export default ThemeContext;

아까의 ThemeToggle 컴포넌트에서 처럼 lightModedarkMode라는 state를 두개나 만들지 않고 그냥 theme이라는 state 하나에 'light'과 'dark'라는 string으로 구분해주기로 했다.

 

기본 값은 'light'으로 넣어주고 toggleTheme이라는 함수에는 기존 theme값이 'light'이면 'dark'로, 'light'이 아니라면 'light'으로 변경시켜주도록 작성했다.

 

여기서 export한 ThemeProvider로 최상위 컴포넌트를 감싸면, 하위 컴포넌트들은 useContext hook을 사용해서 아주 간편하게 ThemeContext.Providervalue 값으로 넣어준 아래와 같은 object 값을 받아서 사용할 수 있다.

{theme: 'light', 
 toggleTheme: () => setTheme(theme === 'light' ? 'dark' : 'light')}

최상위 컴포넌트인 App 컴포넌트 안에서 ThemeProvider로 감싸주었다.

import React from 'react';
import Template from 'components/Template';
import { ThemeProvider } from '../context/theme';

const App = () => {
  return (
    <ThemeProvider>
      <Template />
    </ThemeProvider>
  );
};

export default App;

이렇게 되면 Template부터 하위 컴포넌트들은 useContext를 사용해서 아주 간편하게 themeContext안에서 정의한 theme값과 toggleTheme함수를 사용할 수 있다.

 

아까의 ThemeToggle컴포넌트로 가서 useContext를 사용해보자!

import React, { useState, useContext } from 'react';
import styles from './ThemeToggle.scss';
import classNames from 'classnames/bind';
import ThemeContext from 'context/theme';

const cx = classNames.bind(styles);

const ThemeToggle = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <button className={cx('theme-toggle', theme)} onClick={() => toggleTheme()}>
      <div className='toggle-ball'></div>
    </button>
  );
};

export default ThemeToggle;

코드도 훨신 깔끔해졌고, 이제 다른 하위 컴포넌트들에서도 위의 코드처럼 useContext hook을 이용해서 아주 쉽게 현재 theme이 'light'인지 'dark'인지 알 수 있게 되었다.

 

다음편은 sass 문법을 이용해 'light'과 'dark'를 클래스명 prefix로 사용해서 각 모드에서 사용할 색상과 스타일 값을 변수로 지정해주고, 모드에 따라 스타일이 바뀔 수 있도록 지정해보겠다.

 

토글토글

'Project Log > personal website' 카테고리의 다른 글

Table of Content 컴포넌트 만들기 - useContext  (0) 2019.10.01