React ("리액트를 다루는 기술") - 리덕스란?

김태훈·2023년 1월 18일
0

1. 리덕스란?

리덕스는 리액트 상태 관리 라이브러리이다. 리덕스를 사용하면 컴포넌트의 상태 업데이트 관련 로직을 다른 파일로 분리시켜 효율적으로 관리할 수 있게 된다. 또한, 컴포넌트끼리 똑같은 상태를 공유해야 할 때도 여러 컴포넌트를 거치지 않고 손쉽게 상태 값을 전달하거나 업데이트할 수 있다. 게다가 미들웨어라는 기능을 제공하여 비동기 작업을 훨씬 효율적으로 관리할 수 있게 한다.
-> 정리
1. 상태 업데이트 로직 분리 및 관리
2. 미들웨어를 활용한 비동기 작업의 효율성

2. 리덕스와 관련된 개념 알기

1. 액션

상태에 변화가 필요하면 액션이 발생한다. 이는 하나의 객체로 표현된다

{
	type: 'TOGGLE-VALUE'
}

액션 객체는 반드시 type 필드를 가지고 있어야 하고, 이 값을 액션의 이름이라고 생각하면 된다. 그리고 그 외의 값들은 나중에 상태 업데이트를 할 때 참고해야하는 값이고 이는 작성자 마음이다.

또 다른 예시를 보자

{
	type: 'ADD-TODO',
    data: {
    	id: 1,
        text: '리덕스 배우기'
    }
}

2. 액션 생성 함수

액션 생성 함수는 액션 객체를 만들어 주는 함수이다

  • 예시 1
function addToDo(data){
  return {
    type: 'Add-todo',
    data
  };
}
  • 예시 2
const changeInput = text =>({
  type: 'CHANGE_INPUT',
  text
});

3. 리듀서

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

const initialState = {
  counter:1
};
function reducer(state=initialState,action){
  switch(action.type){
    case INCREMENT :
      return {
        counter:state.counter+1
      };
    default:
      return state;
  }
}

4. 스토어

프로젝트에 리덕스를 적용하기 위해 스토어를 만든다.
한 개의 프로젝트는 단 하나의 스토어만 가질 수 있고, 스토어 안에는 현재 에플리이션 상태와 리듀서가 들어가 있으며, 그 외에도 몇 가지 내장 함수가 있다.

5. 디스패치

디스패치는 스토어 내장함수 중 하나이다. 이 함수는 액션을 발생시킨다.

6. 구독

구독은 스토어 내장함수 중 하나이다.

const listener = () => {
  console.log('상태가 업데이트됨');
}
const unsubscribe = store.subscribe(listener);
unsubscribe(); //추후 구독을 비활성화할 때 함수를 호출

subscribe 함수 내에 리스너 함수를 파라미터로 호출해 주면, 리스너 함수가 액션이 디스패치되어, 상태가 업데이트 될 때마다 호출된다.

3. 리덕스 적용하기

리덕스를 사용할 때에는 액션 타입, 액션 생성 함수, 리듀서 코드를 작성해야 한다. 이 코드들은 각각 다른 파일에 작성하는 방법이 있고, 기능별로 묶어서 파일 하나에 작성하는 방법이 있다.

  • 다른파일에 작성
    다른 파일에 작성하게 되면 종류에 따라 분리가 되므로 편리하지만, 새로운 액션을 만들 때 마다 세 종류의 파일을 모두 수정해야 하는 번거로움이 있다.
  • 한파일에 작성
    액션 타입, 액션 생성 함수, 리듀서 함수를 기능별로 파일 하나에 몰아서 다 작성하는 방식으로 "DUCKS 패턴"이라고 불리운다. 해당 코드에서는 DUCKS패턴을 사용하여 리덕스를 적용해보겠다.

1. 라이브러리 설치

yarn add redux react-redux redux-actions immer redux-devtools-extension

redux : 리액트 상태 관리 라이브러리
react-redux : 기존 바닐라 자바스크립트에서 스토의 내장함수 (dispatch / subscribe)를 사용했었지만, 리액트 어플리케이션에서 리덕스를 사용할 때에는 react-redux라이브러리에서 제공하는 유틸 함수 (connect / Provider)를 사용함 (편리함을 위한 라이브러리이다)
redux-actions / immer : 액션 생성 함수, 리듀서를 작성할 때 해당 라이브러리와 immer 라이브러리를 사용하면 리덕스를 훨씬 더 편하게 사용하게 해준다. redux-actions 에는 액션 생성함수를 createActions, 리듀서를 작성할 때에는 handleActions 함수로 간편하게 사용가능. immer : 불변성 유지하기에는 객체의 구조가 복잡해지거나 객체로 이루어진 배열을 다룰 경우 편리하게 상태를 관리하게 도와준다. (기존에는 spread 연산자와 배열 내장함수를 사용 -> 불변성 유지가 까다로울 수 있다.)

//immer 사용 (produce 함수)
[TOGGLE] : (state,{payload:id})=>
	produce(state,draft => {
  		const todo = draft.todos.find(todo=>todo.id ===id);
  		todo.done = !todo.done;
})

redux-devtools-extension : 리덕스 상태를 브라우저상에서 잘 보이게 하는 확장 프로그램

2. 리덕스 모듈 (골격만 갖추기) 생성

  • auth.js 모듈 생성
import {createAction,handleActions} from 'redux-actions';
const SAMPLE_ACTION = 'auth/SAMPLE_ACTION'; //액션 타입명 (Convention 하게 지음)
//auth 모듈 내에 SAMPLE_ACTION이라는 액션 타입이 존재한다는 뜻
export const sampleAction = createAction(SAMPLE_ACTION);
const initialState = {};

const auth = handleActions(
    {
        [SAMPLE_ACTION]:(state,action)=>state,
    },
    initialState,
);

export default auth;

createAction
createAction 메소드를 사용시, 액션 생성 함수에서 받아온 파라미터를 그대로 payload네 넣는 것이 아니라 변형을 주어 넣고 싶을 수 있다. 예를 들어,

const MY_ACTION = 'sample/MY_ACTION';
const myAction = createAction(MY_ACTION);
const action = myAction('hello world');
/* 결과 : {type : MY_ACTION, payload: 'hello world'} */

인데, 이를 바꾸고 싶다면

const MY_ACTION = 'sample/MY_ACTION';
const myAction = createAction(MY_ACTION,text=>`${text} from goat`);
const action = myAction('hello world');
/* 결과 : {type :MY_ACTION, payload: 'hello world from goat'}
  • 루트 리듀서 생성 (index.js => 기본 리듀서)
import {combineReducers} from 'redux';
import auth from './auth';

const rootReducer = combineReducers({
    auth,
});
export default rootReducer;

나중에 createStore 함수(취소선 무시)를 사용하여 스토어를 만들 때에는 리듀서를 하나만 사용해야 한다. 따라서, 기존에 만들었던 많은 리듀서들을 하나로 합쳐주는 작업을 거쳐여 하는데, 이를 리덕스에서 제공하는 combineReducers라는 유틸 함수로 처리한다.

  • 스토어 생성
    프로젝트의 엔트리 파일 (기본값 "index.js"에) 스토어를 생성후, Provider를 통해 리액트 프로젝트에 리덕스를 적용
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {BrowserRouter} from 'react-router-dom';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import {composeWithDevTools} from 'redux-devtools-extension';
import rootReducer from './modules';

const store = createStore(rootReducer, composeWithDevTools());

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <Provider store={store}>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </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();

profile
기록하고, 공유합시다

0개의 댓글