Lsiron

상태관리(Context API) 본문

프론트엔드/React

상태관리(Context API)

Lsiron 2024. 6. 1. 02:40

상태관리(Context API)는 프론트엔드를 희망한다면 반드시 알고 있어야한다.

 

반드시 알아야 취업이 된다는 아니지만 Context를 사용해야하는 과제를 마주쳤을때 난감할것이다.

 

props Drilling 에 대해 설명해보시오.

=> 컴포넌트에서 사용하지 않는 prop 임에도 불구하고 자식이나 하위 후손 컴포넌트에서 사용을 하고 있다는 이유로 prop을 받아서 다시 재전달해야하는 상황이다. 

 

무엇이 문제가 될까?

 

자식 컴포넌트가 사용하는 prop을 위해 부모 컴포넌트도 prop을 받아야하는데, 이 때문에 점점 아래로 향하는 prop이 많아 질 것이고, 복잡한 기능을 처리하는 컴포넌트 일 경우에 사용할 때 마다 리렌더링이 되므로 무리를 하게 된다.

 

이로인해 성능이 좋지않게 되며, 특히 prop 이름이 변경되면 중간 전달자에서 모두 이름을 변경해주어야 하는 번거로움이 발생한다. 

 

위 상황을 위해 React에서 나온 것이 Context API다. (코딩과제를 받을때, Context API만을 사용해서 상태관리를 구현해보시오. 라고 하는 곳도 있다.)

 

Context API 는 Provider 와 Consumer로 구성되어 있고. 보통 작성할때 Pascal Case 로 작성한다 .

