[React 숙련] Redux 3 - Redux 흐름, counter 예시

조아영·2025년 2월 26일

◼ Redux 전체 흐름

  1. View에서 액션이 일어남(이벤트 발생)
  2. dispatch에서 action이 일어남(dispatch 호출)
  3. action에 의해 reducer 함수가 실행되기 전, middleware가 작동(middleware 실행)
  4. middleware에서 명령내린 일을 수행하고 난 뒤, reducer함수를 실행(reducer 실행)
  5. reducer의 실행결과를 store에 새로운 값으로 저장(새로운 state 생성)
  6. 변경된 state를 구독 중인 UI에 전달

◼ +1 기능 구현(state 수정 기능)

// useState 예시
const [number, setNumber] = useState(0)

const onClickHandler = () => {
	setNumber(number + 1)
}

리덕스에서는 state 수정이 리듀서에서만 발생함.
컴포넌트에서 직접 값을 바꾸지 않음.

counter.js에 있는 number에 +1을 하고 싶다면

  1. 리듀서에게 보낼 number를 +1 하라는 명령 만들기(action 생성)
  2. 명령 보내기(dispatch 호출)
  3. 리듀서에서 명령을 받아 number +1 하기(reducer에서 처리)

1. 리듀서에게 보낼 명령 만들기(action 생성)

리덕스에서 명령은 action임. 리듀서에게 어떤 동작을 원하는지 전달하는 수단임.
action은 객체 형태로 작성함. 이 객체를 액션 객체라고 부름.
액션 객체는 반드시 type이라는 key를 가져야 함. 리듀서는 type이라는 key를 기준으로 동작을 판단함.

//number에 +1 을 하는 액션 객체
{ type : "PLUS_ONE" };

리덕스 state를 변경하려면 변경 의도마다 액션 객체가 필요함.

2. 명령(액션 객체) 보내기(dispatch 호출)

액션 객체를 만들었으면 전달해야 함. 이때 사용하는 것이 dispatch.
dispatch는 useDispatch 훅으로 생성.
react-redux에서 import 필요함. 액션 객체를 리듀서로 보내주는 역할을 함.

버튼 클릭 시 액션객체 전달 예시

// src/App.js

import React from "react";
import { useDispatch } from "react-redux"; // import

const App = () => {
  const dispatch = useDispatch(); // dispatch 생성
  return (
    <div>
      <button
				{/* ✅️ ✅️ 이벤트 핸들러 추가 */}
        onClick={() => {
					{/* ✅️ 클릭했을 때 dispatch가 실행되고 
					()안에 있는 액션객체가 리듀서로 전달. */}
          dispatch({ type: "PLUS_ONE" }); 
        }}
      >
				+ 1
      </button>
    </div>
  );
};

export default App;

dispatch는 함수임. 그래서 사용할 때 ()를 붙여서 함수를 실행해야 함.
사용 시 ()안에 액션객체를 넣어주면 됨.

3. 명령(액션 객체) 받기(reducer에서 처리)

“2. 명령(액션 객체) 보내기(dispatch 호출)”에서 리듀서로 액션을 보내긴 했지만 눈으로 확인하고 싶을때. 리듀서에 액션이 잘 들어오는지 확인함.

// src/redux/modules/counter.js

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

// 리듀서
const counter = (state = initialState, action) => {
	console.log(action); // ✅️ console.log(action) 추가
  switch (action.type) {
    default:
      return state;
  }
};

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

console.log(action) 추가해서
버튼 클릭 시 App.js에서 보낸 액션 객체가 콘솔에 출력되게 함.
dispatch → reducer 전달 구조 확인 가능함.

4. 액션에 따라 state 변경 로직 작성

리듀서가 액션객체를 받아 상태를 바꾸는 원리는 아래와 같음.

  1. 컴포넌트로부터 dispatch를 통해 액션객체를 전달 받음.
  2. action 안에 있는 type을 스위치문을 통해 하나씩 검사해서, 일치하는 case를 찾음.
  3. type과 case가 일치하는 경우에, 해당 코드가 실행되고 새로운 state를 반환(return).
  4. 리듀서가 새로운 state를 반환하면, 그게 새로운 모듈의 state가 됨.

“3. 명령(액션 객체) 받기(reducer에서 처리)”에서
“1. 컴포넌트로부터 dispatch를 통해 액션객체를 전달 받음” 까지 구현했으니,
2~4를 직접 코드로 작성.

// src/modules/counter.js

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

// 리듀서
const counter = (state = initialState, action) => {
  console.log(action);
  switch (action.type) {
		// ✅️ PLUS_ONE이라는 case를 추가.
		// 여기서 말하는 case란, action.type을 의미.
		// dispatch로부터 전달받은 action의 type이 "PLUS_ONE" 일 때
		// 아래 return 절이 실행. 
    case "PLUS_ONE":
      return {
				// 기존 state에 있던 number에 +1을 더함.
        number: state.number + 1,
      };

    default:
      return state;
  }
};

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

type이 "PLUS_ONE"이면 number가 1 증가한 새로운 state 반환함.
기존 state 직접 수정 아님. 새 객체 반환 구조임.

5. useSelector로 변경된 state값 확인

// 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); 

  console.log(number); // ✅️ 콘솔 추가
  return (
    <div>
			{/* ✅️ 코드 추가 */}
      {number}
      <button
        onClick={() => {
          dispatch({ type: "PLUS_ONE" });
        }}
      >
        + 1
      </button>
    </div>
  );
};

export default App;

버튼 클릭 시 0 → 1 → 2 로 증가 확인 가능함.
리덕스 state 변경 시 해당 state를 구독한 컴포넌트 자동 리렌더링 됨.

◼ -1 기능 구현

1. 버튼 구현

// 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" });
        }}
      >
        + 1
      </button>

      {/* ✅️ 빼기 버튼 추가 */}
      <button>
        - 1
      </button>
    </div>
  );
};

export default App;

2. 액션객체 디스패치하기

// 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" });
        }}
      >
        + 1
      </button>
      
			{/* ✅️ dispatch와 액션객체 넣어줌 */}
      <button
        onClick={() => {
          dispatch({ type: "MINUS_ONE" });
        }}
      >
        - 1
      </button>
    </div>
  );
};

export default App;

3. 리듀서 로직 구현

// src/modules/counter.js

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

// 리듀서
const counter = (state = initialState, action) => {
	// ✅️ 스위치문에 case추가
  switch (action.type) {
    case "PLUS_ONE":
      return {
        number: state.number + 1,
      };

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

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

◼ 정리

액션객체

  • type을 반드시 가지는 객체
  • 리듀서로 보내는 명령

디스패치

  • 액션 객체 전달 함수
  • state 변경 요청 역할

리듀서

  • 액션을 검사하는 함수
  • 새로운 state 생성 담당

useDispatch

  • dispatch 생성용 훅

useSelector

  • 리덕스 state 조회 훅

action type

  • 대문자 사용
  • 상수 컨벤션 적용

0개의 댓글