Will find a way

[React] Redux (store를 전역 상태로 관리) 본문

FrontEnd/React

[React] Redux (store를 전역 상태로 관리)

Jaka_Park 2024. 10. 20. 22:53

https://despiteallthat.tistory.com/192

 

[Redux] Redux란?

오늘은 Redux에 대해 알아보겠습니다. :) [ Redux ] Redux(리덕스)란 JavaScript 상태관리 라이브러리입니다. 여기서 말하는 상태(State)란 간단하게 말하자면 데이터를 말합니다. 덧붙이자면 상태는 컴포

despiteallthat.tistory.com

https://jwdevv.tistory.com/37

 

[Redux toolkit] 2 - Slice, Reducer, Action, Thunk란?

지난 글에서 Redux와 Redux Toolkit의 차이점에 대해 알아보았다. [Redux Toolkit] 1 - Redux Toolkit이란? | Redux와 Redux Toolkit 차이 Redux Toolkit이란? 리액트 개발자라면 리덕스에 대해서 들어봤을 것이다. 리덕스

jwdevv.tistory.com

 

학습 목표

- Redux에 대해 알아보자

- Redux를 쓰는 원칙에 대해서 알아보자

- Redux는 어떻게 상태관리를 할까?

 

redux react-redux

react의 전역 상태 관리를 redux로 사용할 때 유용한 hook을 라이브러리가 제공해준다.

 

Redux란?

React의 JavaScript 상태관리 라이브러리다.

 

Redux의 원칙

1) Single source of truth

- 동일한 데이터는 항상 같은 곳에서 가져온다

- 즉, 스토어라는 단 하나의 데이터 공간이 있다는 것을 의미한다.

 

2) State is read-only

- React에서는 setState 메소드를 활용해야만 상태 변경이 가능하다.

- 리덕스에서도 액션이라는 객체를 통해서만 상태를 변경할 수 있다.

 

3) Changes are made with pure functions

- 변경은 순수함수로만 가능하다. (리듀서와 관련된 개념이다.)

- Store(스토어) - Action(액션) - Reducer(리듀서)

 

컴포넌트에서 전역의 상태값이 필요할 때 store에 접근해서 데이터를 참조 혹은 수정

 

- Store / Action / Reducer 에 대해서

 

Store (스토어) : 상태가 관리되는 하나의 공간이다.

- 컴포넌트와는 별개로 스토어라는 공간이 있어서 그 스토어 안에 앱에서 필요한 상태를 담는다.

- 컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다.

 

Action (액션) : 앱에서 스토어에 운반할 데이터를 말한다.

- Action은 자바스크립트 객체 형식으로 되어 있다.

 

Reducer (리듀서)

- Action을 Store에 바로 전달하는 것이 아닌, Reducer에 전달해야 한다.

- Reducer가 주문을 보고 Store의 상태를 업데이트 하는 것이다.

- Action을 Reducer에 전달하기 위해서는 dispatch() 메서드를 사용해야 한다.

 

Dispatch (디스패치)

- Dispatch는 store의 내장 함수 중 하나로, action을 발생시킨다.

- action을 파라미터로 전달하고 reducer를 호출한다.

 

Subscribe (구독)

- Subscribe는 store의 내장 함수 중 하나로, 특정 함수를 전달해주면 action이 dispatch 되었을 때마다 전달된 함수가 호출된다.

 

redux 기본 동작

- Component => dispatch => action => reducer => store

 

액션은 스토어에 전달할 데이터의 값 액션을 보내서 데이터의 상태를 업데이트한다.

dispatch 함수를 실행해서 dispatch({ 액션객체의 내용 }) 리듀서 함수를 실행시키고 반환값으로 상태를 업데이트한다.

 

Redux의 장점

1) 순수 함수를 사용해 상태를 예측 가능하게 만든다.

2) 유지보수에 용이하다

3)  redux dev tool이 있어 디버깅에 유리한다

4)  비동기를 지원하는 Redux Saga, Redux Thunk 등 다양한 미들웨어가 존재한다.

 

Redux가 필요한 순간

1) 전역 상태가 필요하다고 느껴질 때

2) 상태들이 자주 업데이트 될 때

3) 상태를 업데이트 하는 로직이 복잡할 때

4) 앱이 크고 많은 사람들에 의해 코드가 관리될 때

5) 상태가 업데이트 되는 시점을 파악할 필요가 있을 때

 

 

React Redux 시작

npm install @reduxjs/toolkit react-redux

 

Redux

