React에서 암흑 모드를 구현하는 방법

최근에 사람들이 스크린을 움직여도 어둠 모드로 돌아가는 것을 보았을 것이다.
브라우저나 당신이 가장 좋아하는 소셜 미디어 앱너는 아마도 너의 사이트에서 어떻게 같은 기능을 실현하는지 알고 싶을 것이다.
이 강좌에서 전환 단추를 어떻게 사용하고 사용자가 페이지를 눌렀을 때 페이지 테마를 변경하는지 볼 수 있습니다.
또한 브라우저 주제(어둡거나 빛)를 읽고 해당 주제를 기반으로 페이지를 로드하는 방법도 알아봅니다.
또한 테마 설정을 보존하여 나중에 방문할 수 있도록 사용자의 선호도를 저장하는 방법을 볼 수 있습니다.

응용 프로그램 만들기


우선, 평소와 같이, 다음 명령을 사용하여react 프로그램을 만듭니다
npx create-react-app dark-theme

기본 페이지 설정


프레젠테이션의 예제 HTML 페이지를 설정합니다.
다음 코드로 App.js 파일을 업데이트합니다.
우리가 내비게이션 표시줄을 만들었는데, 전환 단추가 있습니다.
테마, 제목, lorem ipsum 텍스트의 몇 단락을 전환하는 데 사용됩니다.
import React from "react"
import "./App.css"

function App() {
  return (
    <div className="App">
      <nav className="navigation">
        <div className="logo">Dark Mode</div>
        <button className="toggle_btn">Toggle</button>
      </nav>
      <h1>Lorem ipsum dolor sit amet consectetur adipisicing elit.</h1> <p>
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Rem, placeat
        adipisci aut repudiandae molestias quis possimus dignissimos tenetur
        tempore numquam, eos, sed deleniti quae voluptas asperiores harum labore
        ab deserunt? Perspiciatis, quisquam totam sapiente dolore cum, officiis
        veritatis sed ut quidem corrupti animi! Officiis animi quaerat officia
        ducimus, eveniet magnam delectus cupiditate amet vero aspernatur
        perferendis dolorem dignissimos praesentium vitae. Architecto dolorem
        eius distinctio nostrum fugit! Quas molestias, unde possimus vitae
        totam, quam eum earum est inventore harum aperiam praesentium sapiente
        repellat minima dolor corrupti eligendi, tempore reprehenderit animi
        delectus. Perferendis, et maxime reprehenderit possimus numquam
        corrupti, libero sed veniam optio vel a voluptates? Vel deserunt a animi
        saepe, dolores consequatur obcaecati ratione odio, ducimus repellendus
        aperiam error, laborum sed. Aspernatur excepturi vitae sint doloremque
        unde ipsa veniam placeat debitis? Aspernatur reprehenderit quibusdam
        pariatur fuga numquam voluptate magni praesentium optio nisi repellat
        placeat maxime at similique, provident, consequuntur, corrupti adipisci!
      </p>
      <p>
        Lorem ipsum dolor, sit amet consectetur adipisicing elit. Quis tempora
        maiores fugiat neque doloribus illum omnis expedita aliquam voluptatum
        possimus ad mollitia laudantium, non cumque quia, illo tempore odit
        veniam! Nisi enim, eligendi error quod dicta sunt fugit non et. Repellat
        corporis officiis odio repudiandae doloremque similique quisquam dicta
        enim, porro sed assumenda architecto iste accusantium quo quod, in
        incidunt? Eaque ipsum, id commodi reprehenderit quam exercitationem ad
        iure a cum necessitatibus corporis quas, odit, deserunt atque reiciendis
        deleniti fuga et laudantium officia adipisci. Voluptates, nesciunt!
        Repellendus consequuntur voluptate vero? Officia quaerat voluptates
        dolorem provident excepturi expedita nostrum, voluptas consequatur
        architecto. Vel recusandae officia quidem impedit magni cupiditate?
        Deserunt qui velit totam dolorem delectus necessitatibus possimus
        explicabo veritatis doloremque sequi. Optio, quod quaerat fugiat
        recusandae officia earum voluptatem aliquam unde obcaecati laborum
        necessitatibus porro omnis laboriosam esse, illum numquam quibusdam
        magnam. Voluptate et nesciunt quisquam sequi perferendis minus quaerat
        temporibus!
      </p>
    </div>
  )
}

