Frontend/Study

[React] React 완벽 가이드 섹션 18 : Redux

BeNI 2022. 9. 27. 21:15
728x90

https://www.udemy.com/course/best-react/

 

【한글자막】 React 완벽 가이드 with Redux, Next.js, TypeScript

Javascript부터 웹 어플리케이션 배포까지, React와 프론트엔드 최신 기술을 가장 쉽고 확실하게 배우는 법

www.udemy.com


1. Redux

1) 개념 : 크로스 컴포넌트, 앱 와이드 상태를 위한 상태 관리 시스템

- 우리의 상태를 변경하고 화면에 표기하는 데이터를 관리하도록 도와주는 역할 

 

2) State의 종류

ⓐ 로컬 state : 데이터가 변경되어서 하나의 컴포넌트에 속하는 UI에 영향을 미치는 상태

👉 useState를 이용하거나 더 복잡하다면 useReducer을 사용하여 관리할수 있음

ⓑ Cross-component state : 다수의 컴포넌트에게 영향을 미치는 상태

👉 prop-chain을 구축하여 상태를 관리해야한다

ⓒ App-Wide State : 전역으로 영향을 미치는 상태  ex) 로그인 상태

👉 프롭체인으로 관리 가능하지만, 복잡하기에 컨텍스트라 리덕스를 사용함

 

3) Context vs Redux

- 컨택스트는 앱 와이드(전역) 상태를 위한 상태관리 시스템

- 컨택스트는 잠재적인 단점이 있다.

  • 상태관리가 굉장히 복잡해 질 수 있음, 설정이 복잡
  • 데이터가 자주 바뀌는 상황에서는 성능에 문제가 있을 수 있음

4) 작동방식

- 하나의 저장소에 전체 애플리케이션의 모든 상태를 저장함

- 컴포넌트가 저장소에서 데이터를 가져와 사용할 수 있음(구독의 개념)

- 하지만 컴포넌트가 직접 저장된 데이터를 조작하지 않는다

- 리듀서 함수가 저장소 데이터의 업데이트를 담당함

- 컴포넌트는 리듀서 함수의 Action을 디스패치하여 리듀서 함수에게 전달함

 

5) 리듀서 함수

- 리덕스 라이브러리에 의해 호출되는 함수

- 항상 2개의 파라미터를 받음(기존의 상태, dispatched 액션)

- 항상 새로운 상태 객체를 리턴해야함, 함수는 pure한 함수여야함 (다른 사이드 이펙트가 실행x)

 

순수 자바스크립트에서 리덕스 사용하기
const redux = require('redux');  // 리덕스를 불러움(설치한 후)


// 리듀서 함수, 이전 상태와 action을 인자로 받음
// 항상 새로운 상태를 리턴함(보통 객체 형태)
const counterReducer = (state = {counter: 0}, action) => {
  if(action.type === 'increment'){
    return {
      counter: state.counter+1
    }
  }
  if(action.type === 'decrement'){
    return {
      counter: state.counter-1
    }
  }
  return state;
};


// 저장소 생성, 매개변수로 리듀서 함수가 들어감
const store = redux.createStore(counterReducer);


// subscriber 정의
const counterSubscriber = () => {
  // createStore로 생성된 저장소에서 사용할수 있는 메서드(상태를 받아옴)
  const lastestState = store.getState(); 
  console.log(lastestState);
};

store.subscribe(counterSubscriber);

// action 정의
store.dispatch({type:'increment'});
store.dispatch({type:'decrement'});

 

 

React에서 리듀서 사용하기

* 절대로 기존의 state를 변경하면 안됨. 객체형태로 새로운 상태를 반환해야한다

왜냐하면 자바스크립트에서 객체, 배열은 참조값이기 때문에 기존의 상태를 변경할시 재정의, 변경 가능성이 있다.

 

1) store을 생성할 파일 생성

- 보통은 src폴더/store 폴더에 생성한다.

 

2) 리듀서 함수를 생성한다.

- 리듀서 함수는 항상 state를 반환해야 하며, 매개변수로 초기 state와 action을 받는다.

const initialCounterState ={counter:0, showCounter: true}; 

const counterReducer = (state = initialCounterState, action) => {
  if(action.type === 'increment'){
    return {
      counter: state.counter+1,
      showCounter: state.showCounter
    }
  }
  if(action.type === 'increase'){
    return {
      counter: state.counter+ action.amount,
      showCounter: state.showCounter
    }
  }

  if(action.type === 'decrement'){
    return {
      counter: state.counter-1,
      showCounter: state.showCounter
    }
  }

  if(action.type === 'toggle') {
    return {
      showCounter: !state.showCounter,
      counter: state.counter
    }
  }
  return state;
}

 

3) store을 생성한다. 

- createStore() 를 이용하여 인자로 리듀서 함수를 받는다.

const store = createStore(counterReducer);

 

3) 리듀서 state가져오기

- store에 저장된 state를 가져오기 위해선 useSelector()를 사용해야한다.

- dispatch()를 사용하여 우리가 생성했던 action type에 따라 state 변경이 가능하다. 

const dispatch = useDispatch();
const counter = useSelector((state) => state.counter);
const show = useSelector((state) => state.showCounter);

const incrementHandler = () => {
    dispatch({type: 'increment'});
  };

  const increseHandler = () => {
    dispatch({type: 'increase', amount: 5 });
  };
  const decrementHandler = () => {
    dispatch({type: 'decrement'});
  };

  const toggleCounterHandler = () => {
    dispatch({type:'toggle'})
  };

 

 문제점 : action 이름을 가져오거나 생성하는 데 있어 실수가 생길 우려 큼, 중복 문제

👉 react-toolkit을 사용한다!

 

 

- 기존 함수 외 createSlice() 사용

import { createSlice } from "@reduxjs/toolkit";

const initialCounterState ={counter:0, showCounter: true}; 

const counterSlice = createSlice({
  name: 'counter',
  initialState: initialCounterState,
  reducers: {
    increment(state){
      state.counter++;
    },
    decrement(state){
      state.counter--;
    },
    increase(state, action) {
      state.counter = state.counter + action.payload;
    },
    toggleCounter(state){
      state.showCounter = !state.showCounter;
    }
  }
});

export const counterActions = counterSlice.actions;
export default counterSlice.reducer;
// store/index.js 

// const store = configureStore({
//   reducer: counterSlice.reducer 
// });  //단일 reducer가져올때 

const store = configureStore({
  reducer: {counter: counterReducer, auth: authReducer}
});  //여러개 가져올때
 const dispatch = useDispatch();
 const counter = useSelector(state => state.counter.counter);
 const show = useSelector(state => state.counter.showCounter);
 
 const incrementHandler = () => {
    dispatch(counterActions.increment());
  };
    const increseHandler = () => {
    dispatch(counterActions.increase(10));
  };
  const decrementHandler = () => {
    dispatch(counterActions.decrement());
  };

  const toggleCounterHandler = () => {
    dispatch(counterActions.toggleCounter());
  };

- 툴킷을 사용했을 때 중복 문제나 코드 유지보수성/가독성이 좋아진다.

 

728x90