๋คํฌ๋ชจ๋ ๊ตฌํ ํ๊ธฐ (react, reduxToolkit, styled components)
- -
๐ ์์
์์ ๋ถํฐ ๋คํฌ ๋ชจ๋๋ฅผ ๊ตฌํํ๊ณ ์ถ์๋ค.
์ด๋ฒ ๊ธฐํ์ ๊ณต๋ถํ ์ ์์๋ ๊ณ๊ธฐ๊ฐ ๋์๋ค.
์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์์ง๋ง body์ ํ
๋ง ์คํ์ผ์ ์ง์ ํ๊ณ
<ThemeProvider>์ theme์์ฑ์ ํ์ฉํ๋ ๋์ค์ ์ธ ๋ฐฉ๋ฒ์ ์ ํํ๊ฒ ๋์๋ค.
ํ ๊ธ ๋ฒํผ ํด๋ฆญ ์ ํ
๋ง๊ฐ ๋ณ๊ฒฝ๋๊ณ
์๋ก๊ณ ์นจ ํด๋ ์ ์ง๋์ด์ผ ํ๊ธฐ์ localStorage๋ ๊ฐ์ด ํ์ฉํ๋ค.
๐ฅน ๋ฌธ์
์ ์ญ์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ ๊น ํ์ง๋ง
localStorage์ ์ ์ฅํ๊ธฐ์ ํ ํ์๊ฐ ์๋ค ์๊ฐํ๋ค.
const themeMode = localStorage.getItem('THEME');
const handlerTheme = () => {
if (themeMode === 'dark') {
localStorage.setItem('THEME', 'light');
} else {
localStorage.setItem('THEME', 'dark');
}
};
function MyApp() {
const themeMode = locaStorage.getItem('THEME');
return (
<ThemeProvider theme={themeMode === 'dark' ? dark : light}>
<GlobalStyle />
<App />
</ThemeProvider>
);
}
์ฐ์ ์ hook ๋ฑ์ ๋ฐ๋ก ์ฌ์ฉํ์ง ์๊ณ ๋ค์ด๋ ํธ๋ก ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์๋ค.
๊ด๋ฆฌ์ ๋ชจ๋๋ก localStorage๋ฅผ ํ์ธํ๋ฉด ํ ๊ธ ๊ธฐ๋ฅ์ ์ ์๋ํ๋ค.
ํ์ง๋ง ํ
๋ง ์คํ์ผ์ด ๋ณ๊ฒฝ๋์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
(์ญ์ ์ธ์์ ํธ๋ฝํธ๋ฝํ์ง ์๋ค..๐ฅน)
๋ด๊ฐ ์๊ฐํ์ ๋์ ๋ฌธ์ ๋
localStorage์ ๊ฐ์ ๊ฐ์งํ๊ณ ๋ฆฌ๋ ๋๋งํด์ผ ํ๋๋ฐ ๊ทธ๋ฌ์ง ๋ชปํ๋ ๊ฒ ๊ฐ๋ค.
๋ํ Redux Toolkit๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉํด ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ ์์ด
Redux Toolkit์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ localStorage๋ฅผ ์
๋ฐ์ดํธํ๊ธฐ๋ก ํ๋ค.
โจ ๊ตฌํ
๊ทธ๋ฌ๋ค ํด๋น ๊ฒ์๋ฌผ์ ์ฐธ๊ณ ํ๊ฒ ๋์๋ค.
์ฐธ๊ณ : https://velog.io/@velopert/velog-dark-mode
์ฐธ๊ณ ๊ฒ์๋ฌผ์์๋ ๋ธ๋ผ์ฐ์ ์์คํ ์ธํ ๋ ๊ณ ๋ คํ์ ๋ชจ์์ด๋ค.
์ด๋ถ๋ถ์ ์ดํ ๋ฆฌํฉํ ๋ง ๋จ๊ณ์์ ์๋ํด ๋ณด๊ธฐ๋ก ํ๋ค.
์ถํ ๋ค์ ์ ๋ ํ๊ณ ์ฝ๋๋ฅผ ๋ฆฌํฉํฐ๋ง ํด๋ด์ผ๊ฒ ๋ค.
0. style ์ค์
//styles/theme.ts
export const light = {
colors: {
bgColor: '#fff',
boxColor: '#fff',
textColor: '#000',
iconColor: '#414443',
iconBoxColor: '#f1f1f1',
},
};
export const dark = {
colors: {
bgColor: '#222',
boxColor: '#444',
textColor: '#FDFDFD',
iconColor: '#FDFDFD',
iconBoxColor: '#444',
},
};
light, dark ๋ ๋ค ํค ๊ฐ ์ด๋ฆ์ด ๋์ผํด์ผ ํ๋ค.
// styles/GlobalStyles
:root {
--bg-color: ${(props) => props.theme.colors.bgColor};
--box-color: ${(props) => props.theme.colors.boxColor};
--text-color: ${(props) => props.theme.colors.textColor};
--icon-color: ${(props) => props.theme.colors.iconColor};
--icon-box-color: ${(props) => props.theme.colors.iconBoxColor};
}
.
.
.
body{
background-color: var(--bg-color);
}
css ํ์์ผ๋ก ์ฌ์ฉํ๊ธฐ ํธํ๊ฒ ํ๋ค.
์ด์ styled components์ ์ฌ์ฉํ ๋ css๋ฐฉ์์ฒ๋ผ ์์ฑํด ์ฃผ๋ฉด ๋๋ค.
const TextColor = css`
color: var(--text-color);
`;
1. slice ์ธํ
// slice.ts
import { createSlice } from '@reduxjs/toolkit';
type initialStateT = {
themeMode: string;
};
const toggleThemeMode = localStorage.getItem('THEME');
const initialState: initialStateT = {
themeMode: toggleThemeMode ? toggleThemeMode : 'light',
};
const themeSlice = createSlice({
name: 'themetype',
initialState,
reducers: {
toggleDarkMode(state) {
state.themeMode = 'dark';
},
toggleLightMode(state) {
state.themeMode = 'light';
},
},
});
export const themeReducer = themeSlice.reducer;
export const themeActions = themeSlice.actions;
๋คํฌ๋ชจ๋, ๋ผ์ดํธ๋ชจ๋๋ฅผ ์ ์ญ์์ ๊ด๋ฆฌํ ์ ์๋๋ก ํ๋ค.
const initialState: initialStateT = {
themeMode: toggleThemeMode ? toggleThemeMode : 'light',
};
๋ํ themeMode๋ localStorage์ ๊ฐ์ด ์์ ๊ฒฝ์ฐ์๋ง ํ ๋น๋๊ณ ์์ ๊ฒฝ์ฐ์ ๊ธฐ๋ณธ์ ์ผ๋ก light ํ ๋ง๋ฅผ ์ ๊ณตํ๋๋ก ํ๋ค.
2. ํ ๊ธ ๋ฒํผ ์์
const Nav = () => {
const dispatch = useDispatch();
const themeMode = useSelector((state: RootState) => state.themeType.themeMode);
const themeSave = (value: 'light' | 'dark') => {
localStorage.setItem('THEME', value);
};
const handlerTheme = () => {
if (!themeMode) return;
if (themeMode === 'dark') {
dispatch(themeActions.toggleLightMode());
themeSave('light');
} else {
dispatch(themeActions.toggleDarkMode());
themeSave('dark');
}
};
return (
<NavContainer>
<MenuList>
<li>
<SearchBtn />
</li>
<li>
<ThemeBtn handlerTheme={handlerTheme} />
</li>
</MenuList>
</NavContainer>
);
};
์ฒ์์๋ ThemeBtn์ปดํฌ๋ํธ ๋ด๋ถ์ ์์ฑํ ๊น ํ์ง๋ง
์ฌ์ฌ์ฉ์ฑ์ ๊ณ ๋ คํ๊ณ ๊ด๋ฆฌํ๊ธฐ ์ฝ๋๋ก Nav ์ปดํฌ๋ํธ์ ์์ฑํ๋ค.
(์ถํ ์ปค์คํ
ํ
์ผ๋ก ์์
ํ ์์ ์ด๋ค.)
handlerTheme ํจ์์์ themeMode์ ๊ฐ์ด 'dark' ๋๋ 'light'์ธ์ง ์กฐ๊ฑด๋ฌธ์ ์์ฑํด
๊ฐ์ ํ ๋นํ๊ณ ,
themeSave ํจ์์ ์ธ์๋ก ์ ๋ฌํด localStorage์ ๊ฐ๋ ๋ณ๊ฒฝํ๋ค.
3. ThemeProvider ํ ๋ง ์ฐ๊ฒฐํ๊ธฐ
function MyApp() {
const themeMode = useSelector((state: RootState) => state.themeType.themeMode);
return (
<ThemeProvider theme={themeMode === 'dark' ? dark : light}>
<GlobalStyle />
<App />
</ThemeProvider>
);
}
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<QueryClientProvider client={queryClient}>
<Provider store={store}>
<MyApp />
</Provider>
</QueryClientProvider>
</React.StrictMode>,
);
์ ์ญ์ ThemeProvider๋ฅผ ๊ฐ์ธ์ฃผ๊ณ theme์์ฑ์ ์ถ๊ฐํด ์ค๋ค.
useSelector๋ฅผ ์ฌ์ฉํด ์ํ ๊ฐ์ ๊ฐ์ ธ์จ๋ค.
์์ฑ!
๋ฒํผ ํด๋ฆญ ์ localStorage์ ๊ฐ์ด ์ ์ฅ๋๊ณ ๊ฐ์ ๋ฐ๋ผ ํ
๋ง๊ฐ ๋ณ๊ฒฝ๋๋ค.
localStorage์ ์ ์ฅ๋๊ธฐ์ ์ ๋ก๊ณ ์นจํด๋ ๊ฐ์ด ์ ์ง๋๋ค.
์ด์ ์ปค์คํ
ํ
์ผ๋ก ๋ง๋ค์ด ์ฌ์ฌ์ฉ์ฑ์ ๋์ฌ ๋ณด์!
๐ฑ ๋ฒ์ธ
์ปค์คํ ํ ์ผ๋ก ๋ง๋ค๊ธฐ
// hooks/useToggleTheme.ts
import { useDispatch, useSelector } from 'react-redux';
import { RootState } from '../store';
import { themeActions } from '../store/theme-slice';
const useToggleTheme = () => {
const dispatch = useDispatch();
const themeMode = useSelector((state: RootState) => state.themeType.themeMode);
const themeSave = (value: 'light' | 'dark') => {
localStorage.setItem('THEME', value);
};
const handlerTheme = () => {
if (!themeMode) return;
if (themeMode === 'dark') {
dispatch(themeActions.toggleLightMode());
themeSave('light');
} else {
dispatch(themeActions.toggleDarkMode());
themeSave('dark');
}
};
return handlerTheme;
};
export default useToggleTheme;
์ปค์คํ
ํ
์ ๋ง๋ค ๋ React์์ ์ ๊ณตํ๋ ๋ค๋ฅธ ํ
๋ค์ฒ๋ผ
์ด๋ฆ ์์ use๋ฅผ ๋ถ์ฌ ์ค์ผ ํ๋ค.
์ปค์คํ
ํ
์ ๋ง๋ค๊ณ handlerThemeํจ์๋ฅผ ๋ฐํํด ์ค๋ค.
๋ฐํํ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ ๋ผ๋ฉด ๋ฐฐ์ด ํ์ [...,...,...]์ผ๋ก ๋ฐํํ๋ฉด ๋๋ค.
const Nav = () => {
const handlerTheme = useToggleTheme();
return (
<NavContainer>
<MenuList>
<li>
<SearchBtn />
</li>
<li>
<ThemeBtn handlerTheme={handlerTheme} />
</li>
</MenuList>
</NavContainer>
);
};
์ด์ ์ ์ฝ๋๋ฅผ ์ง์ฐ๊ณ const handlerTheme = useToggleTheme();๋ง ์์ฑํด ์ฝ๋๊ฐ ๊น๋ํด์ก๋ค.
โ๏ธ ๋ง์น๋ฉฐ
์ฌ์ค ์์ง ๋ฏธํกํ ๋ถ๋ถ์ด ๋ง๋ค.
๊ณต๋ถํ๋ ์ฐจ์์ ์งํํด ๋ดค๋๋ฐ ํ๋ํ๋ ๊ตฌํํด ๋ณด๋ ์พ๊ฐ์ด ๋ค์๋ค ใ
ใ
์ฌ์ฉ์ ๊ฐ์ธ์ด ์ธํ
ํ ํ
๋ง ์ค์ ๋ฑ์ ์์
์ ์ ํด์ ์ถํ ๋ก์ง ์์ ์ด ํ์ํ์ง๋ง
๊ตฌํํ๋ค๋ ์ ์ ์๋ฏธ๋ฅผ ๋๋ ค๊ณ ํ๋ค.
ํด๋น ํ ์ด ํ๋ก์ ํธ๊ฐ ๋ง๋ฌด๋ฆฌ๋๋ฉด ๋ฆฌํฉํ ๋ง ์ฐจ์์์ ์งํํด ๋ณด๋ ค๊ณ ํ๋ค.
์ฌ๋ฌ ๋ฐฉ๋ฒ์ด ์กด์ฌํ๊ณ ๋ด๊ฐ ์ฐธ๊ณ ํ ๊ฒ์๋ฌผ๋ css๋ฅผ ์ด์ฉํด ์งํํ์ จ๋ค.
์๋๋ ๋ฒจ๋ก๊ทธ์ ๋ธ๋ก๊ทธ๋ฅผ ์์ฑํ์ง๋ง...
๊ฐํน ์๋์ผ๋ก ๋น๊ณต๊ฐ ์ฒ๋ฆฌ๋๋ ๋ฌธ์ ๊ฐ ์๋ค..ใ
์ฐ์ ํฐ์คํ ๋ฆฌ์ ์
๋ก๋๋ ๊ฐ์ด ํ๋ ค๊ณ ํ๋ค.
์์คํ ๊ณต๊ฐ ๊ฐ์ฌํฉ๋๋ค :)