스타일 구성 요소를 사용하여 React 앱에서 다크 모드 구현하기

게시 됨: 2022-03-10
빠른 요약 ↬ 라이트 모드는 대부분의 웹 및 모바일 앱에서 사용하는 규칙입니다. 그러나 현대 개발에서 우리는 어두운 배경에 밝은 텍스트와 인터페이스 요소를 표시하는 어두운 모드가 빠르게 사용자 선호 사항이 되는 것을 보았습니다. 이 기사에서는 styled-components 라이브러리를 사용하고 후크와 같은 일부 React 기능을 활용하여 간단한 웹 페이지의 React 앱에서 다크 모드를 효율적으로 구현하는 방법을 배웁니다. 또한 다크 모드의 장단점과 채택해야 하는 이유에 대해서도 논의할 것입니다.

가장 일반적으로 요청되는 소프트웨어 기능 중 하나는 다크 모드(또는 다른 사람들이 부르는 야간 모드)입니다. 우리는 매일 사용하는 앱에서 다크 모드를 봅니다. 모바일에서 웹 앱에 이르기까지 다크 모드는 사용자의 눈을 돌보고 싶은 기업에게 필수 요소가 되었습니다.

다크 모드는 UI에서 대부분 어두운 표면을 표시하는 추가 기능입니다. 대부분의 주요 회사(예: YouTube, Twitter 및 Netflix)는 모바일 및 웹 앱에서 다크 모드를 채택했습니다.

React와 styled-components에 대해 깊이 있게 다루지는 않겠지만 React, CSS 및 styled-components에 대한 기본 지식은 유용할 것입니다. 이 튜토리얼은 다크 모드를 좋아하는 사람들을 만족시켜 웹 애플리케이션을 향상시키려는 사람들에게 도움이 될 것입니다.

StackOverflow, Twitter에서 다크 모드 발표
StackOverflow, Twitter에서 다크 모드 발표(큰 미리보기)

이 기사를 작성하기 며칠 전에 StackOverflow는 사용자에게 두 모드 사이를 전환할 수 있는 기회를 제공하는 다크 모드 출시를 발표했습니다.

다크 모드는 눈의 피로를 줄여주고 컴퓨터나 휴대폰으로 장시간 작업할 때 도움이 됩니다.

다크 모드란?

다크 모드는 어두운 배경에 밝은 텍스트와 인터페이스 요소를 표시하는 모든 인터페이스의 색 구성표로, 휴대폰, 태블릿 및 컴퓨터에서 화면을 좀 더 쉽게 볼 수 있습니다. 다크 모드는 가독성에 필요한 최소 색상 대비 비율을 유지하면서 화면에서 방출되는 빛을 줄입니다.

점프 후 더! 아래에서 계속 읽기 ↓

다크 모드에 관심을 가져야 하는 이유

다크 모드는 눈의 피로를 줄이고 화면을 현재 조명 조건에 맞게 조정하며 야간이나 어두운 환경에서 사용하기 쉽도록 하여 시각적 인체 공학을 향상시킵니다.

앱에서 다크 모드를 구현하기 전에 이점을 살펴보겠습니다.

배터리 절약

웹 및 모바일 앱의 다크 모드는 장치의 배터리 수명을 연장할 수 있습니다. Google은 OLED 화면의 다크 모드가 배터리 수명에 큰 도움이 되었음을 확인했습니다.

예를 들어 밝기가 50%일 때 YouTube 앱의 어두운 모드는 평평한 흰색 배경보다 화면 에너지를 약 15% 더 절약합니다. 100% 화면 밝기에서 어두운 인터페이스는 화면 에너지를 무려 60%나 절약합니다.

다크 모드는 아름답다

어두운 모드는 아름답고 화면의 매력을 크게 높일 수 있습니다.

대부분의 제품이 비슷한 밋밋한 화이트 룩을 추구하는 반면, 다크 모드는 신비롭고 새로운 느낌을 주는 색다른 것을 제공합니다.