export default App
현재 index.css 파일에 기본 스타일을 추가합니다.
css 변수의 사용을 알 수 있습니다. 다음 장에서 사용할 것입니다.
body {
  margin: 1rem auto;
  max-width: 800px;
  background-color: #fff;
  color: #000;
  --button-text: #000;
  --button-bg: #fff;
}

.toggle_btn {
  background-color: var(--button-bg);
  color: var(--button-text);
  cursor: pointer;
}
.navigation {
  display: flex;
  justify-content: space-between;
}
.logo {
  font-size: 1.2rem;
  font-weight: 600;
}
현재 yarn start 명령을 사용하고 http://localhost:3000 프로그램을 실행합니다.
다음과 같은 페이지를 볼 수 있습니다.

지금 전환 버튼을 누르면 아무것도 할 수 없습니다.열심히 합시다!

useDarkMode 갈고리


암흑 모드 기능을 실현하기 위해서, 우리는 사용자 정의 연결을 작성할 것이다.
따라서 hooks 디렉터리에 src라는 폴더를 만들고 useDarkMode.js라는 파일을 만듭니다.
다음 코드를 사용하여 파일을 업데이트합니다.
import { useEffect, useState } from "react"

export default () => {
  const [isDark, setIsDark] = useState(false)

  useEffect(() => {
    const className = "dark"
    if (isDark) {
      window.document.body.classList.add(className)
    } else {
      window.document.body.classList.remove(className)
    }
  }, [isDark])

  return [isDark, setIsDark]
}
위 코드에서 볼 수 있듯이, 우리는 useState 훅을 사용하여 국부 상태 변수를 초기화하고, 기본값은false입니다.
이 상태에서는 다크 모드가 활성화되어 있는지 여부를 결정합니다.또한 useEffect 연결 고리를 사용하고 있습니다.
우리는 isDark 상태가true 또는false로 설정되었는지 확인하고 문서 주체에서 dark라는 클래스를 추가/삭제할 것입니다.
저희가 isDarkuseEffect 갈고리의 의존항으로 추가한 것을 보실 수 있습니다.
이렇게 하면 isDark 상태의 값이 변경된 경우에만 효과가 실행됩니다.

useDarkMode 갈고리를 이용하다


이제 App.js에서 만든 갈고리를 사용하고 단추를 눌러 귀속시킵니다.
import React from "react"
import "./App.css"
import useDarkMode from "./hooks/useDarkMode"

function App() {
  const [isDarkMode, setDarkMode] = useDarkMode()
  return (
    <div className="App">
      <nav className="navigation">
        <div className="logo">Dark Mode</div>
        <button className="toggle_btn" onClick={() => setDarkMode(!isDarkMode)}>
          Toggle
        </button>
      </nav>
      <h1>Lorem ipsum dolor sit amet consectetur adipisicing elit.</h1>
      ...
    </div>
  )
}

export default App
위 코드에서 사용자가 전환 버튼을 클릭할 때마다
우리는 setDarkMode 의 반수치 호출 isDarkMode 을 사용하여 연속적으로 눌렀을 때 truefalse 로 설정합니다.
현재, 만약 전환 단추를 누르려고 시도한다면, 아무런 변화도 볼 수 없을 것이다.
그러나 문서를 검토하고 보면 클래스 추가 및 삭제dark가 표시됩니다.

어두운 모드에 스타일 추가하기


현재 우리는 주체에 dark 클래스를 추가했습니다. css를 사용하여 배경과 글꼴 색을 변경하여 어두운 모드를 만들 수 있습니다.
다음 규칙 추가index.css
body.dark {
  background-color: #000;
  color: #fff;
  --button-text: #fff;
  --button-bg: #000;
}
상기 스타일에서 body에 클래스dark가 있으면 배경색을 검은색으로 설정하고 텍스트 색을 흰색으로 설정합니다.
또한, 단추의 스타일을 제어하기 위해 css 변수를 사용하고 있는 것을 보실 수 있습니다.
지금 전환 버튼을 클릭하면 주제가 전환되는 것을 볼 수 있습니다.

로컬 스토리지에 사용자 기본 설정 저장


사용자로서, 선택한 테마가 나중에 페이지를 다시 방문할 때 기억되고 보존되기를 원할 수도 있습니다.
현재 어두운 모드로 설정하고 페이지를 다시 불러오면 페이지가 밝은 모드로 불러옵니다.
보존 모드를 위해 사용자 기본 설정을 로컬 스토리지에 저장합니다.
다음 코드를 사용하여 업데이트useDarkMode.js
import { useEffect, useState } from "react"

