나의 개발일지

[Redux] Redux를 사용하는 이유 / Redux 실행 과정 이해하기 /Counter 만들기 본문

React/Redux

[Redux] Redux를 사용하는 이유 / Redux 실행 과정 이해하기 /Counter 만들기

heew0n 2023. 11. 20. 20:50

Redux가 필요한 이유

 

useState의 불편함

- 반드시 부모-자식 관계가 형성돼있어야 한다

- 조부모에서 손자 컴포넌트로 보낼 시 반드시 부모 컴포넌트를 거쳐야 한다

- 자식--> 부모로 보낼 수 없다

 

하지만 redux를 쓰게 된다면 중앙 state 관리소에서 생성된 Global state에서 불러와서

사용할 수 있다! 어느 특정 컴포넌트에 종속되어 있지 않다

유지보수성이 좋아진다.

 


리덕스 패키지 설치

git bash에 yarn add redux react-redux 입력 후 설치 --> package.json 에서 확인 --> 폴더 생성

 

 

package.json에서 설치되어있는지 확인!

 


 

 

counter.js

 

// 초기 상태값(state)
// const [number, setNumber] = useState(0)
const initialState = {
  number: 0,
};

// 리듀서 : 'state에 변화를 일으키는' 함수
// state를 action의 type에 따라 변경하는 함수
// input : state와 action 두 가지의 인자
// action에는 type과 payload가 있다

const counter = (state = initialState, action) => {
  switch (action.type) {
    default:
      return state;
  }
};

export default counter;

 

 

 

Action : { type, payload } 형태의 객체

- 리듀서 함수에게 전달되는 인자

- 리듀서에게 요구할 상태변경 작업에 대한 정의를 나타내는 객체

 ( 상태 변경 요청서 )

 


 

 

configStore.js

 

 

Reducer : state 변경 함수

- 매개 변수로 기존 상태와 액션 객체를 받음

- 상태 변경 후 최신 상태를 Store에 제공(리턴)

// 중앙 데이터 관리소(store)를 설정하는 부분
import { createStore } from "redux";
import { combineReducers } from "redux";

const rootReducer = combineReducers({
  //리듀스를 묶어놓는 곳
  counter : counter,
  
});
const store = createStore(rootReducer);

export default store;

 

 

createStore()

리덕스의 가장 핵심이 되는 스토어를 만드는 메소드(함수)

리덕스는 단일 스토어이기 때문에 createStore를 단 한 번만 호출!

 

 

 

combineReducers()

출처: https://blog.ldtalentwork.com/2020/07/02/how-to-create-a-custom-redux-saga-template-with-cra

 

 

리덕스는 action -> dispatch -> reducer 순으로 동작한다

 

app이 복잡해지게 되면 reducer 부분을 여러 파일로 나눠야 하는데

이 때 combineReducers는 여러 개의 독립적인 reducer의 반환 값을 하나의 객체 상태로 만들어준다

 

 

 

 

 

export default store;

default 를 해놨기 때문에 index.js에서 import 할 때 { } 필요없다

 

 

 

 


 

 

index.js 

 

 

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import store from "./redux/config/configStore";
import { Provider } from "react-redux";

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

 

 

App을 <Provider>로 감싸주고, configStore에서 export default 한 store를 넣어준다

 

** 너무 세세하게 이해하려고 하지 않기 **

 

 


 

 

App.jsx

 

 

import "./App.css";

function App() {
// 여기에서 store에 접근하여, counter의 값을 읽어오고 싶다!
// useSelector 사용
const data = useSelector((state)=>{
  return state;
})
console.log('data', data)
  return <div>Redux!</div>;
}

export default App;

 

 

컴포넌트에서 스토어를 직접 조회하기 --> useSelector()

 ( ) 안에는 함수

return 하여 console.log로 확인하기

 

 

만약 counter만 확인하고 싶다면?

  const counter = useSelector((state) => {
    return state.counter;
  });
  console.log("counter", counter.number);

 

 

 


 

 

Counter 만들어보기

 

 

 

우선 만들기 전,

우리는 dispatch가 무엇인지 알 필요가 있다

 

dispatch

예를 들어 onClick이 발생하면 action 객체를 가지고 store에 던져주는 역할을 한다 (전달자 함수)

dispatch라는 함수에는 액션을 파라미터로 전달한다(dispatch(action))

그리고 type과 payload를 가지고 있다

 

 

+1 기능 구현해보기

 

1. UI를 만든다

 

 

