COCO World

[React/리액트] Redux 사용방법과 구조 본문

Front-End/React

[React/리액트] Redux 사용방법과 구조

코코월드주인장 2023. 2. 16. 23:52

1. Redux 설치

리액트에서 리덕스를 사용하기 위하여 패키지 설치를 해줘야 한다.

// 한번에 설치하기
yarn add redux react-redux

// 아래처럼 따로 설치하기
yarn redux
yarn react-redux

 

2. 폴더 구조 생성

   [1] src 폴더 안에 redux 폴더를 생성

   [2] redux 폴더 안에 config, modules 폴더를 생성

   [3] config 폴더 안에 configStore.js 파일을 생성

 

각각 폴더와 파일의 역할

  • redux :  리덕스와 관련된 코드를 모두 모아 놓을 폴더
  • config : 리덕스 설정과 관련된 파일들을 놓을 폴더
  • configStore : '중앙 state 관리소'인 Store를 만드는 설정 코드들이 있는 파일
  • modules : 우리가 만들고 필요한 State들을 모아둔 파일. 이것이 곧 하나의 모듈.

3. 설정 코드 작성

  • src/configStore.js
import { createStore } from "redux";
import { combineReducers } from "redux";

/*
1. createStore()
리덕스의 가장 핵심이 되는 스토어를 만드는 메소드(함수) 입니다. 
리덕스는 단일 스토어로 모든 상태 트리를 관리한다고 설명해 드렸죠? 
리덕스를 사용할 시 creatorStore를 호출할 일은 한 번밖에 없을 거예요.
*/

/*
2. combineReducers()
리덕스는 action —> dispatch —> reducer 순으로 동작한다고 말씀드렸죠? 
이때 애플리케이션이 복잡해지게 되면 reducer 부분을 여러 개로 나눠야 하는 경우가 발생합니다. 
combineReducers은 여러 개의 독립적인 reducer의 반환 값을 하나의 상태 객체로 만들어줍니다.
*/

const rootReducer = combineReducers({}); 
const store = createStore(rootReducer); 

export default store;
  • index.js
// 원래부터 있던 코드
import React from "react";
import ReactDOM from "react-dom/client";
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(

	//App을 Provider로 감싸주고, configStore에서 export default 한 store를 넣어줍니다.
  <Provider store={store}> 
    <App />
  </Provider>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

 

3. Redux 모듈의 구성요소 살펴보기

[1] initialState : 초기 상태값

   단어 그대로 초기 상태값이다. 즉, 어떤 State의 초기값을 정해주는 것이다.

   우리가 useState를 사용했을 때 괄호 안에 초기값을 지정해주는 것과 같은 이치로 이해하자

// 초기값이 0
const initialState = 0;

// 초기값이 0이 있는 배열 
const initialState = [0];

// 초기값이 number = 0, name = '석구'인 객체
const initialState = {
	number: 0,
	name: '석구'
};

 

[2] Reducer = 변화를 일으키는 함수

   우리가 useState()를 사용할 때, 상태값을 변경하기 위해 setState()를 사용하는 것처럼, 리듀서가 이 역할을 해준다.

// src/redux/modules/counter.js


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

export default counter; // 여기

 

[3] Reducer 함수 모듈을 스토어에 연결하기

   여태까지 모듈파일에서 초기 상태값과 리듀서 함수를 작성했다. 아직까지 모듈과 스토어가 각각 따로 분리되어 있는 상태이기 때문에

   우리가 만든 State를 스토어에서 꺼낼 수 없다. 이제 우리가 만든 모듈을 스토어에 연결 시켜줘야 한다.

// src/redux/modules/config/configStore.js


// 원래 있던 코드
import { createStore } from "redux";
import { combineReducers } from "redux";

// 새롭게 추가한 부분
import counter from "../modules/counter";

const rootReducer = combineReducers({
  counter: counter, // <-- 새롭게 추가한 부분
});
const store = createStore(rootReducer);

export default store;

 

4. 스토어와 모듈 연결 확인하기

[1] useSelector = 스토어 조회

   우리가 생성한 모듈을 스토어에 잘 연결했는지 확인하는 방법은 컴포넌트에서 스토어를 직접 조회하면 된다.

   컴포넌트에서 Redux Store를 조회하고자 할때는 useSelector라는 react-redux hook을 사용해야 한다.

// 1. store에서 꺼낸 값을 할당 할 변수를 선언합니다.
const number = 

// 2. useSelector()를 변수에 할당해줍니다.
const number = useSelector() 

// 3. useSelector의 인자에 화살표 함수를 넣어줍니다.
const number = useSelector( ()=>{} )

// 4. 화살표 함수의 인자에서 값을 꺼내 return 합니다. 
// 우리가 useSelector를 처음 사용해보는 것이니, state가 어떤 것인지 콘솔로 확인해볼까요?
const number = useSelector((state) => {
	console.log(state)
	return state
});

 

[2] App.js

// src/App.js

import React from "react";
import { useSelector } from "react-redux"; // import 해주세요.

const App = () => {
  const counterStore = useSelector((state) => state); // 추가해주세요.
  console.log(counterStore); // 스토어를 조회해볼까요?

  return <div></div>;
}

export default App;

 

4. Redux의 흐름

[1] View에서 액션이 일어난다.

[2] dispatch에서 action이 일어나게 된다.

[3] action에 의한 reducer 함수가 실행되기 전에 middleware가 작동한다.

[4] middleware 에서 명령내린 일을 수행하고 난뒤, reducer 함수를 실행한다.

[5] reducer 의 실행결과 store에 새로운 값을 저장한다.

[6] store의 state에 subscribe 하고 있던 UI에 변경된 값을 준다.

 

** 흐름 정리 **

  • 액션 객체란, 반드시 type이란 key를 가져야 하는 객체이며, reducer로 보낼 '명령'이다.
  • 디스패치란, 액션 객체를 reducer로 보내는 '전달자' 함수 이다.
  • 리듀서란, 디스패치를 통해 전달받은 액션객체를 검사하고, 조건이 일치했을 때 새로운 상태값을 만들어내는 '변화를 만들어내는' 함수이다.
  • 디스패치(dispatch)를 사용하기 위해서는 useDispatch()라는 훅을 사용한다.
  • 액션객체 type의 value는 대문자로 작성한다.

 

5. Redux의 Refactoring (action creators, action values) 

[1] payload 

   : 리듀서에게 액션 객체를 보낼 때, 목적어가 생긴 경우, 목적어도 액션 객체에 담아서 같이 보내야한다. 이럴 때 사용한다.

  • 리듀서로 보내는 액션객체에 어떤 정보를 담아 같이 보내고자 한다면 payload를 사용하자
  • payload는 Action Creator를 생성할 때 매개변수에 자리에서 받을 준비를 하고, 반환하는 액션객체에 payload라는 key와 받은 매개변수를 value로 하여 구현한다.
  • 리듀서에서 payload를 사용하고 할 때는 action.payload로 사용할 수 있다

[2] Action Creator

   : 만약 우리가 액션객체의 value를 변경할 일이 생긴다면 코드상으로 4군데에서 변경해야 한다. 그치만 대규모 프로젝트가 될 경우,

     하드  코딩이 곤란하기 때문에 액션객체를 한 곳에서 관리할 수 있도록 '함수'와 액션 value를 상수로 만들어야 한다.

  • 액션객체를 만드는 함수를 Action Creator라고 한다.
  • Action Creator는 모듈 파일안에서 생성된다.
  • 액션객체의 type value로 상수로 생성해서 관리한다.
  • Action Creator를 사용하면, 여러가지 문제점을 해소할 수 있다.