[React.js] 다크모드 (Emotion.js + Next.js + TypeScript)
https://velog.io/@ongddree/블로그만들기-다크모드-구현
게시물을 통해 참고를 하였고, 이 게시물은 다른 프로젝트에 구현한 것을 회고합니다.
🎯 TL;DR
-
다크모드는 프로젝트 시작 후 빠르게 구현하는게 좋습니다. (꿀팁🍯)
-
헤더에 라이트모드/다크버튼의 토글을 통해 테마를 변경한다.
-
페이지를 이동, 새로고침 해도 적용한 테마가 유지되게 한다. (Context API + localStorage)
1️⃣ theme 색상 지정
// styles/theme.ts
export const lightTheme = {
MAIN: 'black',
SUB: 'white',
BACKGROUND: '#fdfdff',
};
export const darkTheme = {
MAIN: 'white',
SUB: 'black',
BACKGROUND: '#202124',
};
export type ColorTheme = typeof lightTheme;
-
프로젝트의 특성에 맞게 MAIN
, SUB
는 글자의 색상, BACKGROUND
는 배경색을 지정합니다. (더 필요한 색상이 있으면 추가합니다!)
-
typeof
를 통해 ColorTheme type을 export 해줍니다.
2️⃣ useDarkMode() 구현
-
Next.js에서는 렌더링이 일어나기 전에 localStorage
, window
등에 접근하면 에러가난다. 그래서 useEffect
내부에서 window.localStorage.getItem('theme')
를 통해 새로고침 or 첫 렌더링시 테마를 결정하게 된다. (default value는 lightTheme
이다.)
-
처음으로 localStorage.setItem
을 하는 시기는 라이트모드 -> 다크모드를 처음했을때다.
// hooks/useDarkMode.ts
import { useEffect, useState } from 'react';
import { lightTheme, darkTheme, ColorTheme } from '../styles/theme';
export const useDarkMode = () => {
// 1. 초기 colorTheme은 lightTheme를 가진다.
const [colorTheme, setColorTheme] = useState<ColorTheme>(lightTheme);
// 4. state의 값도 변경 + local 저장 값도 변경
const setMode = (mode: ColorTheme) => {
mode === lightTheme
? window.localStorage.setItem('theme', 'light')
: window.localStorage.setItem('theme', 'dark');
setColorTheme(mode);
};
// 3. 사용자가 toggleColorTheme을 하면 setMode를 통해 기존의 colorTheme과 반대 값을 저장한다.
const toggleColorTheme = () => {
colorTheme === lightTheme ? setMode(darkTheme) : setMode(lightTheme);
};
// 2. 마운트 되면 localStorage에 'theme'이 있는지 찾는다.
// - 새로고침시 다크모드/라이트모드 바로 적용
// - 페이지 로드가 처음이면 이 과정은 무시된다
useEffect(() => {
const localTheme = window.localStorage.getItem('theme');
if (localTheme !== null) { // localTheme이 존재한다면
if (localTheme === 'dark') {
setColorTheme(darkTheme);
} else {
setColorTheme(lightTheme);
}
}
}, []);
return { colorTheme, toggleColorTheme };
};
3️⃣ darkmode 상태 관리
다크모드는 프로젝트 시작 후 빠르게 구현하는게 좋습니다. (꿀팁🍯)
헤더에 라이트모드/다크버튼의 토글을 통해 테마를 변경한다.
페이지를 이동, 새로고침 해도 적용한 테마가 유지되게 한다. (Context API + localStorage)
// styles/theme.ts
export const lightTheme = {
MAIN: 'black',
SUB: 'white',
BACKGROUND: '#fdfdff',
};
export const darkTheme = {
MAIN: 'white',
SUB: 'black',
BACKGROUND: '#202124',
};
export type ColorTheme = typeof lightTheme;
-
프로젝트의 특성에 맞게
MAIN
,SUB
는 글자의 색상,BACKGROUND
는 배경색을 지정합니다. (더 필요한 색상이 있으면 추가합니다!) -
typeof
를 통해 ColorTheme type을 export 해줍니다.
2️⃣ useDarkMode() 구현
-
Next.js에서는 렌더링이 일어나기 전에 localStorage
, window
등에 접근하면 에러가난다. 그래서 useEffect
내부에서 window.localStorage.getItem('theme')
를 통해 새로고침 or 첫 렌더링시 테마를 결정하게 된다. (default value는 lightTheme
이다.)
-
처음으로 localStorage.setItem
을 하는 시기는 라이트모드 -> 다크모드를 처음했을때다.
// hooks/useDarkMode.ts
import { useEffect, useState } from 'react';
import { lightTheme, darkTheme, ColorTheme } from '../styles/theme';
export const useDarkMode = () => {
// 1. 초기 colorTheme은 lightTheme를 가진다.
const [colorTheme, setColorTheme] = useState<ColorTheme>(lightTheme);
// 4. state의 값도 변경 + local 저장 값도 변경
const setMode = (mode: ColorTheme) => {
mode === lightTheme
? window.localStorage.setItem('theme', 'light')
: window.localStorage.setItem('theme', 'dark');
setColorTheme(mode);
};
// 3. 사용자가 toggleColorTheme을 하면 setMode를 통해 기존의 colorTheme과 반대 값을 저장한다.
const toggleColorTheme = () => {
colorTheme === lightTheme ? setMode(darkTheme) : setMode(lightTheme);
};
// 2. 마운트 되면 localStorage에 'theme'이 있는지 찾는다.
// - 새로고침시 다크모드/라이트모드 바로 적용
// - 페이지 로드가 처음이면 이 과정은 무시된다
useEffect(() => {
const localTheme = window.localStorage.getItem('theme');
if (localTheme !== null) { // localTheme이 존재한다면
if (localTheme === 'dark') {
setColorTheme(darkTheme);
} else {
setColorTheme(lightTheme);
}
}
}, []);
return { colorTheme, toggleColorTheme };
};
3️⃣ darkmode 상태 관리
Next.js에서는 렌더링이 일어나기 전에 localStorage
, window
등에 접근하면 에러가난다. 그래서 useEffect
내부에서 window.localStorage.getItem('theme')
를 통해 새로고침 or 첫 렌더링시 테마를 결정하게 된다. (default value는 lightTheme
이다.)
처음으로 localStorage.setItem
을 하는 시기는 라이트모드 -> 다크모드를 처음했을때다.
// hooks/useDarkMode.ts
import { useEffect, useState } from 'react';
import { lightTheme, darkTheme, ColorTheme } from '../styles/theme';
export const useDarkMode = () => {
// 1. 초기 colorTheme은 lightTheme를 가진다.
const [colorTheme, setColorTheme] = useState<ColorTheme>(lightTheme);
// 4. state의 값도 변경 + local 저장 값도 변경
const setMode = (mode: ColorTheme) => {
mode === lightTheme
? window.localStorage.setItem('theme', 'light')
: window.localStorage.setItem('theme', 'dark');
setColorTheme(mode);
};
// 3. 사용자가 toggleColorTheme을 하면 setMode를 통해 기존의 colorTheme과 반대 값을 저장한다.
const toggleColorTheme = () => {
colorTheme === lightTheme ? setMode(darkTheme) : setMode(lightTheme);
};
// 2. 마운트 되면 localStorage에 'theme'이 있는지 찾는다.
// - 새로고침시 다크모드/라이트모드 바로 적용
// - 페이지 로드가 처음이면 이 과정은 무시된다
useEffect(() => {
const localTheme = window.localStorage.getItem('theme');
if (localTheme !== null) { // localTheme이 존재한다면
if (localTheme === 'dark') {
setColorTheme(darkTheme);
} else {
setColorTheme(lightTheme);
}
}
}, []);
return { colorTheme, toggleColorTheme };
};
props를 계속 넘겨주는 방식을 피하기 위해서 Context API를 사용합니다.
페이지를 이동해도 테마를 유지할 수 있도록 ThemeContext.Provider
를 통해서 하위 컴포넌트들이 Context를 통해 테마를 접근할 수 있도록합니다.
Next.js에서는 최상단이
_app
이기 때문에 여기서 설정을 하게 됩니다.
// pages/_app.tsx
import React, { createContext } from 'react';
import type { AppProps } from 'next/app';
import { Global } from '@emotion/react';
import { lightTheme, darkTheme, ColorTheme } from '../styles/theme';
// createContext 타입지정
interface ContextProps {
colorTheme: ColorTheme;
toggleColorTheme: () => void;
}
// Context 생성
export const ThemeContext = createContext<ContextProps>({
colorTheme: lightTheme, // 초기 값으로 lightTheme를 넣어줍니다.
toggleColorTheme: () => { // light || dark mode를 토글합니다.
return null
},
})
function MyApp({ Component, pageProps }: AppProps) {
// ❗️useDarkMode hook을 통해 theme과 toggleTheme return;
const { theme, toggleTheme } = useDarkMode();
return (
// Provider은 context의 변화를 알리는 역할을 합니다.
// toggleTheme를 통해 theme이 변경되면 하위 컴포넌트들은 모두 리렌더링됩니다.
<ThemeContext.Provider value={{ theme, toggleTheme }}>
<Component {...pageProps} />
</ThemeContext.Provider>
)
}
export default MyApp
4️⃣ 다크모드 사용 및 토글 버튼
-
useDarkMode
를 통해 생성되고 ThemeContext
로 전파가 된 ~colorTheme
, toggleColorTheme
를 useContext
를 사용해서 구독합니다.
-
버튼을 클릭하면 toggleColorTheme
을 작동합니다.
-
가져온 colorTheme
를 통해 css를 적용합니다.
// components/Header/HeaderBtns/DarkModeToggle/index.tsx
import React, { ReactElement, useContext } from 'react';
import { ThemeContext } from '../../../../pages/_app';
import styled from '@emotion/styled';
import { lightTheme, ColorTheme } from '../../../../styles/theme';
interface ToggleProps {
colorTheme: ColorTheme;
}
const DarkModeToggle = () => {
// 1. useContext를 통해서 colorTheme, toggleColorTheme를 구독한다
const { colorTheme, toggleColorTheme } = useContext(ThemeContext);
return (
// 2. 버튼을 클릭하면 toggleColorTheme을 작동한다
<ToggleButton onClick={toggleColorTheme} colorTheme={colorTheme}>
{colorTheme === lightTheme ? '다크 모드' : '라이트 모드'}
</ToggleButton>
);
}
// 3. colorTheme을 prop으로 가져와 css를 적용한다.
const ToggleButton = styled('button')<ToggleProps>`
display: flex;
color: ${({ colorTheme }) => colorTheme.MAIN};
cursor: pointer;
background: ${({ colorTheme }) => colorTheme.BACKGROUND};
box-shadow: 3px 3px 10px rgb(0 0 0 / 20%);
&:hover {
filter: brightness(${({ colorTheme }) => (colorTheme === lightTheme ? '0.9' : '1.13')});
}
`;
export default DarkModeToggle;
Author And Source
이 문제에 관하여([React.js] 다크모드 (Emotion.js + Next.js + TypeScript)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다
https://velog.io/@minbr0ther/React.js-다크모드-Emotion.js-Next.js-TypeScript
저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)
useDarkMode
를 통해 생성되고 ThemeContext
로 전파가 된 ~colorTheme
, toggleColorTheme
를 useContext
를 사용해서 구독합니다.
버튼을 클릭하면 toggleColorTheme
을 작동합니다.
가져온 colorTheme
를 통해 css를 적용합니다.
// components/Header/HeaderBtns/DarkModeToggle/index.tsx
import React, { ReactElement, useContext } from 'react';
import { ThemeContext } from '../../../../pages/_app';
import styled from '@emotion/styled';
import { lightTheme, ColorTheme } from '../../../../styles/theme';
interface ToggleProps {
colorTheme: ColorTheme;
}
const DarkModeToggle = () => {
// 1. useContext를 통해서 colorTheme, toggleColorTheme를 구독한다
const { colorTheme, toggleColorTheme } = useContext(ThemeContext);
return (
// 2. 버튼을 클릭하면 toggleColorTheme을 작동한다
<ToggleButton onClick={toggleColorTheme} colorTheme={colorTheme}>
{colorTheme === lightTheme ? '다크 모드' : '라이트 모드'}
</ToggleButton>
);
}
// 3. colorTheme을 prop으로 가져와 css를 적용한다.
const ToggleButton = styled('button')<ToggleProps>`
display: flex;
color: ${({ colorTheme }) => colorTheme.MAIN};
cursor: pointer;
background: ${({ colorTheme }) => colorTheme.BACKGROUND};
box-shadow: 3px 3px 10px rgb(0 0 0 / 20%);
&:hover {
filter: brightness(${({ colorTheme }) => (colorTheme === lightTheme ? '0.9' : '1.13')});
}
`;
export default DarkModeToggle;
Author And Source
이 문제에 관하여([React.js] 다크모드 (Emotion.js + Next.js + TypeScript)), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@minbr0ther/React.js-다크모드-Emotion.js-Next.js-TypeScript저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)