[React] react-redux 사용법

hellow_coding·2023년 7월 17일

👩‍🔧 숫자를 증가시키거나 감소시키는 카운터 애플리케이션

1. 액션 타입(Action Types) 정의하기

// actions.ts
export const INCREASE = 'INCREMENT/DECREMENT';

액션 타입은 상수로 정의하고, 문자열 리터럴 타입으로 선언합니다.



2. 액션 생성자 함수 작성하기

// actions.ts
export interface IncreaseAction {
  type: typeof INCREASE; // typeof "상수 문자열" -> ~type을 반환
  count: number;
}

// 액션 타입들을 하나로 묶어서 Union 타입을 구성하기 위해 사용
export type CounterAction = IncreaseAction;

export const increaseCount = (count: number): IncreaseAction => ({
  type: INCREASE,
  count, // payload 필드
});

액션 크리에이터 함수를 작성하여 액션을 생성합니다. 액션 객체는 type 필드와 필요한 경우 추가적인 데이터를 가진 payload 필드로 구성됩니다.

INCREASE를 사용하여 타입을 제한하고, IncreaseAction 인터페이스로 액션의 타입을 지정합니다.


※ typeof👩‍🔧

리덕스의 액션 타입을 정의할 때 상수로 문자열을 선언한 경우 typeof를 사용하여 타입의 안정성을 높일 수 있습니다.

스위치 문에서 action.type과 case의 타입이 정확히 일치하는지 확인할 수 있습니다. 따라서 action.type이 'INCREMENT/DECREMENT'가 아닌 다른 문자열을 갖게 된다면 컴파일러가 오류를 발생시킵니다.

📌 Action

액션 객체를 나타내는 인자입니다. 액션 객체는 상태를 변경하는 데 필요한 정보를 포함하고 있으며, 해당 객체는 최소한 type이라는 필드를 가져야 합니다.

※ 액션의 기본 구조👩‍🔧

const increaseCount = (count: number) => ({
  type: INCREASE, // 액션의 타입
  count, // 추가 데이터 (예: 증가량)
});


3. 초기 상태(State) 타입 정의하기

// store.ts
export interface RootState {
  count: number;
}


4. 리듀서(Reducer) 함수 작성하기

// store.ts
import { INCREASE, CounterAction } from './actions';

// 초기값 설정
const initialState: RootState = {
  count: 0,
};

// state: RootState -> 타입스크립트가 상태의 구조와 속성을 추론하고,
// 잘못된 타입의 값이 상태에 할당되는 것을 방지

const counterReducer = (state: RootState = initialState, action: CounterAction): RootState => {
  // 앞의 RootState -> 첫 번째 매개변수 타입지정, 뒤 RootState -> RootState 타입의 객체
  switch (action.type) {
    case INCREASE:
      // { ...현재값, 액션 }; -> 새로운 상태를 계산하고 반환
      return { ...state, count: action.count }; // count : count 속성 업데이트
    default:
      return state;
  }
};

export { RootState, counterReducer };

현재값을 스프레드(spread)연산자(...)를 사용하여 그대로 복제하기 때문에 불변성을 유지합니다.

📌 Reducer

리듀서는 변화를 일으키는 함수입니다. 액션을 만들어 발생시키려면 리듀서가 현재 상태와 전달받은 액션 객체를 파라미터로 받아 옵니다. 그리고 두 값을 참고하여 새로운 상태로 반환해 줍니다.

※ 리듀서 기본 구조👩‍🔧

리듀서 함수는 현재 상태(state)와 액션(action)을 매개변수로 받고, 새로운 상태를 반환합니다.

state
: 리듀서 함수가 현재 상태를 나타내는 인자입니다. 이 매개변수는 현재 상태를 변경하거나 새로운 상태를 반환하는 데 사용됩니다. 일반적으로 state 매개변수는 기본값을 가지며, 애플리케이션이 처음 실행될 때 이 기본값을 가진 상태로 초기화됩니다.

action
: action 매개변수를 통해 액션의 타입을 식별하고, 해당 액션에 따라 상태를 변경합니다.

