Project Shall-We-Health #4-2 다크 모드/라이트 모드 구현

시작하며

SR에서 기획했던 Bare Minimum과 Advanced 단계의 목표 중 내가 맡은 파트를 끝내서 Nightmare 중 다크 모드 구현을 하게 되었다. 다른 목표들은 서버의 구현이 필요하거나 데이터 베이스의 스키마를 수정해야해서 클라이언트에서 구현이 가능한 다크모드를 우선 구현하였다.

라이트 모드/다크 모드 여부 저장

유저가 다크모드로 변경하면 추후 페이지에 접속하거나 새로고침을 해도 유지가 되어야 하기 때문에 다크모드여부를 따로 저장할 수 있는 공간이 필요했다. state나 redux로 관리하면 새로고침 될때마다 초기화되기 때문에 부적합하고 처음에는 쿠키로 저장해놓을까 고민했었는데, 여러 포스팅을 찾아보면서 localStorage를 사용하는 것이 적절하다는 것을 알게 되었다.

//useTheme.js
import { useState } from 'react';

const useTheme = () => {
  const prefersColorScheme = window.matchMedia('(prefers-color-scheme: dark)').matches
  ? 'dark'
  : 'light';
  const localTheme = localStorage.getItem('theme');
  const initialTheme = localTheme || prefersColorScheme;
  const [theme, setTheme] = useState(initialTheme);
  const setMode = (mode) => {
    localStorage.setItem('theme', mode);
    setTheme(mode);
  };
  const themeToggler = () => {
    theme === 'light' ? setMode('dark') : setMode('light');
  };
  return [theme, themeToggler];
};

export default useTheme;

처음에는 라이트/다크 모드 여부만 localStorage에 저장하고 처음 접속한 경우 라이트 모드가 적용되도록 설정하려고 했었다. 관련된 포스트를 찾아보던 중 prefers-color-schemewindow.matchMedia를 사용하여 유저의 테마를 적용할 수 있는 방법이 잘 정리되어 있는 글을 발견하여 같은 방법을 사용하였다.

참고 포스팅 : Gatsby Blog 다크 모드 적용기

테마 버튼 구현

function App() {

  const [theme, themeToggler] = useTheme();
  
  ...
  
  return (

    <div className='App' data-theme={theme}>
    
      ...
  
      {theme==='light' ? (
        <div className='btn-theme dark' onClick={()=>{themeToggler()}}><FontAwesomeIcon className='icon-theme' icon={faMoon} /><div>다크 모드로 보기</div></div>
      ) : (
        <div className='btn-theme light' onClick={()=>{themeToggler()}}><FontAwesomeIcon className='icon-theme' icon={faSun} /><div>라이트 모드로 보기</div></div>
      )}
    </div>
  );
}

export default App;

App 컴포넌트에 position:fixed인 버튼을 생성하고 useTheme의 theme에 따라 보여지는 버튼이 다르게 설정하고 버튼 클릭시 themeToggler()이 실행되어 localStorage의 theme 값이 변경되도록 설정해주었다.

최상단 태그의 속성에 data-theme={theme}를 추가해주어 theme에 따라 적용되는 CSS가 달라질 수 있도록 설정하였다.

CSS 변수를 사용한 색상 적용

다크모드에 따라 CSS의 color 값이 변경되는 방법이 다양했는데 보통 styled-components를 사용한 방법이 대다수였다. 다크모드 구현을 위해서 웹페이지 전체에 styled-components를 사용하는 방식으로 변경하는 것은 무리여서 다른 방법을 찾던 중 CSS도 변수를 설정할 수 있다는 것을 알게 되었다.

/*APP.css*/
:root {
  --main-color1: #39CFD9;
  --main-color2: rgb(172, 172, 172);
  --btn-color1: rgb(100, 100, 100);
  --btn-color2: #FFF;
  --nav-color: rgb(211, 211, 211);
  --main-backcolor1: #fafafa;
  --main-backcolor2: #f4f4f49d;
  --main-backcolor3: white;
  --main-backcolor4: #656565;
  --font-color1: black;
  --font-color2: #535353;
  --font-color3: rgb(56, 56, 56);
  --shadow-color: rgb(187, 187, 187);
}

[data-theme="dark"] {
  --main-color1: #367c81;
  --main-color2: rgb(88, 86, 86);
  --btn-color1: rgb(240, 240, 240);
  --btn-color2: rgb(54, 54, 54);
  --nav-color: rgb(93, 93, 93);
  --main-backcolor1: #202020;
  --main-backcolor2: #202020;
  --main-backcolor3: rgb(93, 93, 93);
  --main-backcolor4: #313131;
  --font-color1: white;
  --font-color2: #eeeeee;
  --font-color3: rgb(233, 233, 233);
  --shadow-color: rgb(0, 0, 0);
}

.App {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: Apple SD Gothic Neo,arial,sans-serif;
  overflow-x: hidden;
  background-color: var(--main-backcolor1);
  color: var(--font-color1);
}

...

--로 변수명을 설정해주고 background-colorcolor 값을 var(--변수명)으로 설정해주면 해당하는 색상으로 적용이 가능하다. 모든 페이지에서 따로 설정할 필요없이 색상만 설정해주고 CSS 변수만 사용하면 구현이 가능하다.

구현 화면

마치며

이전 프로젝트부터 구현해보고 싶었던 기능인데 이번에 경험할 수 있어서 좋은 기회였다. 처음에 다크모드 구현을 생각하지 않고 메인 컬러를 제외하고 각자 color 값을 설정해서 통일시키는데 시간을 썼던것 같다. 다음 프로젝트에서는 SR 단계에서 좀 더 세부적으로 color 값을 정하고 개발을 진행하는 것도 좋을 것 같다.

좋은 웹페이지 즐겨찾기