또한 대시보드, 그림, 사진 등의 그래픽 콘텐츠를 신선하게 표현할 수 있는 좋은 기회를 제공합니다.

트위터 다크 대 라이트 모드
밝은 모드보다 Twitter의 어두운 모드의 아름다움 (큰 미리보기)

이제 다음 웹 앱에서 다크 모드를 구현해야 하는 이유를 알았으므로 이 자습서의 정의 리소스인 styled-components에 대해 자세히 살펴보겠습니다.

다크 모드는 어두운 배경에 밝은 텍스트와 인터페이스 요소를 표시하는 모든 인터페이스의 색 구성표로 휴대폰, 태블릿 및 컴퓨터에서 조금 더 쉽게 볼 수 있습니다.

"

스타일이 지정된 구성 요소는 무엇입니까?

이 기사 전체에서 styled-components 라이브러리를 매우 자주 사용할 것입니다. 최신 웹 앱의 스타일을 지정하는 방법은 항상 여러 가지가 있었습니다. index.css 파일을 만들어 HTML에 연결하거나 HTML 파일 내부에 스타일을 지정하는 등 문서 수준에서 스타일을 지정하는 전통적인 방법이 있습니다.

CSS-in-JS가 도입된 이후 웹 앱의 스타일 지정 방식이 최근 많이 변경되었습니다.

CSS-in-JS는 CSS가 JavaScript를 사용하여 구성되는 패턴을 말합니다. 태그가 지정된 템플릿 리터럴을 활용하여 JavaScript 파일의 구성 요소에 스타일을 지정합니다.

CSS-in-JS에 대해 자세히 알아보려면 해당 주제에 대한 Anna Monus의 기사를 확인하십시오.

styled-components는 CSS-in-JS 라이브러리로 미디어 쿼리, 유사 선택기 및 중첩을 포함하여 CSS의 모든 기능을 사용할 수 있습니다.

스타일이 지정된 구성 요소를 사용하는 이유는 무엇입니까?

styled-components는 다음과 같은 이유로 만들어졌습니다.

  • 클래스 이름 없음 지옥
    요소에 대한 클래스 이름을 찾기 위해 머리를 긁는 대신 styled-components는 스타일에 대한 고유한 클래스 이름을 생성합니다. 철자 오류나 의미 없는 클래스 이름 사용에 대해 걱정할 필요가 없습니다.
  • 소품 사용하기
    styled-components를 사용하면 React에서 일반적으로 사용되는 props 매개변수를 사용하여 스타일 속성을 확장할 수 있습니다. 따라서 애플리케이션의 상태를 통해 구성 요소의 느낌에 동적으로 영향을 줍니다.
  • Sass 구문 지원
    styled-components를 사용하면 전처리기나 추가 빌드 도구를 설정하지 않고도 즉시 Sass 구문을 작성할 수 있습니다. 스타일 정의에서 & 문자를 사용하여 현재 구성 요소를 대상으로 하고, 유사 선택기를 사용하고, 중첩을 실험할 수 있습니다.
  • 테마
    styled-components는 ThemeProvider 래퍼 구성 요소를 내보내 전체 테마를 지원합니다. 이 구성 요소는 Context API를 통해 자체 내의 모든 React 구성 요소에 테마를 제공합니다. 렌더링 트리에서 모든 스타일이 지정된 구성 요소는 여러 수준의 깊이에 있는 경우에도 제공된 테마에 액세스할 수 있습니다. 이 튜토리얼에서 계속 진행하면서 styled-components의 테마 기능을 더 깊이 살펴볼 것입니다.

styled-components의 장점을 더 배우려면 Kris Guzman의 기사를 확인하십시오.

다크 모드 구현

이 기사에서는 YouTube와 같은 간단한 웹 페이지에서 다크 모드를 구현하려고 합니다.