store.js 를 만들어서 configuteStore Redux Toolkit Redux Toolkit에서 API를 가져온다. 빈 Redux 스토어를 만들고 내보낸다.

import { configureStore } from '@reduxjs/toolkit';

export default configureStore({
  reducer: {}
});

 

이렇게 하면 Redux 저장소가 생성되고 개발중에 저장소를 검사할 수 있도록 Redux DevTools 확장도 자동으로 구성된다.

 

React에 Redux Store 제공

Store가 생성되면 index.js의 애플리케이션 주위에 React Redux <Provider>를 배치하여 React 컴포넌트에서 이를 사용할 수 있도록 할 수 있다. 방금 생성한 Redux 스토어를 가져오고 <App> 주위에 <Provider>를 배치한 다음 스토어를 prop으로 전달한다.

 

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import store from './app/store';
import { Provider } from 'react-redux';

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
  <Provider store={store}>
    <App />
  </Provider>
)

 

Redux State

counterSlice.js 해당 파일에서 createSlice를 Redux Toolkit에서 API를 가져온다.w슬라이스를 생성하려면 슬라이스를 식별하기 위한 문자열 이름, 초기 상태 값, 그리고 상태 업데이트 방법을 정의하기 위한 하나 이상의 리듀서 함수가 필요하다. 슬라이스가 생성되면 생성된 Redux 액션 생성자와 전체 슬라이스에 대한 리듀서 기능을 내보낼 수 있다. Redux에서는 데이터 복사본을 만들고 복사본을 업데이트하여 모든 상태 업데이트를 불변하게 작성해야 한다. 그러나 Redux Toolkit의 createSlice 및 createReducer API는 Immer 내부를 사용하여 올바른 불변 업데이트가 되는 '변형' 업데이트 논리를 작성할 수 있도록 한다.

import { createSlice } from '@reduxjs/toolkit';

export const counterSlice = createSlice({
  name: 'counter'
  initialState: {
    value: 0
  },
  reducers: (state) => {
    // Redux Toolkit을 사용하면 리듀서에 "변형"논리를 작성할 수 있다.
    // Immer 라이브러리를 사둉하기 때문에 실제로 상태를 변경하지 않다.
    // 초기상태에 대한 변경 사항을 감지하고 새로운 상태를 생성한다.
    // 해당 변경 사항을 기반으로 한 불변 상태다.
    // 이 함수에는 리턴이 필요하지 않다.
    increment: (state) => {
      state.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    // 각 케이스 리듀서 함수에 대해 액션 생성자가 생성된다.
    incrementByAmount: (state, action) => {
      state.value += action.payload
    }
  }
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export default counterSlice.reducer;

 

슬라이스를 생성하려면 슬라이스를 식별하는 문자열 이름, 초기 상태 값, 상태를 업데이트하는 방법을 정의하는 이상의 리듀서 함수가 필요하다. 슬라이스가 생성되면 생성된 Redux 액션 생성자와 전체 슬라이스에 대한 리듀서 함수를 보낼 수 있다.

 

Store에 슬라이스 리듀서 추가

counterSlice 에서 리듀서 함수를 가져와서 스토어에 추가해야 합니다. 매개 reducer 변수 내부에 필드를 정의하여 스토어에 이 슬라이스 리듀서 함수를 사용하여 해당 상태에 대한 모든 업데이트를 처리하도록 지시한다.

import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

export default configureStore({
  reducer: {
    counter: counterReducer
  }
});

 

React 컴포넌트

React Redux hooks를 사용하여 React 컴포넌트가 Redux store와 상호 작용하도록 할 수 있다. useSelector를 사용하여 store에서 데이터를 읽을 수 있고 useDispatch를 사용하여 작업을 전달할 수 있다. 내부에 <Counter> 구성 요소가 포함된 Counter.js 파일을 만든 다음 해당 구성 요소를 App.js로 가져와 <App> 내부에서 렌더링 한다.

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { decrement, increment } from './counterSlice';
import styles from './Counter.module.css';

export function Counter(){
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();
  
  return (
    <div>
      <div>
        <button onClick={() => dispatch(increment())}>
          Increment
        </button>
        <span>{count}</span>
        <button onClick={() => dispatch(decrement())}>
          Decrement
        </button>
      </div>
    </div>
  )
}

 

증가 및 감소 버튼을 클릭할 수 있다.

 

- 해당 Redux 작업이 스토어로 전송된다.

- counterSlice 리듀서는 동작을 확인하고 상태를 업데이트한다.

- 구성 <Counter>요소는 저장소에서 새 상태 값을 확인하고 새 데이터로 다시 렌더링한다.