데브코스/도서 판매 쇼핑몰

[FE] 테마 변경하기(light, dark 모드)

hxx_1 2024. 11. 11. 21:04

🌏global style

1. global = 프로젝트 전체에 적용 = 프로젝트에 일관된 스타일링을 적용

2. "user agent stylesheet" 로 표시되는 브라우저의 기본 스타일이 차이를 만든다.

3. 브라우저 간의 스타일 차이를 극복하기 위해 사용 

 

❓css-in-js 는 왜 필요할까

: CSS-in-JS는 전통적인 CSS 파일 대신 JavaScript 안에서 직접 스타일을 작성하는 방식을 말한다.

 

1. 전역 충돌

2. 의존성 관리 어려움

3. 불필요한 코드, 오버라이딩

4. 압축

5. 상태 공유 어려움

6. 순서와 명시도

7. 캡슐화 

 

<사용 예시>

import styled from 'styled-components';

const Button = styled.button`
  background-color: blue;
  color: white;
  padding: 10px;
  
  &:hover {
    background-color: darkblue;
  }
`;

 

테마

1. UI, UX 일관성 유지

2. 유지보수가 용이

3. 확장성

4. 재사용성

5. 사용자 정의 


👩🏻‍💻 테마 변경하기(light, dark 모드) 

App.tsx

import Layout from "./components/layout/Layout";
import Home from "./pages/Home";
import ThemeSwitcher from "./components/header/ThemeSwitcher";
import { BookStoreThemeProvider} from "./context/themeContext";
function App() {
  return (
    <BookStoreThemeProvider>
        <ThemeSwitcher />
        <Layout>
          <Home />
        </Layout>
    </BookStoreThemeProvider>
  );
}

export default App;

 

 

BookStoreThemeProvider

 

① 상수 및 인터페이스 정의

const DEFAULT_THEME_NAME = "light";
const THEME_LOCALSTORAGE_KEY = "book_store_theme";

interface State {
  themeName: ThemeName;
  toggleTheme: () => void;
}
  • 기본 테마는 'light'로 설정
  • localStorage에서 테마 설정을 저장할 때 사용할 키 정의
  • State 인터페이스로 테마 관련 타입 정의

②  Context 생성

export const ThemeContext = createContext<State>(state);
  • React Context API를 사용하여 테마 상태를 전역적으로 관리할 수 있는 컨텍스트 생성

③ BookStoreThemeProvider 컴포넌트

export const BookStoreThemeProvider = ({children}: {children:ReactNode})
  • 테마 관리를 위한 프로바이더 컴포넌트
  • children prop을 통해 하위 컴포넌트들을 감싸는 구조

④ 테마 상태 관리

const [themeName, setThemeName] = useState<ThemeName>(DEFAULT_THEME_NAME);

const toggleTheme = () => {
  setThemeName(themeName === "light"? "dark" : "light");
  localStorage.setItem(THEME_LOCALSTORAGE_KEY, themeName === "light" ? "dark" : "light");
}
  • useState로 현재 테마 상태 관리
  • toggleTheme 함수로 라이트/다크 모드 전환
  • 테마 변경 시 localStorage에 저장

⑤ 초기 테마 설정

useEffect(() => {
  const savedThemeName = localStorage.getItem(THEME_LOCALSTORAGE_KEY) as ThemeName;
  setThemeName(savedThemeName || DEFAULT_THEME_NAME);
},[]);
  • 컴포넌트 마운트 시 localStorage에서 저장된 테마 설정을 불러옴
  • 저장된 설정이 없으면 기본 테마 사용

⑥ 컴포넌트 렌더링

return (
  <ThemeContext.Provider value={{themeName, toggleTheme}}>
    <ThemeProvider theme={getTheme(themeName)}>
      <GlobalStyle themeName={themeName} />
      {children}
    </ThemeProvider>
  </ThemeContext.Provider>
)
  • ThemeContext.Provider로 테마 상태와 토글 함수를 하위 컴포넌트에 제공
  • styled-components의 ThemeProvider로 테마 스타일 적용
  • GlobalStyle 컴포넌트로 전역 스타일 적용
  • children을 통해 전달된 하위 컴포넌트들을 렌더링

➡️ Provider 란?

Provider는 createContext()로 생성한 컨텍스트의 기본 제공 컴포넌트이며, 이를 통해 하위 컴포넌트들에게 데이터를 전달할 수 있다. 

 

이렇게 Provider로 감싸진 컴포넌트들은:

  • Provider의 value prop으로 전달된 값에 접근 가능
  • value 값이 변경되면 자동으로 다시 렌더링됨
  • 중첩해서 사용 가능(이 경우 가장 가까운 Provider의 값이 우선됨)

createContext

: 전역적으로 데이터를 공유할 수 있는 Context를 생성하는 함수

import { createContext } from 'react';
const ThemeContext = createContext('light'); // 기본값 지정
  • 컴포넌트 외부에서 생성해야 함
  • Provider와 Consumer를 자동으로 생성
  • 기본값(defaultValue)을 설정할 수 있음

useContext

: createContext로 만든 Context의 값을 읽어오는 Hook

function MyComponent() {
  const theme = useContext(ThemeContext);
  return <div className={theme}>...</div>;
}
  • props drilling 문제 해결
  • 전역 상태 관리 가능
  • 컴포넌트 트리 깊숙이 데이터 전달 가능

useEffect

: 컴포넌트의 부수 효과(side effects)를 처리하는 Hook

useEffect(() => {
  // 실행할 부수 효과
  return () => {
    // 클린업 함수 (선택사항)
  }
}, [dependencies]); // 의존성 배열
  • 컴포넌트 렌더링 후 실행됨
  • 의존성 배열이 변경될 때마다 재실행
  • 데이터 페칭, 구독 설정, DOM 조작 등에 사용

🌟 배운 점

오늘은 라이트 모드와 다크 모드로 테마를 변경하는 것을 구현했다. 리액트를 잘 알지 못하다보니, 훅이나 api 나 사용법을 아직 잘 모르겠다. 그리고 코드를 이해하는 데에도 시간이 많이 걸리는 것 같다. 강의를 들으면서 거의 클론 코딩을 하고 있는데 따라하는 것에 급급하기 보다는 이해하는 데 중점을 둬야겠다.