const initialState = {
  // 초기 상태를 정의합니다.
};

const reducerName = (state = initialState, action) => {
  // 액션 타입에 따라 상태를 처리하고 변경한 새로운 상태를 반환합니다.
  switch (action.type) {
    case 'ACTION_TYPE_1':
      // 처리 로직
      return {
        ...state,
        // 업데이트할 상태 속성들
      };
    case 'ACTION_TYPE_2':
      // 처리 로직
      return {
        ...state,
        // 업데이트할 상태 속성들
      };
    default:
      // 기본 동작 (액션이 처리되지 않을 때)
      return state;
  }
};

export default reducerName;


5. 스토어(Store) 생성하기

// store.ts
import { createStore } from 'redux';
import { counterReducer } from './store';

const store = createStore(counterReducer);

📌 Store

redux를 사용하기 위해서 store을 만듭니다. 1개의 프로젝트에는 1개의 store을 가질 수 있으며 store 안에는 현재 애플리케이션 상태와 reducer이 들어있습니다. createStore 함수를 사용하여 생성합니다.



6. 루트 리듀서(Root Reducer) 생성하기 (옵션)

루트 리듀서를 사용하는 경우, 여러 개의 리듀서를 결합합니다. 이는 프로젝트의 규모에 따라 다르며, 작은 프로젝트에서는 필요하지 않을 수 있습니다.



7. 미들웨어(Middleware) 적용하기 (옵션)

미들웨어를 적용하여 리덕스의 동작을 확장하거나 비동기 작업을 처리합니다. 미들웨어를 적용하는 방법은 다양합니다.



8. 스토어를 프로바이더(Provider)로 감싸기

// App.tsx
import React from 'react';
import { Provider } from 'react-redux';
import Counter from './Counter';


const App: React.FC = () => {
  return (
    // 만든 store를 앱 상위에 넣어줍니다.
    <Provider store={store}>
      <Counter />
    </Provider>
  );
};

export default App;

📌 Provider

울타리 역할을 하며 props로 store을 지정해줘야 합니다.



9. 컴포넌트에서 스토어 사용하기

// Counter.tsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { RootState } from './store';
import { increaseCount } from './actions';

const Counter: React.FC = () => {
  // store에 접근하여 state 가져오기
  // useSelector의 콜백 함수로 RootState의 타입을 지정하여 상태의 타입을 정의
  const count = useSelector((state: RootState) => state.count);
  
  // dispatch를 사용하기 위한 준비
  const dispatch = useDispatch();

  const handleIncrement = () => {
    // store에 있는 state 바꾸는 함수 실행
    dispatch(increaseCount(count + 1));
  };

  const handleDecrement = () => {
    dispatch(increaseCount(count - 1));
  };

  return (
    <div>
      <h2>Counter</h2>
      <p>Count: {count}</p>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  );
};

export default Counter;

컴포넌트에서 리덕스 스토어의 상태를 읽거나 액션을 디스패치하여 상태를 업데이트합니다. useSelector 훅을 사용하여 상태를 읽고, useDispatch 훅을 사용하여 액션을 디스패치합니다.

📌 Dispatch

디스패치는 스토어의 내장 함수 중 하나입니다. 쉽게 말해 액션을 발생시키는 역할을 합니다.
이 함수는 dispatch(action)과 같은 형태로 액션 객체를 파라미터로 넣어서 호출합니다. 이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜서 새로운 상태를 만들어 줍니다.


📌 useSelector

Store의 상태를 쉽게 선택할 수 있게 하는 함수로 이를 통해 컴포넌트에서 필요한 상태를 쉽게 조회할 수 있습니다.


리듀서가 여러개일 경우❓

combineReducers는 Redux에서 사용되는 여러 개의 리듀서(reducer)를 하나로 결합하는 함수입니다.

바로가기 Click

profile
꿈많은 개발자

2개의 댓글

comment-user-thumbnail
2023년 7월 18일

글이 잘 정리되어 있네요. 감사합니다.

답글 달기
comment-user-thumbnail
2023년 7월 18일

덕분에 좋은 정보 얻어갑니다, 감사합니다.

답글 달기