2. 리듀서에게 number + 1을 하라는 명령을 보낸다

명령이 바로 action 객체

 

const counter = (state = initialState, action) => {
  console.log(state);
  switch (action.type) {
    case "PLUS_ONE":
      return {
        number: state.number + 1,
      };
    default:
      return state;
  }
};

export default counter;

 

 

3. useDispatch 훅 만들기

 const dispatch = useDispatch();

 

 

4. onClick 이벤트 추가

마우스를 클릭했을 때 dispatch(action)이 실행되고, ( )안에 있는 액션객체가 리듀서로 전달된다.

<div>현재 카운트 : {counter.number}</div>
      <button
        onClick={() => {
          dispatch({ type: "PLUS_ONE" });
        }}
      >
        +
      </button>

 


 

 

++) 만약 "PLUS_ONE"이 수백개의 파일에서 쓰여지고,

수정해야 할 일이 있다면 하나하나 수정하기란 번거롭다

그래서 변수에 저장해서 사용하면 휴먼에러를 줄일 수 있다

다른 파일에서도 쓰기 위해 export , import 필수

 

export const PLUS_ONE = "PLUS_ONE";

 

import { PLUS_ONE } from "./redux/modules/counter";

 

액션 객체를 인자로 받아 리듀서를 호출시킨다

 

 

더 간단하게 구현 ▼

 

 action creator :액션 객체도 변수 선언해주기

 

dispatch 안에 있는 객체를 counter.js 에서 만들어준다

(App컴포넌트에서 사용해야 하니까 꼭 export 해주기)

 

 

 

액션 객체 변수 선언

dispatch안에서 만든 함수 호출(return 값을 반환한다)

 

action creator를 생성하여 변수값을 넣어주어 함수 호출

 

 

 


 

입력한 숫자만큼 더해지거나 빼주는 Counter 만들기 (payload)

 

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

div>현재 카운트 : {counter.number}</div>
      <input
        type="number"
        value={number}
        onChange={(event) => {
          setNumber(event.target.value);
        }}
      ></input>
      
     
  //number가 렌더링 될 때마다 실행된다!!
  useEffect(() => {
    console.log("number", number);
  }, [number]);

 

 

input 태그를 관리하는 state를 만들기

실제 렌더링될 때마다 state 값이 변하는지 확인하기 위해

useEffect를 사용하여 console.log에 찍어보았다

 

 

 

 

배열 [number] 가 실행될 때마다 state 값이 변경된다 

 

 


 

counter.js

 

const PLUS_N = "PLUS_N";
const MINUS_N = "MINUS_N";

// payload를 인자로
export const plus_n = (payload) => {
  return {
    type: PLUS_N,
    payload,
  };
};
//payload 만큼 숫자 변화
export const minus_n = (payload) => {
  return {
    type: MINUS_N,
    payload,
  };
};


const counter = (state = initialState, action) => {
  console.log(state);
  switch (action.type) {
    case PLUS_ONE:
      return {
        number: state.number + 1,
      };
    case MINUS_ONE:
      return {
        number: state.number - 1,
      };
      
   //  현재 state 값에서 + payload 값만큼 더해주기
    case PLUS_N:
      return {
        number: state.number + action.payload,
      };
    case MINUS_N:
      return {
        number: state.number - action.payload,
      };
    default:
      return state;
  }
};

 

 

 

 


 

 

App.jsx

 

import { minus_n } from "./redux/modules/counter";
import { plus_n } from "./redux/modules/counter";

 return (
    <>
      <div>현재 카운트 : {counter.number}</div>
      <input
        type="number"
        value={number}
        onChange={(event) => {
          // + --> 자동으로 문자열에서 숫자로 변환 ***
          setNumber(+event.target.value);
        }}
      ></input>
      <br />
      <button
        onClick={() => {
          // dispatch({ type: PLUS_ONE });
          // 함수 호출 시, payload 인자값도 같이
          // 인자값은 바뀌는 state랑 연결돼있다
          dispatch(plus_n(number));
        }}
      >
        +
      </button>
      <button
        onClick={() => {
          // dispatch({ type: MINUS_ONE });
          // dispatch(minus_one());
          dispatch(minus_n(number));
        }}
      >
        -
      </button>
    </>
  );

 

 

 

 

 

 


 

 

'React > Redux' 카테고리의 다른 글

[React] Redux-Toolkit 설치 / counter app / TodoList  (1) 2023.12.23
[React] Redux로 TodoList 만들기  (0) 2023.11.28