또한 변수 선언은 const로 한다.(참고로 react에서 let을 쓸 일은 생각보다 많이 없다. 대부분 const를 쓴다.)

 
  //LsironProvider.jsx (Provider)
 
  import {createContextfrom 'react';
 
  export const LsironContext = createContext("")

 

=> 먼저 createContext를 import 해주고, 변수를 선언해주자.

  //LsironProvider.jsx (Provider)
 
  const LsironProvider = {{children}} => {
  const lsironState = useState("Lsiron");

  return (
    <LsironContext.Provider value={lsironState}>
      {children}  // Consumer
    </LsironContext.Provider>
  )
}

 

=> lsironState 와 setLsironState로 배열형태로 받지않고, lsironState 하나를 통으로 받아서 배열을 통째로 넘긴 것이다.

바로 뒤에 설명하지만, Provider 안에 들어가고 Consumer 역할을 하는 컴포넌트(children)는  useContext를 사용해서 Context를 넣어주면 Value에 넣어준 값을 받아올 수 있다.

 

여기서 children이 바로 consumer이다. 즉, 정보를 소비하는 요소라고 볼 수 있다. 

  //index.js (Provider)
 
  import {LsironProviderfrom './providers/LsironProvider';

  root.render(
  <React.StrictMode>
    <LsironProvider>
      <App />
    </LsironProvider>
  </React.StrictMode>
);

 

=> 위 예시처럼 index.js에서 App 컴포넌트를 넣었다는 것은 App 컴포넌트가 Lsiron이라는 Context에 들어있는 데이터를 공유 받을 수 있는 소비자로 들어간다는 것이다. 다시 말해서, 여기에 들어 가 있는 App이 LsironProvider에 있는 children 요소로 가게되고 그게 결국 LsironContext의 Provider 의 value 를 전달받아서 공유가 된다.

  //Lsiron.jsx (Consumer)  
  import {useContextfrom 'react';
  import {LsironContextfrom './providers/LsironProvider';

  const Lsiron = () => {
  const lsiron = useContext(LsironContext);

  return <div>{Lsiron}</div>;
}

 

=> Consumer는 위와 같이 useContext라는 훅을 사용한다. useContext 훅에 사용할 Context를 인자로 전달하면, Context의 Provider를 통해 전달된 데이터를 그대로 받아올 수 있다. 

 

여기서 주목할 점은 Lsiron에 이제 더이상 Prop으로 전달되지 않는다는것이다.

 

비유 때문에 말좀 편하게 하겠습니다..

음 그러니까.. 이렇게 비유를 하면 괜찮을 거 같다.하루는 한 회사의 회장이 신입사원의 방이 너무 궁금한거야.

그런데  회장이 신입사원의 방에는 직접 가지 못해. 그리고 일을 하느라 좀 많이 바쁘네?  

그래서 회장이 회장의 아들한테 시킨거야. 신입사원 방에있는 CCTV 파일좀 가져오라고.

그런데 이때 회장의 아들이 사장 부장 차장 과장 말을 다 거쳐서 가져오라고하는게 prop drilling 이야.

그렇다면? 회장의 아들이 신입사원한테서 사장 부장 차장 과장을 안 거치고 비서를 통해 바로 CCTV 파일을 가져오는게 Context API 방식이라고 생각하면 돼.

그러면 여기서 회장과 회장의 아들은 회장의 방에 있고, 신입사원은 신입사원의 방에 있지?

이때 이 회장은 'App' 이고 회장의 아들은 'children' , 회장의 방은 'useContext' , 신입사원의 방은 'LsironContext' 이고 비서는 'Provider' 야.

이 말은 즉, 회장과 회장의 아들은 'Cousumer' , 신입사원은 'Provider'가 돼.

그러면 회장의 아들이 회장의 방에서 비서한테 신입사원 방 열쇠를 줄테니 방에있는 CCTV 파일을 USB에 담아오라고 할꺼야.

그럼 신입사원 방 열쇠가 'createContext' 이고, USB가 'value',  CCTV 파일은 'value의 값' 이라는 거지.

자 그러면? 회장(App)이 회장의 아들(children)한테 시킨거야.

비서(Provider)한테 신입사원 방(LsironContext)에 가서 열쇠(createContext)로 방을 열고 USB(value)에 CCTV파일(value의 값)을 담아오도록 지시를 하라고.

그러면 비서가 CCTV파일을 가져올꺼야. 이제 이 과정을 봐보자.

 

 
  //LsironProvider.jsx (Provider)
  import {createContextfrom 'react';
  export const LsironContext = createContext("")
 
 

=> 신입사원 방( LsironContext )을 열기위한 열쇠( createContext )를 가지고 나왔어.

  //LsironProvider.jsx (Provider)
  const LsironProvider = {{children}} => {
  const lsironState = useState("Lsiron");

  return (
    <LsironContext.Provider value={lsironState}>
      {children}  // Consumer
    </LsironContext.Provider>
  )
}

 

=> 신입사원의 방( LsironContext )에서 비서( Provider )가 USB( value )에 CCTV파일( {lsironState} )을 담았고 이걸 회장의 아들( {children} )에게 건네준거야.  이러한 과정이 바로 ' LsironProvider ' 야

  //index.js (Provider)
  import {LsironProviderfrom './providers/LsironProvider';

  root.render(
  <React.StrictMode>
    <LsironProvider>
      <App />
    </LsironProvider>
  </React.StrictMode>
);

 

=> 그러면 비서가 회장의 아들한테 CCTV파일이 담긴 USB를 건네주는 과정( <LsironProvider>   )을 통해

회장(  <App /> )도 신입사원의 방을 볼 수 있게 된거야.

  //Lsiron.jsx (Consumer)  
  import {useContext} from 'react';
  import {LsironContext} from './providers/LsironProvider';

  const Lsiron = () => {
  const lsiron = useContext(LsironContext);

  return <div>{Lsiron}</div>;
}

 

=> 결국, 회장은 회장의 방( useContext )에서 신입사원의 방( LsironContext )을 볼 수 있게 되었어. 이게 useContext(LsironContext)  로 되는 거야. 반환값은 이때 CCTV파일 ( {lsironState} )이 되는거지.

const Lsiron = () => {
  const [lsiron, setLsiron] = useContext(LsironContext);

  return (
    <div className='lsiron'>
      {Lsiron}
      <input
        value={lsiron}
        onChange={(e) => {
          setLsiron(e.target.value);
        }}
      />
    </div>
  );
};

 

=> 자 그러면 얼마나 이해 됐는지 확인해보자. const [lsironsetLsiron] 으로 useContext(LsironContext) 을 통해 기본 값 {lsironState} 를 받아왔다. input value  값에 {lsiron} 을 넣으면 value 의 값은 기본 값 {lsironState} 가 되고 onChange를 통해 입력하는대로 값을 변경할 수 있다.

 

그러나, Lsiron 컴포넌트에서 [lsironsetLsiron] 은 Lsiron 컴포넌트의 state가 이제는 아니다.

 

왜냐하면 useContext (회장의 방)가 LsironContext (신입사원의 방)라고 하는 전역에서 공유할 목적의 데이터 주머니에 들어가 있는 녀석(신입사원 방 CCTV 파일 {lsironState} )을 그냥 꺼내쓴거니까, Lsiron 컴포넌트는 독자적인 state를 갖고있지 않다.

 

'프론트엔드 > React' 카테고리의 다른 글

axios(get, post, put, delete)  (2) 2024.06.02
Custom hook, styling, SPA와 라우팅  (0) 2024.05.30
event처리, hooks  (0) 2024.05.24
JSX, Components, Props/State  (0) 2024.05.22
리액트 시작하기.  (0) 2024.05.19