240820 React Redux3 - payload, ducks

신한별·2024년 8월 20일

💪 매일 기록

목록 보기
62/97

01. Action Creator

(1) Action Creator 란?

액션을 만드는 생성자. 액션 객체를 만드는 함수?

액션객체의 value를 변경할 일이 생긴다면 어떨까 PLUS_ONE ,MINUS_ONE 이라는 value 대신 이 액션객체가 counter 모듈안에 있다는 것을 강조하기 위해서 counter/PLUS_ONE, counter/MINUS_ONE 이라는 value로 바꾸길 각각 바꾸길 원한다면 아래 코드에서 4번이나 변경해줘야 한다. 💢

// src/App.js

import React from "react";
import { useDispatch, useSelector } from "react-redux";

const App = () => {
  const dispatch = useDispatch();
  const number = useSelector((state) => state.counter.number);

  return (
    <div>
      {number}
      <button
        onClick={() => {
          dispatch({ type: "PLUS_ONE" }); // counter/PLUS_ONE로 변경
        }}
      >
        + 1
      </button>
      <button
        onClick={() => {
					// 액션객체 디스패치
          dispatch({ type: "MINUS_ONE" }); // counter/MINUS_ONE로 변경
        }}
      >
        - 1
      </button>
    </div>
  );
};

export default App;
// src/modules/counter.js

// 초기 상태값
const initialState = {
  number: 0,
};

// 리듀서
const counter = (state = initialState, action) => {
  switch (action.type) {
    case "PLUS_ONE": // counter/PLUS_ONE로 변경
      return {
        number: state.number + 1,
      };

		// action.type이 MINUS_ONE 일 때 새로운 state 반환
    case "MINUS_ONE": // counter/MINUS_ONE로 변경
      return {
        number: state.number - 1,
      };
    default:
      return state;
  }
};

// 모듈파일에서는 리듀서를 export default 한다.
export default counter;

(2) Action Creator 만들기

액션객체를 한 곳에서 관리할 수 있도록 함수와 액션 value를 상수로 만들어보자

PLUS_ONE 액션 객체를 만드는 함수 생성
=> Action Creator 액션을 만드는 생성자

// src/redux/modules/counter.js

const PLUS_ONE = "PLUS_ONE"; // value는 상수로 생성

// 액션객체를 반환하는 함수 생성
// export 가 붙는 이유는 plusOne()는 밖으로 나가서 사용될 예정이기 때문입니다.
export const plusOne = () => { 
  return {
    type: PLUS_ONE, // type에는 위에서 만든 상수로 사용 (vscode에서 자동완성 지원)
  };
};
  • value는 상수로 따로 만들어주고, 이를 이용해 액션객체를 반환하는 함수를 작성
  • 실제로 리듀서와 컴포넌트에서는 아래와 같이 작성합니다.

모듈에 initialState와 리듀서밖에 없었지만
액션의 value와 Action Creator가 추가

// src/modules/counter.js

// 추가된 코드 👇 - 액션 value를 상수들로 만들어 줍니다. 보통 이렇게 한곳에 모여있습니다.
const PLUS_ONE = "PLUS_ONE";
const MINUS_ONE = "MINUS_ONE";


// 추가된 코드 👇 - Action Creator를 만들어 줍니다. 
export const plusOne = () => {
  return {
    type: PLUS_ONE,
  };
};

export const minusOne = () => {
  return {
    type: MINUS_ONE,
  };
};


// 초기 상태값
const initialState = {
  number: 0,
};

// 리듀서
const counter = (state = initialState, action) => {
  switch (action.type) {
    case PLUS_ONE: // case에서도 문자열이 아닌, 위에서 선언한 상수를 넣어줍니다. 
      return {
        number: state.number + 1,
      };
    case MINUS_ONE: // case에서도 문자열이 아닌, 위에서 선언한 상수를 넣어줍니다. 
      return {
        number: state.number - 1,
      };
    default:
      return state;
  }
};


export default counter;

(3) Action Creator 사용하기

생성한 Action Creator를 컴포넌트에서 어떻게 사용하는지

  1. export 된 Action Creator import 하기
  2. dispatch()에 있던 액션객체를 지우고, Action creator 넣기
// src/App.js

import React from "react";
import { useDispatch, useSelector } from "react-redux";

// 사용할 Action creator를 import 합니다.
import { minusOne, plusOne } from "./redux/modules/counter";

const App = () => {
  const dispatch = useDispatch();
  const number = useSelector((state) => state.counter.number);

  return (
    <div>
      {number}
      <button
        onClick={() => {
          dispatch(plusOne()); // 액션객체를 Action creator로 변경합니다.
        }}
      >
        + 1
      </button>
      {/* 빼기 버튼 추가 */}
      <button
        onClick={() => {
          dispatch(minusOne()); // 액션객체를 Action creator로 변경합니다.
        }}
      >
        - 1
      </button>
    </div>
  );
};

export default App;

Q. dispatch()안에는 반드시 객체만 들어가야 한다고 알고 있는데, 어떻게 함수가 들어갈 수 있을까요?

A. {type: “PLUS_ONE”} === plusOne() 는 같은 값
함수를 실행한 것은 함수의 return 값과 같습니다.
다시 말해, const one = () => {return 1; } 로 함수를 만들었을 때 one() === 1 이다.

02. 왜 Action creator를 사용하나

(1) 휴먼에러 (오타) 방지

액션객체의 type value를 상수로 만들어놓았기 때문에, 개발툴에서 자동완성등의 보조 기능을 지원받을 수 있다.

(2) 유지보수의 효율성 증가