export default () => {
  const key = "isDarkMode"
  const [isDark, setIsDark] = useState(() => {
    try {
      // Get from local storage by key
      const item = window.localStorage.getItem(key)
      // JSON.parse converts from Sting to Boolean
      return item ? JSON.parse(item) : undefined
    } catch (error) {
      // If error return false, i.e, light mode
      return false
    }
  })

  useEffect(() => {
    const className = "dark"
    if (isDark) {
      window.document.body.classList.add(className)
    } else {
      window.document.body.classList.remove(className)
    }
    try {
      window.localStorage.setItem(key, isDark)
    } catch (e) {
      console.error("Error in setting preference")
    }
  }, [isDark])

  return [isDark, setIsDark]
}
위 코드에서 보시면, 리셋을 사용하여 초기화 isDark 상태를 사용합니다. 리셋에서 로컬 저장소에 접근해서 사용자의 첫 번째 옵션을 얻습니다.사용자가 상태를 전환할 때, 우리는 기본 설정을 useEffect 리셋의 로컬 저장소에 저장합니다.
기본 설정을 흐림 모드로 설정하고 페이지를 다시 로드하면 흐림 모드로 로드된 페이지가 표시됩니다.

브라우저 테마/설정 읽기


대부분의 현대 브라우저는 미디어 쿼리prefers-color-scheme를 지원합니다.
이를 사용하면 사용자가 어두운 모드를 좋아하는지 밝은 모드를 좋아하는지 확인할 수 있습니다.
사용 가능Window.matchMedia()
방법 조회prefers-color-scheme의 값은 아래 코드에서 강조된 것과 같다.
또한 기본 설정 값을 저장하는 새로운 변수darkModeEnabled를 도입했습니다.
import { useEffect, useState } from "react"

export default () => {
  const key = "isDarkMode"
  const [isDark, setIsDark] = useState(() => {
    try {
      // Get from local storage by key
      const item = window.localStorage.getItem(key)
      // JSON.parse converts from Sting to Boolean
      return item ? JSON.parse(item) : undefined
    } catch (error) {
      // If error return false, i.e, light mode
      return false
    }
  })

  // Check if user has any preference in the local storage.
  // If not then load the system preference

  const darkModeEnabled =
    typeof isDark !== "undefined"
      ? isDark
      : window.matchMedia("(prefers-color-scheme: dark)").matches
  useEffect(() => {
    const className = "dark"
    if (darkModeEnabled) {
      window.document.body.classList.add(className)
    } else {
      window.document.body.classList.remove(className)
    }
    try {
      window.localStorage.setItem(key, darkModeEnabled)
    } catch (e) {
      console.error("Error in setting preference")
    }
  }, [darkModeEnabled])

  return [darkModeEnabled, setIsDark]
}
현재 시스템이 암흑 모드로 설정되어 있으면 기본적으로 페이지가 암흑 모드로 열립니다.

모드마다 다른 아이콘 보이기


이제 어둠과 광명 모드를 위한 단독 아이콘을 보여 줍니다.
이를 위해 react-icons를 사용하겠습니다.
다음 명령을 사용하여 react 아이콘을 설치합니다.
yarn add react-icons
설치가 완료되면 BsSunBsMoon 아이콘을 사용하여 각각 밝기 모드와 어둡기 모드를 나타냅니다.
import React from "react"
import "./App.css"
import useDarkMode from "./hooks/useDarkMode"
import { BsMoon, BsSun } from "react-icons/bs"

function App() {
  const [isDarkMode, setDarkMode] = useDarkMode()
  return (
    <div className="App">
      <nav className="navigation">
        <div className="logo">Dark Mode</div>
        <button className="toggle_btn" onClick={() => setDarkMode(!isDarkMode)}>
          {isDarkMode ? (
            <BsSun color="#ff0" size="24" title="Switch to light mode" />
          ) : (
            <BsMoon size="24" title="Switch to dark mode" />
          )}
        </button>
      </nav>
      <h1>Lorem ipsum dolor sit amet consectetur adipisicing elit.</h1>
      ...
    </div>
  )
}

export default App
마지막으로 버튼의 테두리를 제거하기 위해 CSS 파일을 업데이트합니다.
...
.toggle_btn {
  background-color: var(--button-bg);
  color: var(--button-text);
  cursor: pointer;
  border: none;
}
...
지금 페이지를 불러오면 추가된 아이콘을 볼 수 있습니다.

소스 코드 및 데모


전체 source code heredemo here

좋은 웹페이지 즐겨찾기