다크 모드를 구현해보자 (feat. styled-components with Next.js)

Paul Kang·2021년 9월 18일
0
post-thumbnail

🕶 다크 모드란?

다크 모드는 어두운 배경에 밝은 텍스트를 표시하는 것을 말합니다. 스크린에서 방출되는 빛을 줄임으로써 컴퓨터나 테블릿, 모바일에서 화면을 조금 더 쉽게 볼 수 있도록 해줍니다.

💄 스타일 컴포넌트?

리액트와 가장 호흡이 찰떡이라 생각하는 CSS in JS 라이브러리 입니다.
모던한 프론트엔드 개발자에게 개발자 경험을 획기적으로 개선해줍니다.

그럼 다크 모드를 구현해 봅시다!

npm i styled-components

우선 스타일 컴포넌트를 설치해주고 그 다음 각 프로젝트의 root 디렉토리에 ThemeConfig.js(TS 환경이라면 확장자를 바꿔줍시다) 파일을 만들고 다음과 같이 설정합니다.

import { createGlobalStyle} from "styled-components"

export const lightTheme = {
  body: '#FFF',
  text: '#363537',
  toggleBorder: '#FFF',
  background: '#363537',
}

export const darkTheme = {
  body: '#363537',
  text: '#FAFAFA',
  toggleBorder: '#6B8096',
  background: '#999',
}

export const GlobalStyles = createGlobalStyle`
  body {
    background: ${({ theme }) => theme.body};
    color: ${({ theme }) => theme.text};
    font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif;
    transition: all 0.50s linear;
  }
`

여기서, light 테마dark 테마, 그리고 GlobalStyles를 정의합니다. 나중에 앱전체를 Theming하기 위해서는 이것들이 필요합니다. createGlobalStyle는 전역 스타일을 앱전체(body)에 주기 위해 React Component를 생성해줍니다.

이렇게 다크모드를 구현하기 위한 테마를 설정했습니다. 그럼 다크모드로 전환하기 위한 토글 버튼을 만들어 봅시다. _app.js 파일에 테마 상태를 만들고 토글을 설정해 봅시다.

const [theme, setTheme] = useState("light") 

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

가장 중요한 것을 까먹었네요. 바로 ThemeProvider로 _app.js의 렌더링 부분을 감싸주는 것입니다.

import { useState } from "react"
import { ThemeProvider } from "styled-components";

const [theme, setTheme] = useState("light") 

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

return (
  <ThemeProvider>
    <button onClick={toggleTheme}>Switch Theme</button>
    <Component {...pageProps} />
  </ThemeProvider>
) 

이렇게 설정이 모두 끝났습니다. 마지막으로 _app.js의 전체 코드를 봅시다.

function App({ Component, pageProps }) {
  const [theme, setTheme] = useState("light") 

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

  return (
    <ThemeProvider theme={theme == 'light' ? lightTheme : darkTheme}>
      <GlobalStyles />
      <button onClick={toggleTheme}>Switch Theme</button>
      <Component {...pageProps} />
    </ThemeProvider&gt;
  ) 
}
export default App

이렇게 성공적으로 Next.js에서의 다크모드를 구현했습니다. 🤩

TS환경에서의 _app.ts도 올려봅니다.

import Layout from '../components/Layout'
import { AppProps } from 'next/app'
import { ThemeProvider } from 'styled-components'
import { useState } from 'react'
import { darkTheme, GlobalStyles, lightTheme } from '../my-theme'

function MyApp({ Component, pageProps }: AppProps) {

  const [theme, setTheme] = useState<string>('light');
  const toggleTheme = () => {
    theme == 'light' ? setTheme('dark') : setTheme('light')
  }
  return (
    <ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>
      <GlobalStyles />
        <Layout>
          <Component {...pageProps} />
          <button onClick={toggleTheme}>Switch Theme</button>
        </Layout>
    </ThemeProvider>
    )
}

export default MyApp

이건 ThemeConfig.ts

import { createGlobalStyle, DefaultTheme } from 'styled-components';

export const lightTheme: DefaultTheme = {
  body: '#FFF',
  text: '#363537',
  toggleBorder: '#FFF',
  background: '#363537',
}

export const darkTheme: DefaultTheme = {
  body: '#363537',
  text: '#FAFAFA',
  toggleBorder: '#6B8096',
  background: '#999',
}

export const GlobalStyles = createGlobalStyle`
  body {
    background: ${({ theme }) => theme.body};
    color: ${({ theme }) => theme.text};
    font-family: Tahoma, Helvetica, Arial, Roboto, sans-serif;
    transition: all 0.50s linear;
  }
`

TS 환경에선 특별히 styled.d.ts 설정을 해줘야 합니다 (root 디렉토리에 만들어줍니다.)

import 'styled-components';

declare module 'styled-components' {
  export interface DefaultTheme {
    body: string;
    text: string;
    toggleBorder?: string;
    background?: string;
  }
}

더 modern한 다크 모드 구현 방법은 없을까?

이 글을 작성하기 위해 조사를 하던 중 useDarkMode Hook이 존재한다는 것을 알게 되었습니다.

useDarkMode Hook를 사용하면 테마 상태를 설정하거나 토글을 만들 필요가 없습니다.

이런 핫한 기능은 다음에 따로 정리해볼게요!

profile
뭐든 기록하면 자산!

0개의 댓글