Action Creator가 만약 100군데에서 쓰이고 있는 상태에서 혹여나 그것을 바꾸어야 하는 상황이 오더라도 단 한번의 수정으로 100군데에 모든 수정사항을 반영할 수 있다.

(3) 코드 가독성

모듈 파일에서 Action Creator가 일목요연하게 정리가 되어있으면, 다른 개발자가 보았을 때 해당 모듈이 가지고 있는 모든 Action들을 한눈에 알 수 있게 된다.

(4) 리덕스 공식문서에서도 추천함

https://redux.js.org/api/bindactioncreators/#todoactioncreatorsjs

03. payload

reducer에 얼만큼의 값을 어떻게 해! 라고 할 때, 이 얼만큼의 값을 전달해 줄 수 있는 것이 payload🥇

(1) Payload란?

  • 이전 챕터에서+1 기능과 -1 기능을 만들었다.

    BUT 1로 정해진 기능을 만드는 것이 아니라 증가 시킬 숫자를 사용자가 직접 정할 수 있게하는 기능을 만들려면?

    사용자가 5을 더하고 싶으면 input에 5를 입력해서 버튼을 누르면 5가 더해지고, 10을 더하고 싶으면 10을 입력하고 버튼을 눌렀을 때 10이 더해지는 프로그램을 만들려면?

  • 우리가 이전까지 만든 기능은 리듀서에게 "플러스해" 라고만 말을 했다. 1이라는 것은 우리가 임의적으로 정해서 리듀서에게 액션 객체를 보냈다.

하지만 우리는 이제 "N을 플러스해" 라고 N을 같이 리듀서에서 보내야 한다.
지금까지는 ~을 이라는 목적어가 없었다면, 이제는 그 목적어가 생긴것이고 목적어도 액션객체에 담아 같이 보내줘야 한다.

이렇게 액션객체에 같이 담아 보내주는 게 payload

type뿐만 아니라 payload라는 key와 value를 같이 담는다.

  // payload가 추가된 액션객체
{type: "ADD_NUMBER", 
  payload: 10}   

(2) payload를 이용하여 기능 구현

값을 입력받을 input 구현 -> Action Creator 작성 -> 리듀서 작성 -> 구현 기능 테스트

a. input 구현

// src/App.js

import React from "react";
import { useState } from "react";

const App = () => {
  const [number, setNumber] = useState(0);

  const onChangeHandler = (event) => {
    const { value } = event.target;
    setNumber(+value);
  };

	// 콘솔로 onChangeHandler가 잘 연결되었는지 확인
	// input에 값을 넣을 때마다 콘솔에 그 값이 찍히면 연결 성공!
  console.log(number);

  return (
    <div>
      <input type="number" onChange={onChangeHandler} />
      <button>더하기</button>
      <button>빼기</button>
    </div>
  );
};

export default App;

b. counter.js 모듈 작성: Action Creator

// src/redux/modules/counter.js

// Action Value
// ES6에서 객체 key, value가 같으면 이부분 생략 가능 
const ADD_NUMBER = "ADD_NUMBER";

// Action Creator
export const addNumber = (payload) => {
  return {
    type: ADD_NUMBER,
    payload: payload,
  };
};

// Initial State

// Reducer

// export default reducer

c. counter.js 모듈 작성: Initial State, Reducer, 내보내기(export default)

// src/redux/modules/counter.js

// .. 중략

// Initial State
const initialState = {
  number: 0,
};

// Reducer 기본형태
const counter = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

// export default reducer
export default counter;

initial State와 리듀서의 기본 형태를 만들어준다.
export default 를 통해서 생성한 리듀서를 내보내준다.

ADD_NUMBER의 로직 구현

// 리듀서

const counter = (state = initialState, action) => {
  switch (action.type) {
    case ADD_NUMBER:
      return {
				// state.number (기존의 nubmer)에 action.paylaod(유저가 더하길 원하는 값)을 더한다.
        number: state.number + action.payload,
      };
    default:
      return state;
  }
};

d. 테스트

App.js

import React from "react";
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";

// 4. Action Creator를 import
import { addNumber } from "./redux/modules/counter";

const App = () => {
	// 1. dispatch를 사용하기 위해 선언
  const dispatch = useDispatch();
  const [number, setNumber] = useState(0);
  const globalNumber = useSelector((state) => state.counter.number);

  const onChangeHandler = (event) => {
    const { value } = event.target;
    setNumber(+value);
  };

	// 2. 더하기 버튼을 눌렀을 때 실행할 이벤트핸들러를 생성
  const onClickAddNumberHandler = () => {
		// 5. Action creator를 dispatch 해주고, 그때 Action creator의 인자에 number를 넣어줍니다.
    dispatch(addNumber(number));
  };

  return (
    <div>
      <div>{globalNumber}</div>
      <input type="number" onChange={onChangeHandler} />
			{/* 3. 더하기 버튼 이벤트핸들러를 연결 */}
      <button onClick={onClickAddNumberHandler}>더하기</button>
      <button>빼기</button>
    </div>
  );
};

export default App;

04. Ducks 패턴

Ducks 패턴은 Redux 앱을 구성할 때 사용하는 방법론 중 하나로, 일반적으로 분산되어 있던 액션 타입, 액션 생성자, 리듀서를 하나의 파일로 구성하는 방식. 코드의 관리를 보다 간결하고 모듈화하여 관리할 수 있도록 도와준다.

🐥 Erik Rasmussen 이 제안한 Ducks 패턴

  1. Reducer 함수를 export default 한다.

  2. Action creator 함수들을 export 한다.

  3. Action type은 app/reducer/ACTION_TYPE 형태로 작성한다.

🐥 모듈 파일 1개에 Action Type, Action Creator, Reducer 가 모두 존재하는 작성방식

0개의 댓글