woolta

styled-components로 다크모드(darkMode) 만들기

wooltaUserImgb00032 | React | 2020-01-19

이번 포스팅에서는 styled-components 를 사용하여 간편하고 코드의 변화량이 거의 없이 다음과 같이 darkMode를 추가하는 작업을 진행하도록 하겠습니다.

https://image.woolta.com/theme.gif

ThemeProvider

ThemeProviderstyled-components 에서 지원 하는 함수로 redux 처럼 ThemeProvider 로 감싼 내부의 컴포넌트들에게 공통된 global 값을 내려줄 수 있습니다. 우선 아래 예제를 따라가며 배워보도록 하겠습니다.

1. 설치

npx create-react-app styled-dark-mode
npm install --save styled-components

2. app.js 작성

아래와 같이 app.js 를 작성해준다음 실행시켜보도록 하겠습니다.

import React from 'react';
import styled, { ThemeProvider } from 'styled-components';

function App() {

    const theme = {
        colors:{
            bgColor: '#121212',
        }
    }

    return (
        <ThemeProvider theme={theme}>
            <S.Main>
                <button >버튼</button>
            </S.Main>
        </ThemeProvider>
    );
}

export default App;

const S = {};
S.Main = styled.div`
  width: 100%;
  height: 100vh;
  background-color: ${props => props.theme.colors.bgColor}; //props.theme 를 통해 값을 주입받을수 있습니다.
`;

3. 실행결과

https://image.woolta.com/3fe8cc6a9d105f27.png 위의 이미지 처럼 props.theme 를 사용하여 ThemeProvider 에서 주입받은 값을 사용 가능한 것을 확인할 수 있습니다.

다크 모드 추가하기

위의 App.js 를 보게 되면 각 모드 변경시에 주입되는 부분의 theme만 변경하게 되면 아주 손쉽고 유지보수 간편한 다크모드를 만들 수 있습니다.

  <ThemeProvider theme={theme}/> //theme 만 각 모드에 따라 변경되도록 설정하기.

1. 각 모드별 파일 생성하기.

theme.js 파일을 생성해 각 테마 별 컬러를 지정해 주도록 하겠습니다. 이때 각 테마별 변수 이름이 다르면 안되니 주의해 주세요.!!

export const dark ={
    colors:{
        titleColor : '#121212',
        bgColor: '#b8b8b8',
    }
}

export const light ={
    colors:{
        titleColor : '#b8b8b8',
        bgColor: '#121212',
    }
}

2. 버튼 컴포넌트 작성

테마 변경을 위한 버튼 컴포넌트를 작성하도록 하겠습니다. button.js 파일 생성후 아래와 같이 작성해주세요.

import React from 'react';
import styled from 'styled-components';

function Button({title, click}) {

    return (
        <S.Button onClick={click}>
            <span>{title}</span>
        </S.Button>
    );
};


export default Button;

const S = {};

S.Button = styled.button`
  margin: 5rem;
  width:20rem;
  height: 20rem;
  border: none;
  background-color: #6e827f;
  color: ${props => props.theme.colors.titleColor}; // 테마 변경 컬러 지정
  border-radius: 8px;
  cursor: pointer;
  
  span{
    font-size: 1.6rem;
    font-weight: bold;
  }
`;

3. app.js 테마변경 작업 추가

테마 변경 이벤트 적용을 위해 다음과 같이 app.js를 수정해보도록 하겠습니다. 추가되는 부분은 다음과 같습니다.

  • 환경별 테마정보 가져오기
  • 테마 모드 세팅 하기
  • 테마 변경 이벤트 추가
import React, {useState} from 'react';
import Button from "./components/Button";
import styled, { ThemeProvider } from 'styled-components';
import {dark, light} from "./theme"; // 환경별 테마 정보 가져오기

function App() {
    const [themeMode, setThemeMode] = useState('light'); // 테마 모드 세팅
    const theme = themeMode === 'light' ? light : dark; // 테마 환경에 맞는 테마 컬러 가져오기.

    const toggleTheme = () => setThemeMode(themeMode === 'light' ? 'dark' : 'light'); // 테마 변경하기 이벤트

    return (
        <ThemeProvider theme={theme}>
            <S.Main>
                <Button  title={theme ==='light'? '일반모드로 테마 변경하기' : '다크모드로 테마 변경하기' }
                         click={toggleTheme}/>
            </S.Main>
        </ThemeProvider>
    );
}

export default App;

const S = {};
S.Main = styled.div`
  width: 100%;
  height: 100vh;
  background-color: ${props => props.theme.colors.bgColor};
`;

4. 결과 확인

자 이제 위의 작성한 코드를 실행시켜 보도록 하겠습니다. 아래와 같이 보이시면 성공입니다.!!

https://image.woolta.com/theme.gif

hook 만들어 보기

브라우저 테마 상태에 따라 그에 맞는 테마를 보여주고 사용자가 테마설정을 하게 되면 브라우저 상태를 보지 않고 강제로 설정테마를 가져오도록 hook 함수를 만들어 사용하도록 하겠습니다.! 우선 useTheme.js 파일을 생성후 다음과 같이 작성해주세요.!

1. hook함수 useTheme 만들기

import {useState} from 'react';

export const useTheme = () => {

    // 브라우저 테마 정보 확인
    const isBrowserDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
    let initTheme = isBrowserDarkMode ? 'dark' : 'light';
    
    // 사용자가 테마 설정을 직접 지정한 테마가 있는지 확인
    const localSettingTheme = localStorage.getItem('theme');

    // 지정한 테마가 존재한다면 해당 테마로 설정 없으면 브라우저 기본 설정 테마로 세팅 
    if (localSettingTheme) {
        initTheme = localSettingTheme;
    }

    const [theme, setTheme] = useState(initTheme);

    const setMode = mode => {
        // 테마정보 변경하면 localstorage 에 저장해 다음에도 지정한 값으로 테마가 보이도록 설정
        window.localStorage.setItem('theme', mode)
        setTheme(mode)
    };

    const toggleTheme = () => setMode(theme === 'light' ? 'dark' : 'light');

    return [theme, toggleTheme];
};

위의 파일을 보면 matchMedia 속성을 통해 브라우저의 기본 테마 상태를 가져오고 사용자가 직접 세팅하지 않으면 해당 테마로 넣어주는 것을 확인하실 수 있습니다. 또한 toggleTheme 을 통해 테마를 변경하면 해당 테마를 localstorage에 저장해 다음 초기값을 해당값으로 세팅해주도록 하고 있습니다. 이제 app.js 파일을 hooks 을 사용하도록 변경해 보도록 하겠습니다.

1. hook을 사용한 테마 변경 작업

...
import {dark, light} from "./theme";
import {useTheme} from "./useTheme"; // 환경별 테마 정보 가져오기

function App() {
    const [themeMode, toggleTheme] = useTheme(); // hook 함수 하용
    const theme = themeMode === 'light' ? light : dark; // 테마 환경에 맞는 테마 컬러 가져오기.

    return (
        <ThemeProvider theme={theme}>
            <S.Main>
                <Button title={theme === 'light' ? '일반모드로 테마 변경하기' : '다크모드로 테마 변경하기'}
                        click={toggleTheme}/>
            </S.Main>
        </ThemeProvider>
    );
}
...

훨씬 간결하게 테마 변경 작업을 진행하는것을 보실 수 있습니다. :)

참조

Copyright © 2018 woolta.com

gommpo111@gmail.com