styled-components
์ ๋ค์ํ ๊ธฐ๋ฅ์ ๋ํด ๋ ์ ์ ์์๋
๋คํฌ ๋ชจ๋์ค์์น ์์
ํํธ.
๋น์์ ๊ธํ๊ฒ ์ฒ๋ฆฌํ๋๋ผ ThemeProvider
๋ฅผ ์ฌ์ฉํ์ง๋ง,
์ฌ์ฉํ๊ธฐ ๊ธ๊ธํด์ ์ ๋๋ก ์ ๋ฆฌ๊ฐ ์๋ ๊ฒ ๊ฐ์์
๋ค์ ๊ณต๋ถํ๋ฉฐ ๋ฆฌํฉํ ๋งํ๊ณ ์ ๋ฆฌํ๊ธฐ!
๋คํฌ๋ชจ๋๋ฅผ ๊ตฌํํ๋ ์์ธํ ๋ฐฉ๋ฒ๋ณด๋จ ์ค์ ๋๋ ๊ณผ์ ์ ์ง์คํด ์ ๋ฆฌํ์์ต๋๋ค
์ต๊ทผ ์น์ด๋ ์ฑ์ ๋ณด๋ฉด ํ๊ฒฝ์ ๋ฐ๋ผ ๋์ ํผ๋ก๋๋ฅผ ๋ฎ์ถ ์ ์๋๋ก
๋คํฌ๋ชจ๋๋ฅผ ์ง์ํ๋ ๊ฒ์ ๋ง์ด ๋ณด์๋ค.
๊ทธ๋์ ๋๋ ํ๋ก์ ํธ์ ๋ง์ง๋ง์ ์ถ๊ฐ ํด๋ณด๊ฒ ๋์๋ค.
์๋ฌด๋๋ ์คํ์ผ๋ง์ ์ผ๋ก ์กฐ์ ํด์ผ ํ๋๊ฒ ๋ง์์ ๋ง์ง๋ง์ ํ๋๊ฒ ๋์๋ ๊ฒ ๊ฐ๋ค :)
์ฌ๋ฌ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๊ฒ ์ง๋ง, ์คํ์ผ๋ง์ ์ฝ๊ฒ ํ๊ธฐ ์ํด์
styled-components
์ ThemeProvider
๋ฅผ ํตํด ์ ์ญ์ผ๋ก ์คํ์ผ๋ง์ด
๋ณ๊ฒฝ๋ ์ ์๋๋ก ๊ด๋ฆฌํ๊ธฐ๋ก ํ๋ค.
useThemeMode
๋ผ๋ ์ปค์คํ
ํ
์ ๋ง๋ค์ด์ localStorage
์ ๊ฐ์ ์ ์ฅํ์ฌ
์๋ก๊ณ ์นจ์ ํ๋๋ผ๋ ์ค์ ํ ๋ชจ๋๊ฐ ์ ์ง๋ ์ ์๋๋ก ์ค์ ํ๋ค.
๊ทธ๋ฐ ๋ค์,
App.tsx
์์ ์ปค์คํ
ํ
์ ๋ถ๋ฌ์์ ๋ชจ๋์ ๊ดํ ๊ฐ์
ThemeProvider
์ ์ ๋ฌํ๊ณ ,
๋ชจ๋๋ฅผ ๋ณ๊ฒฝ์ํฌ ์ ์๋ ํจ์๋ ๋ชจ๋์ค์์น ๋ฒํผ์ ์ ๋ฌํด์
์คํ์ผ๋ง์ด ๋ณ๊ฒฝ๋๊ฒ ํ๋ค.
ํ
๋ง ๋ชจ๋ ์ค์ ์ ์ํ useThemeMode
๋ผ๋ ํ
์ ๋ง๋ค์๋ค.
์ํ๊ฐ์ธ themeMode
์ boolean
ํ์ผ๋ก
true์ผ ๋ ๋คํฌ๋ชจ๋, false์ผ ๊ฒฝ์ฐ์ ๋ผ์ดํธ๋ชจ๋๋ก ์ค์ ๋๊ฒ ํ๋ค.
const useThemeMode = () => {
// ๋ธ๋ผ์ฐ์ ์ ํ
๋ง์ ๋ณด ํ์ธ
const isBrowserDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme : dark').matches;
// ๋ธ๋ผ์ฐ์ ์ ํ
๋ง์ ๋ณด๋ฅผ ํ ๋๋ก ์ด๊ธฐ ์ํ๊ฐ ์ค์
let initialTheme = isBrowserDarkMode;
const [themeMode, setThemeMode] = useState<boolean>(initialTheme);
// ์ฌ์ฉ์ ์ง์ ํ
๋ง๊ฐ ์๋์ง ํ์ธ ํ ์์ผ๋ฉด ํด๋น ํ
๋ง๋ก ์ค์ , ์๋ค๋ฉด ๋ธ๋ผ์ฐ์ ํ
๋ง๋ก ์ค์
const localSettingTheme = Boolean(localStorage.getItem("themeMode"));
if(localSettingTheme) {
initialTheme = localSettingTheme;
}
const setMode = (mode : boolean) => {
localStorage.setItem("themeMode", JSON.stringify(mode));
setThemeMode(mode);
}
// themeMode ๋ณ๊ฒฝ ์๋ง๋ค localStorage์ ์ค์ ๋๋ฉด์ ํ
๋ง ๋ณ๊ฒฝ๋๊ฒ ํ๋ ํจ์ ์์ฑ
const switchThemeMode = () => {
setMode(!themeMode);
}
return {
themeMode,
switchThemeMode
}
export default useThemeMode;
์ฌ๊ธฐ์ ๋ช๊ฐ์ง ํ์ธํด ๋ณผ ์ฝ๋๋,
matchMedia()
๋ฅผ ํตํด ๋ธ๋ผ์ฐ์ ์ ํ
๋ง๋ชจ๋๋ฅผ ํ์ธํ๋ ๊ฒ์ด๋ค.
๐
window.matchMedia()
๋?
์ฃผ์ด์ง ๋ฏธ๋์ด ์ฟผ๋ฆฌ ๋ฌธ์์ด์ ๋ถ์ ๊ฒฐ๊ณผ๋ฅผ ๋ํ๋ด๋MediaQueryList
๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค.
์ด๋ฒ ์์ ์์ ์ฌ์ฉํprefers-color-scheme : dark
๋ ์ฌ์ฉ์์ ์์คํ ์ด ๋คํฌ ํ ๋ง๋ฅผ ์ฌ์ฉํ๋์ง๋ฅผ ํ์งํ๋ ์ญํ ์ ํ๋ค.
์ดํ, Boolean()
ํจ์๋ฅผ ์ฌ์ฉํด์
์ฌ์ฉ์๊ฐ ์ง์ ํ ํ
๋ง๊ฐ ์๋์ง๋ฅผ ํ์ธํ๋ ์์
์ ๊ฑฐ์น ํ
์๋ค๋ฉด ํด๋น ํ
๋ง๋ก, ์๋ค๋ฉด ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ํ
๋ง๋ก ์ค์ ํ๊ฒ ํ๋ค.
๐ Boolean ํจ์๋?
- ๋ค๋ฅธ ์๋ฃํ์ booleanํ์ผ๋ก ๋ณํํ ๋ ์ฌ์ฉํ๋ ํจ์
- false :
0
,''
,null
,undefined
,NaN
- true : false๋ฅผ ์ ์ธํ ๊ฒฝ์ฐ, ์ฐธ์ธ ํํ์
localStorage
์ themeMode
๋ฅผ ์ค์ ํ ๋๋,
key
๊ฐ๊ณผ value
๊ฐ์ด ๋ชจ๋ string
์ด์ด์ผ ํ๋๋ฐ
๋๋ themeMode
๊ฐ์ boolean
ํ์ผ๋ก ์ค์ ํ๊ธฐ ๋๋ฌธ์
JSON.stringify
๋ก string
ํ ์์
์ ๊ฑฐ์ณ์ฃผ์๋ค.
ThemeProvider
๋ react์ context API
๋ฅผ ์ฌ์ฉํด์
์ ์ญ์ ์ผ๋ก ์คํ์ผ๋ง์ ๋ณ๊ฒฝํ ์ ์๋๋ก styled-components
์์
์ ๊ณตํ๋ ์ ์ญ์คํ์ผ๋ง ๊ธฐ๋ฅ์ด๋ค.
๋ฐ๋์ theme
์์ฑ์ ๊ฐ๋ ์ปดํฌ๋ํธ๋ก,
theme
์์ฑ์ ๊ฐ์ผ๋ก ์ค์ ๋ ๊ฐ์ฒด๋ฅผ
์ผ์ผ์ด ๋๊ฒจ์ค ํ์ ์์ด ํ๋ฒ์ ๋ชจ๋ ํ์ ์ปดํฌ๋ํธ๋ค์ props
๋ก ์ ๋ฌํ๋ค.
์์์ ์์ฑํ ์ปค์คํ
ํ
๊ณผ ํจ๊ป ThemeProvider
๋ฅผ ์ฌ์ฉํด๋ณด์.
...
import { ThemeProvider } from "styled-components";
import { darkTheme, lightTheme } from "./globals/theme";
import useThemeMode from "./hooks/useThemeMode";
import SwitchButton from "./components/SwitchButton";
function App() {
const {themeMode, switchThemeMode} = useThemeMode();
return (
<ThemeProvider theme={themeMode ? darkTheme : lightTheme}>
<SwitchButton changeTheme={switchThemeMode} isDark={themeMode} />
</ThemeProvider>
);
}
export default App;
์ด๋ ๊ฒ ํ๋ฉด ThemeProvider
์ theme
์ ํตํด
์ปค์คํ
ํ
์ ์ ๊ทผํด themeMode
๋ฅผ ๊ฐ์งํ๋ฉฐ
์ค์์น ๋ฒํผ์ด ํด๋ฆญ ๋ ๊ฒฝ์ฐ์ ํจ์๊ฐ ๋ฐ๋๋๋ฉด์
๋ชจ๋๊ฐ ๋ณ๊ฒฝ์ด ๋๊ณ ,
ThemeProvider
๊ฐ ๊ฐ์ธ๊ณ ์๋ ํ์ ์ปดํฌ๋ํธ๋ค์ ์คํ์ผ๋ง์ด
๋ด๊ฐ ๊ตฌ๋ถํด๋ ๋๋ก ๋ชจ๋ ๋ณ๊ฒฝ๋๋ค.
๋๋ฆ ์ดํดํ๋๋ก ์ ๋ฆฌํ๋๋ฐ ํน์๋ ํ๋ฆฐ ๋ถ๋ถ์ด ์๋ค๋ฉด ๋๊ธ๋ก ์๋ ค์ฃผ์ธ์!
์ฆ๊ฒ๊ฒ ๋ค์ ๊ณต๋ถํ๊ฒ ์ต๋๋ค ๐ค
๐ ์ฐธ๊ณ ํ์์ต๋๋ค :)