따라가려면 starter 브랜치에서 원본 리포지토리를 복제해야 합니다.

설정

package.json 파일에 모든 종속성을 설치해 보겠습니다. 터미널에서 다음 명령을 실행합니다.

 npm install

성공적으로 설치되면 npm start 를 실행합니다. 다음은 다크 모드가 구현되지 않은 웹 페이지의 모습입니다.

다크 모드 없이 사용할 웹 페이지
다크 모드 없이 사용할 웹 페이지. (큰 미리보기)

styled-components 를 설치하려면 터미널에서 npm install styled-components 실행하십시오.

구현

다크 모드를 구현하려면 네 가지 구성 요소를 만들어야 합니다.

  • Theme
    여기에는 밝고 어두운 테마의 색상 속성이 포함됩니다.
  • GlobalStyles
    여기에는 전체 문서에 대한 전역 스타일이 포함됩니다.
  • Toggler
    이것은 기능을 토글하는 버튼 요소를 보유합니다.
  • useDarkMode
    이 사용자 정의 후크는 localStorage에서 테마 변경과 테마 지속성 뒤에 있는 논리를 처리합니다.

테마 구성 요소

src 폴더의 components 폴더에 구성 요소가 표시됩니다. Themes.js 파일을 만들고 다음 코드를 추가합니다.

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

여기에서 고유한 색상 변수를 사용하여 lightThemedarkTheme 개체를 정의하고 내보냈습니다. 자유롭게 실험하고 자신에게 맞게 변수를 사용자 정의하십시오.

globalStyles 구성 요소

