클론코딩 #5 | State Management (1)

HyeonWooGa·2022년 6월 28일
0

클론코딩

목록 보기
6/20

- State Management

대부분의 선배 개발자 분들이 React 에서 Recoil, Redux 같은 state management library 를 사용해야 한다고 하는데 아직 그 이유를 몰라서 그 이유에 대해서 공부 해보겠습니다

그래서 다크모드와 라이트모드를 바꾸는 스위치 기능을 그냥 만들어보고 Recoil 을 이용해 만들어 보면서 왜 recoil, state management 가 필요한지, 기술은 문제를 해결하기 위해 만들어지는 거라는 것을 명심하며 공부해 보겠습니다.

state management 없이 만드려면 다크모드/라이트모드 스위치를 만드려면 두가지를 바꿔야합니다.

우선 이전에 만들어둔 코인시세 트래커 서비스 App.tsx 파일에 useState 를 사용하여 지금 Dark 모드인지 아닌지 상태 파악 할 수 있게 해줍니다.

import { createGlobalStyle, ThemeProvider } from "styled-components";
import Router from "./Router";
import { ReactQueryDevtools } from "react-query/devtools";
import { useState } from "react";
import { darkTheme, lightTheme } from "./theme";

...

function App() {
  const [isDark, setIsDark] = useState(false);
  return (
    <>
      <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
        <GlobalStyle />
        <Router>
        <ReactQueryDevtools initialIsOpen={true} />
      </ThemeProvider>
    </>
    );
}

여기에 const toggleDark 로 function 을 만들고 내부에서 setIsDark 를 사용합니다.
setState function 을 사용할때 두 가지 옵션 중에서 function 보내는 옵션을 사용합니다.
(1. value 를 그냥 보내는 것, 2. function 을 보내는 것)


...

function App() {
  const [isDark, setIsDark] = useState(false);
  const toggleDark = () => setIsDark(true
  return (
    ...
    );
}

그리고 잘 동작하는지 확인하기 위해서 "Toggle Mode" 버튼을 위에 만든 setState function(toggleDark)을 onClick으로 이벤트 펑션으로 받으며 작성합니다.


...

function App() {
  const [isDark, setIsDark] = useState(false);
  const toggleDark = () => setIsDark(true
  return (
    ...
    <button onClick={toggleDark}>Toggle Mode</button>
    ...
    );
}

위에서 다크모드/라이트모드 기능이 구현되는 것을 확인 하였으니 이제 토글의 위치를 수정해주고 메인화면의 Text, Box 들 뿐아니라 Chart 에서도 적용 되도록 Chart 컴포넌트(Chart.tsx) 에서도 Props 를 전달 해줘야 합니다.

  1. 토글의 위치를 메인화면 헤더 타이틀 옆으로 수정

    <Header>
          <Title>Coins</Title>
          <button onClick={toggleDark}>Toggle Dark Mode</button>
     </Header>
  2. App 컴포넌트에서 만들어놓은 toggleDark function 을 메인화면 컴포넌트(Coins.tsx) 에 전달해줘야 하고 메인화면 컴포넌트는 Router 내부에 있기 때문에 이 경우 function 을 Router 로 보내야 합니다. 그리고 TypeScript 를 사용중이기 때문에 interface 도 생성해줍니다.

    import { HashRouter, Switch, Route } from "react-router-dom";
     import Coin from "./routes/Coin";
     import Coins from "./routes/Coins";
    
     interface IRouterProps {
       toggleDark: () => void;
     }
    
     function Router({ toggleDark }: IRouterProps) {
       return (
         <HashRouter>
           <Switch>
             <Route path="/:coinId">
               <Coin />
             </Route>
             <Route path="/">
               <Coins toggleDark={toggleDark} />
             </Route>
           </Switch>
         </HashRouter>
       );
     }
    
     export default Router;
  3. Router 에서 메인페이지 컴포넌트로 toggleDark 상태판단 함수를 또 보내줍니다. 또 interface 도 동일하게 생성해줍니다.

    interface ICoinsProps {
       toggleDark: () => void;
     }
    
     function Coins({ toggleDark }: ICoinsProps) {
       const { isLoading, data } = useQuery<ICoin[]>("allCoins", fetchCoins); // hook
       return (
         <Container>
           <Helmet>
             <title>Coins</title>
           </Helmet>
           <Header>
             <Title>Coins</Title>
             <button onClick={toggleDark}>Toggle Dark Mode</button>
           </Header>

    현재는 global state management 라이브러리 없이 진행하다보니 props 를 두 단계나 아래로 보내고 있는 상황이 발생했습니다.

  4. 그리고 Chart 컴포넌트에 다크모드/라이트모드를 적용하기 위해서 위의 과정을 또 반복해야합니다. 추가로 상태값인 isDark 도 보내줘야합니다.

    따라서 위에 했던 과정에서 toggleDark function 만 보내는 것이 아니라 상태값 isDark 도 보낼 수 있도록 추가해주고 isDark 를 Chart 컴포넌트까지 내려준 후 다크모드/라이트모드 변경이 가능하도록 작성합니다.

    interface IChartProps {
       coinId: string;
       isDark: boolean;
     }
    
     function Chart({ coinId, isDark }: IChartProps) {
       ...
       return (
         ...
               options={{
                 theme: {
                   mode: isDark ? "dark" : "light",
                 },

    이번에는 Chart 컴포넌트까지 세 단계로 Props 를 내려주었고 이는 명백히 효율적이지 않습니다.
    그리고 서비스가 커질 수록 관리해야하는 state 들도 많아질 것입니다.


state 가 만들어지고 전달되는 과정을 한 번 정리 해보겠습니다.

App (isDark, modifierFn)

 -> Router -> Coins (modifierFn)
 -> Router -> Coin -> Chart (isDark)

상당히 긴 여정이였습니다.


- Global State

  • 어플리케이션이 무언가를 인지해야 할때 사용합니다.
  • 어플리케이션 전체에서 공유되는 state 입니다.

이 글에서는 단지 다크모드/라이트모드의 state 만을 가지고 global state 와 왜 state management 라이브러리 가 필요한지 공부해 보았습니다.
실제로는 유저로그인 확인 등 하나의 상태가 변경됨으로 더 많은 것들이 변경되는 기능들이 필요할 것입니다.

따라서 상태관리 코드를 다른 곳에 둔다면, props 를 엄청 내려보내지 않아도 component 가 이들에 언제 접근할지 선택 가능해 질것입니다.
이에 대해서 상태관리 코드를 다른 곳에 두는 것과 현재의 방법의 비교를 해보겠습니다.

// 현방법(without state management 라이브러리)
isDark: App -> Router -> Coin -> Chart

// 상태관리 코드를 다른 곳에 두는 방법(ex_Recoil)
Header -> (isDark) <- Chart
App    -> (isDark)

State Management 라이브러리를 사용하는 것이 훨씬 간단해 보입니다.
다음 글에서는 Recoil 을 사용하여 다크모드/라이트모드 구현하는 것을 진행해보겠습니다.

profile
Aim for the TOP, Developer

0개의 댓글