웹 시스템 개발 #React Context

학교 공부를 복습할 겸 적는 것이기에 내용이 부족할 수 있습니다.

 

부족한 것은 상관 없으나, 잘못된 부분이 발견된다면 지적해주시면 감사하겠습니다.


 

React의 Context API는 React 애플리케이션 전체에서 전역적으로 상태를 관리하는 방법입니다. 이를 통해 구성 요소 계층의 각 수준을 통해 수동으로 props을 전달할 필요 없이 React 구성 요소의 전체 트리에서 값(데이터 또는 함수)을 공유할 수 있습니다.

 

즉, 깊게 중첩된 구성 요소를 처리하거나 여러 구성 요소가 동일한 데이터에 액세스해야 할 때 Context를 사용할 수 있습니다.

Prop Drilling의 문제점

Prop Drilling / 출처 - 아주대학교 웹시 수업 자료

 

Heading 구성 요소에는 렌더링할 HTML 제목 태그(<h1><h2> 등)를 결정하기 위해 level prop이 필요합니다. 구성 요소 트리가 깊게 중첩되면 트리의 모든 수준을 통해 level prop을 직접 필요하지 않은 구성 요소에도 전달해야 합니다. 이는 "Prop Drilling"으로 알려져 있으며 장황하고 유지 관리하기 어려운 코드로 이어지게 됩니다.

 

Context를 사용하여 Prop 전달 단순화

모든 레벨을 통해 prop을 명시적으로 전달하지 않고도 구성요소 트리의 모든 레벨에서 값(예: level prop)을 공유할 수 있도록 하여 이 문제를 해결할 수 있습니다.

import React from 'react';

const HeadingLevelContext = React.createContext();

export default function Page() {
  return (
    <HeadingLevelContext.Provider value={1}>
        <Heading>Heading</Heading>
        {/* other components */}
    </HeadingLevelContext.Provider>
  );
}
import React, { useContext } from 'react';

export default function Heading({ children }) {
  const level = useContext(HeadingLevelContext);

  switch (level) {
    case 1:
      return <h1>{children}</h1>;
    // other cases
    default:
      throw Error('Unknown level: ' + level);
  }
}

 

React의 Context API에 대한 핵심 사항

  1. 컨텍스트 생성:
    • 새로운 컨텍스트를 생성하려면 createContext를 사용하면 됩니다. 인수로 기본값을 사용합니다.
    • 예: export const LevelContext = createContext(1);
  2. 컨텍스트 사용:
    • 구성 요소에 생성된 컨텍스트와 함께 useContext 후크를 가져와 사용합니다.
    • 주의: 후크는 React 구성 요소의 최상위 수준에서 호출되어야 합니다(루프나 조건 내부가 아님).
  3. 컨텍스트 제공:
    • Context.Provider를 사용하여 구성 요소 트리에서 컨텍스트 값을 사용할 수 있도록 합니다.
    • 예: <LevelContext.Provider value={level}>{children}</LevelContext.Provider>에 구성 요소를 래핑하여 LevelContext를 제공합니다.
import { LevelContext } from './LevelContext.js'; 

export default function Section({ level, children }) {
  return (
    <section className="section">
      <LevelContext.Provider value={level}>
        {children}
      </LevelContext.Provider> 
    </section>
  ); 
}

 


 

Redux란?

Redux는 React와 함께 자주 사용되는 JavaScript 앱의 예측 가능한 상태 컨테이너입니다. 스토어라고 불리는 변경 불가능한 단일 객체에서 애플리케이션의 상태를 관리하는 데 도움이 됩니다. 

  • Redux Store: 애플리케이션의 전체 상태 트리를 보유하는 객체입니다. 저장소는 리듀서를 전달하여 생성되며 'dispatch'(저장소에 작업 보내기) 및 'getState'(현재 상태 가져오기)와 같은 메서드가 있습니다.
  • 리듀서: 리듀서는 현재 상태와 작업(actions)을 취하고 새 상태를 반환하는 함수입니다. 
  • 액션: 액션은 상태 변경 의도를 나타내는 일반 JavaScript 개체입니다. 
  • 디스패치: 디스패칭은 상태 변경을 호출하기 위해 스토어에 작업을 보내는 프로세스입니다. 상태 변경을 트리거하는 유일한 방법입니다.
  • 구독자: 구독자는 UI를 다시 렌더링하거나 다른 작업을 수행하여 업데이트된 상태에 반응할 수 있도록 상태 변경을 수신하는 구성 요소 또는 함수입니다.
// store/index.js
import { createStore } from 'redux';

// A simple counter reducer
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    case 'RESET':
      return 0;
    default:
      return state;
  }
};

// Create the Redux store with the counter reducer
const store = createStore(counterReducer);

export default store;

 

// App.js
import React from 'react';
import Counter from './components/Counter'; // Make sure to create this component

function App() {
  return <Counter />;
}

export default App;
// components/Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

const Counter = () => {
  const dispatch = useDispatch();
  const counter = useSelector((state) => state);

  const incrementHandler = () => {
    dispatch({ type: 'INCREMENT' });
  };

  const decrementHandler = () => {
    dispatch({ type: 'DECREMENT' });
  };

  const resetHandler = () => {
    dispatch({ type: 'RESET' });
  };

  return (
    <div>
      <h2>Redux Counter</h2>
      <div>{counter}</div>
      <button onClick={incrementHandler}>Increment</button>
      <button onClick={decrementHandler}>Decrement</button>
      <button onClick={resetHandler}>Reset</button>
    </div>
  );
};

export default Counter;
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './store/index';
import { Provider } from 'react-redux';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <App />
  </Provider>
);

 

코드가 기네요 후하..

Redux의 데이터 흐름

데이터 흐름 / 출처 - 아주대학교 웹시 수업 자료

  1. 작업(Actions)은 클릭과 같은 사용자 상호작용에 대한 응답으로 전달됩니다.
  2. 저장소는 새로운 상태를 계산하기 위해 리듀서 기능을 실행합니다.
  3. UI는 새 상태를 읽어 새 값을 표시합니다.

 

Redux vs Context

Redux는 예측 가능한 방식으로 복잡한 상태를 유지해야 하는 대규모 애플리케이션에 특히 적합합니다. 애플리케이션의 여러 부분이 동일한 상태 중 일부에 액세스해야 하는 경우에도 유용합니다.

컨텍스트는 Prop 드릴링 없이 Prop을 전달하는 데 적합하며 소규모 애플리케이션이나 Redux와 같은 상태 관리 라이브러리의 복잡성을 추가하지 않고 상태를 전달해야 하는 경우 충분할 수 있습니다.