components 폴더에 남아 globalStyles.js 파일을 만들고 다음 코드를 추가합니다.

 import { createGlobalStyle} from "styled-components" 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; } `

styled-components에서 createGlobalStyle 을 가져왔습니다. createGlobalStyle 메소드는 styled-components 버전 3에서 이제 사용되지 않는 injectGlobal 메소드를 대체합니다. 이 메소드는 컴포넌트 트리에 추가될 때 문서(이 경우 App.js )에 전역 스타일을 삽입하는 React 컴포넌트를 생성합니다.

GlobalStyle 구성 요소를 정의하고 backgroundcolor 속성을 테마 개체의 값에 할당했습니다. 따라서 토글을 전환할 때마다 ThemeProvider 에 전달하는 어두운 테마 또는 밝은 테마 개체에 따라 값이 변경됩니다(나중에 진행하면서 생성됨).

0.50s 의 transition 속성은 이 변경이 조금 더 부드럽게 일어나도록 하여 앞뒤로 토글할 때 변경이 일어나는 것을 볼 수 있습니다.

테마 토글 기능 만들기

테마 토글 기능을 구현하려면 몇 줄의 코드만 추가하면 됩니다. App.js 파일에 다음 코드를 추가합니다(강조 표시된 코드는 추가해야 하는 코드임).

 import React, { useState, useEffect } from "react";
import {ThemeProvider} from "styled-components"; import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes"
import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
 const [theme, setTheme] = useState('light'); const themeToggler = () => { theme === 'light' ? setTheme('dark') : setTheme('light') }
 useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (
 <ThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}> <> <GlobalStyles/>
 <div className="App">
 <button onClick={themeToggler}>Switch Theme</button>
 { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div>
 </> </ThemeProvider>
); }; export default App;

강조 표시된 코드는 App.js 에 새로 추가된 코드입니다. styled-components 에서 ThemeProvider 를 가져왔습니다. ThemeProvider 는 테마 지원을 제공하는 styled-components 라이브러리의 도우미 구성 요소입니다. 이 도우미 구성 요소는 Context API를 통해 자체 아래의 모든 React 구성 요소에 테마를 주입합니다.

렌더링 트리에서 모든 스타일이 지정된 구성 요소는 여러 수준의 깊이에 있는 경우에도 제공된 테마에 액세스할 수 있습니다. "테마" 섹션을 확인하세요.

다음으로 ./components/Globalstyle 에서 GlobalStyle 래퍼를 가져옵니다. 마지막으로 맨 위에서 ./components/Themes 에서 lightThemedarkTheme 객체를 모두 가져옵니다.

토글 방법을 만들려면 테마의 초기 색상 값을 유지하는 상태가 필요합니다. 따라서 theme 상태를 만들고 useState 후크를 사용하여 초기 상태를 light 로 설정합니다.

이제 토글 기능을 위해.

themeToggler 메서드는 삼항 연산자를 사용하여 theme 의 상태를 확인하고 조건 값에 따라 어둡거나 밝게 토글합니다.

스타일이 지정된 구성 요소 도우미 구성 요소인 ThemeProviderreturn 문에 모든 것을 래핑하고 그 아래에 구성 요소를 삽입합니다. GlobalStyles글로벌 스타일 을 구성 요소에 주입한다는 것을 기억하십시오. 따라서 ThemeProvider 래퍼 구성 요소 내부에서 호출됩니다.

마지막으로 themeToggler 메서드를 할당하는 onClick 이벤트가 있는 버튼을 만들었습니다.

지금까지의 결과를 보자.

지속성 없이 구현된 다크 모드.
지속성 없이 구현된 다크 모드(큰 미리보기)

App.js 파일을 리팩토링해야 합니다. 많은 코드가 DRY가 아닙니다. (DRY는 반복을 줄이기 위한 소프트웨어 개발의 기본 원칙인 "Don't repeat yourself"의 약자입니다.) 모든 논리는 App.js 에 있는 것 같습니다. 명확성을 위해 논리를 분리하는 것이 좋습니다. 따라서 토글 기능을 처리하는 구성 요소를 만듭니다.

토글 컴포넌트

계속 components 폴더 내에서 Toggler.js 파일을 만들고 여기에 다음 코드를 추가합니다.

 import React from 'react' import { func, string } from 'prop-types'; import styled from "styled-components" const Button = styled.button` background: ${({ theme }) => theme.background}; border: 2px solid ${({ theme }) => theme.toggleBorder}; color: ${({ theme }) => theme.text}; border-radius: 30px; cursor: pointer; font-size:0.8rem; padding: 0.6rem; } \`; const Toggle = ({theme, toggleTheme }) => { return ( <Button onClick={toggleTheme} > Switch Theme </Button> ); }; Toggle.propTypes = { theme: string.isRequired, toggleTheme: func.isRequired, } export default Toggle;

깔끔하게 유지하기 위해 styled-components의 styled 함수를 사용하여 Toggle 구성 요소에서 토글 버튼의 ​​스타일을 지정했습니다.

이것은 순전히 프레젠테이션을 위한 것입니다. 원하는 대로 단추의 스타일을 지정할 수 있습니다.

Toggle 구성 요소 내에서 두 개의 소품을 전달합니다.

  • theme 는 현재 테마(밝음 또는 어두움)를 제공합니다.
  • toggleTheme 함수는 테마 사이를 전환하는 데 사용됩니다.

다음으로 Button 구성 요소를 반환하고 onClick 이벤트에 toggleTheme 함수를 할당합니다.

마지막으로 propTypes 를 사용하여 유형을 정의하여 themestring 이고 isRequired 하고 toggleThemefuncisRequired 합니다.

사용자 정의 후크 사용( useDarkMode )

애플리케이션을 구축할 때 확장성이 가장 중요합니다. 즉, 비즈니스 로직을 재사용할 수 있어야 여러 곳에서, 심지어 다른 프로젝트에서도 사용할 수 있습니다.

그렇기 때문에 토글 기능을 별도의 구성 요소로 옮기는 것이 좋습니다. 이를 위해 우리는 우리 자신의 커스텀 훅을 만들 것입니다.

components 폴더에 useDarkMode.js 라는 새 파일을 만들고 약간의 조정을 통해 이 파일로 논리를 이동하겠습니다. 파일에 다음 코드를 추가합니다.

 import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light'); const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme && setTheme(localTheme) }, []); return [theme, themeToggler] };

여기에 몇 가지를 추가했습니다.

  • setMode
    브라우저의 세션 간에 유지하기 위해 localStorage 를 사용합니다. 따라서 사용자가 어둡거나 밝은 테마를 선택한 경우 다음에 앱을 방문하거나 페이지를 새로고침하면 해당 테마가 표시됩니다. 따라서 이 함수는 상태를 설정하고 themelocalStorage 에 전달합니다.
  • themeToggler
    이 함수는 삼항 연산자를 사용하여 테마의 상태를 확인하고 조건의 진실에 따라 어둡거나 밝게 토글합니다.
  • useEffect
    컴포넌트 마운팅을 확인하기 위해 useEffect 후크를 구현했습니다. 사용자가 이전에 테마를 선택한 경우 setTheme 함수에 전달합니다. 결국 우리는 선택한 theme 와 모드 간 전환을 위한 themeToggler 함수가 포함된 theme 을 반환합니다.

다크 모드 구성 요소가 매끄럽게 보인다는 데 동의할 것입니다.

마지막 터치를 위해 App.js 로 가봅시다.

 import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components";
import {useDarkMode} from "./components/useDarkMode"
import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
 const [theme, themeToggler] = useDarkMode(); const themeMode = theme === 'light' ? lightTheme : darkTheme;
useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []); return (
 <ThemeProvider theme={themeMode}>
 <> <GlobalStyles/> <div className="App">
 <Toggle theme={theme} toggleTheme={themeToggler} />
 { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;

강조 표시된 코드가 App.js 에 새로 추가되었습니다.

먼저 사용자 정의 후크를 가져오고 themethemeToggler 소품을 구조화하고 useDarkMode 함수로 설정합니다.

useDarkMode 메서드는 처음에 App.js 에 있던 theme 상태를 대체합니다 .

당시 theme 모드의 조건에 따라 밝은 테마 또는 어두운 테마를 렌더링하는 themeMode 변수를 선언합니다.

이제 ThemeProvider 래퍼 구성 요소에 방금 생성 themeMode 변수가 theme 소품에 할당되었습니다.

마지막으로 일반 버튼 대신 Toggle 구성 요소를 전달합니다.

Toggle 구성 요소에서 버튼을 정의하고 스타일을 지정하고 themetoggleTheme 를 소품으로 전달했습니다. 따라서 우리가 해야 할 일은 이 props를 Toggle 구성 요소에 적절하게 전달하는 것뿐입니다. 이 구성 요소는 App.js 에서 버튼 역할을 합니다.

네! 다크 모드가 설정되어 있으며 페이지를 새로 고치거나 새 탭에서 방문할 때 색상이 변경되지 않고 지속됩니다.

실행 결과를 살펴보겠습니다.

다크 모드가 구현되었지만 브라우저가 다시 로드될 때 버튼 색상에 결함이 있습니다.
다크 모드가 구현되었지만 브라우저가 다시 로드될 때 버튼 색상에 결함이 있습니다. (큰 미리보기)

거의 모든 것이 잘 작동하지만, 우리의 경험을 훌륭하게 만들기 위해 우리가 할 수 있는 한 가지 작은 일이 있습니다. 어두운 테마로 전환한 다음 페이지를 새로고침하세요. 버튼의 파란색이 잠시 동안 회색보다 먼저 로드되는 것이 보입니까? 이는 useState 후크가 처음에 light 테마를 시작하기 때문에 발생합니다. 그 후 useEffect 가 실행되고 localStorage 를 확인한 다음 themedark 로 설정합니다. 사용자 정의 후크 useDarkMode.js 로 건너뛰고 약간의 코드를 추가해 보겠습니다.

 import { useEffect, useState } from 'react'; export const useDarkMode = () => { const [theme, setTheme] = useState('light');
 const [mountedComponent, setMountedComponent] = useState(false)
 const setMode = mode => { window.localStorage.setItem('theme', mode) setTheme(mode) }; const themeToggler = () => { theme === 'light' ? setMode('dark') : setMode('light') }; useEffect(() => { const localTheme = window.localStorage.getItem('theme'); localTheme ? setTheme(localTheme) : setMode('light')
 setMountedComponent(true)
 }, []); return [theme, themeToggler, }, []); return [theme, themeToggler, mountedComponent ] 반환
};

강조 표시된 코드는 useDarkMode.js 에 추가된 유일한 코드입니다. mountedComponent 라는 다른 상태를 만들고 useState 후크를 사용하여 기본값을 false 로 설정했습니다. 다음으로 useEffect 후크 내부에서 setMountedComponent 를 사용하여 mountedComponent 상태를 true 로 설정합니다. 마지막으로 return 배열에 mountedComponent 상태를 포함합니다.

마지막으로 App.js 에 약간의 코드를 추가하여 모두 작동하도록 합시다.

 import React, { useState, useEffect } from "react"; import {ThemeProvider} from "styled-components"; import {useDarkMode} from "./components/useDarkMode" import { GlobalStyles } from "./components/Globalstyle"; import { lightTheme, darkTheme } from "./components/Themes" import Toggle from "./components/Toggler" import "./App.css"; import dummyData from "./data"; import CardList from "./components/CardList"; const App = () => { const [videos, setVideos] = useState([]);
 const [theme, themeToggler, mountedComponent] = useDarkMode();
 const themeMode = theme === 'light' ? lightTheme : darkTheme; useEffect(() => { const timer = setTimeout(() => { setVideos(dummyData); }, 1000); return () => clearTimeout(timer); }, []);
 if(!mountedComponent) return <div/>
 return ( <ThemeProvider theme={themeMode}> <> <GlobalStyles/> <div className="App"> <Toggle theme={theme} toggleTheme={themeToggler} /> { videos.map((list, index) => { return ( <section key={index}> <h2 className="section-title">{list.section}</h2> <CardList list={list} /> <hr /> </section> ); })} </div> </> </ThemeProvider> ); }; export default App;

우리는 mountedComponent 상태를 useDarkMode 후크에 추가했으며 구성 요소가 마운트되었는지 확인했습니다. useEffect 후크에서 발생하기 때문입니다. 아직 발생하지 않은 경우 빈 div 를 렌더링합니다.

다크 모드 웹 페이지의 결과를 봅시다.

다크 모드의 최종 결과
다크 모드의 최종 결과(큰 미리보기)

이제 어두운 모드에 있는 동안 페이지가 다시 로드될 때 버튼의 색상이 변경되지 않음을 알 수 있습니다.

결론

다크 모드는 점점 더 사용자 선호 사항이 되고 있으며, 스타일이 지정된 구성 요소에서 ThemeProvider 테마 래퍼를 사용할 때 React 웹 앱에서 이를 구현하는 것이 훨씬 쉽습니다. 다크 모드를 구현할 때 스타일이 지정된 구성 요소를 실험해 보십시오. 버튼 대신 아이콘을 추가할 수 있습니다.

아래 의견 섹션에서 스타일 구성 요소의 테마 기능에 대한 피드백과 경험을 공유하십시오. 나는 당신이 무엇을 생각해내는지 보고 싶습니다!

이 문서의 지원 리포지토리는 GitHub에서 사용할 수 있습니다. 또한 CodeSandbox에서 확인하십시오.

참고문헌

  • "문서", 스타일이 지정된 구성 요소
  • "스타일 구성 요소를 사용하여 앱의 다크 모드 만들기", Tom Nolan, Medium