redux

FeelSoo·2022년 4월 25일
0

CodeStates

목록 보기
24/43

redux란?


리덕스는 react에서 가장 많이 사용되는 상태 관리 라이브러리이다. react에서 가장 많이 사용될 뿐 react 라이브러리 위에서 작동한다는 말이 아니다.

리덕스는 react 라이브러리 없이 구현 가능한 라이브러리이다.

그럼 상태란 무엇일까?

리액트에서 상태, 즉 State는 웹 페이지에서 제공되는 기능이다. 다음의 사진을 보자

위의 사진에서 '장바구니에 담기' 버튼을 클릭하면 선택한 물건이 장바구니 페이지에 담기게 되는데 이를 JS 동작 시퀀스로 풀어보면

  1. 버튼이 클릭되면 버튼에 구현되어있던 이벤트 함수가 발동되고

  2. 이벤트 함수는 선언해놓았던 state 초기값을 변경시키게 되고

  3. 이 변경된 state값으로 기본 app화면에 장바구니에 몇 개의 항목이 담겼는지 갯수가 표시되고

  4. 다시 장바구니 탭에 선택 항목들이 담기게 된다.


즉, 버튼이 눌리면 상태값이 변경되며 해당 기능이 작동된다. 이렇듯 state는 웹에 구현된 기능들 ( 삭제 버튼, 새로 고침, 카트에 담기 etc.. ) 이라고 볼 수 있다.

상태 관리란 이런 기능들 ( === state ) 을 개발자 입장에서 직관적이고 수월하게 구현 혹은 수정할 수 있게 작업하는 것을 의미한다.

위에서 언급했듯이 REDUX는 용이한 상태 관리를 위해 만들어진 라이브러리라고 했고 React의 기존 상태 관리를 보완할 수 있다.




redux가 등장한 배경



리액트는 부모 컴포넌트에서 내려준 props를 통해 자식 컴포넌트들의 상태를 변경한다.

위의 예시를 조금 더 풀어보자.

  1. '장바구니에 담기' 버튼을 클릭한다

  2. 상품 정보를 모아놓은 아이템 컴포넌트 ( 메인 컴포넌트의 자식 컴포넌트 ) 에서 이벤트가 발생하여 메인 컴포넌트에 이벤트 결과 값을 전달하게 되고

  3. 이 메인 컴포넌트에서는 결과값으로 인해 상태값이 변경되고

  4. 다시 장바구니 탭 화면을 표시하는 자식 컴포넌트에 변경된 상태값을 전달하고

  5. 장바구니 페이지가 출력된다


    이렇게 리액트에서는 장바구니에 담기 버튼으로 장바구니 탭을 연동시키려면 부모 컴포넌트를 거쳐서 정보를 변경시켰다.

    하지만 기능들이 많지 않은 경우에는 크게 문제가 없었지만 복잡계의 기능들을 구현하게 되면 매번 상위 컴포넌트를 거쳐야 하기 때문에 에러가 발생하면 상위 컴포넌트들을 모두 확인해야 하는 끔찍한 일이 발생할 수 있다.

이를 해결하고자 Redux가 등장했다.



redux 특징



1. single source of truth :

하나의 소스를 가진다. 즉, 모든 컴포넌트는 동일한 곳에서만 제어할 수 있다. 이 하나의 소스는 후에 서술할 store가 되겠다.


2. State is read-only :

state는 읽기 전용이다. 이 말은 즉슨 state값은 직접적으로 변경하지 못한다는 말이다.

( state에 값을 다이렉트로 할당해서 변경시키지 못한다 )

React에서는 state 값을 변경하기 위해 SetState를 사용했다. Redux도 비슷하게 action이라는 객체를 통해서만 가능하다.


3. Changes are made with pure functions :

변화는 순수 함수에 의해서만 일어난다.

즉, Side Effect를 가지고 있는 함수로는 redux를 구현할 수 없게 된다. 리듀서와 연결되는 개념이다



** 순수함수란 ? 함수 내에 인풋을 주었을 때 외부 영향에 따라 값이 변하지 않는 함수

** 부수 효과 ( Side Effect ) --- 함수 내부의 값이 외부에 영향을 주는 것. 혹은 함수 외부의 값이 내부에 영향을 미치는 것.

ex ---- 지역 변수가 전역 변수에 영향을 끼치는 것




redux 구조



1 . action

{
  type: ADD_TO_CART,
  payload: {
    quantity: 1,
    itemId
  }
}

state 에 어떤 변화가 필요할 때 우린 액션이란 것을 발생시키며 이는 하나의 객체이다.

액션은 반드시 type 필드를 가지고 있어야 하며, 그 외의 값은 상황에 따라 넣어줄 수 있다.




2. 액션 생성함수

const addToCart = (itemId) => {
return {
  type: ADD_TO_CART,
  payload: {
    quantity: 1,
    itemId
  }
}
}

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



3 . 리듀서(reducer)


const initialState =
{
"items": [
  {
    "id": 1,
    "name": "노른자 분리기",
    "img": "../images/egg.png",
    "price": 9900 
  }]}


 const itemReducer = (state = initialState, action) => {

switch (action.type) {
  case ADD_TO_CART:
    //TODO
    return Object.assign({}, state, {
      cartItems: [...state.cartItems, action.payload]
    })

  default:
    return state;
}
}

State 에 변화를 일으키는 함수이다.

리듀서는 현재의 State 와 Action 을 인자로 받아 Store 에 접근해 Action 에 맞게 State 를 변경한다.





4 . store

import { compose, createStore, applyMiddleware } from "redux";
import rootReducer from '../reducers/index';
import thunk from "redux-thunk";

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
: compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));

export default store;

Store(스토어)는 상태가 관리되는 오직 하나의 공간이다.



5 . Dispatch


const dispatch = useDispatch()


const handleClick = (item) => {

  if (!cartItems.map((el) => el.itemId).includes(item.id)) {
    dispatch(addToCart(item.id))
    dispatch(notify(`장바구니에 ${item.name}이(가) 추가되었습니다.`))
  }
  else {
    dispatch(notify('이미 추가된 상품입니다.'))
  }
}

디스패치는 리듀서를 호출하는 함수다.

dispatch(action) 식으로 Action 을 인자로 리듀서 함수에게 전달한다.

이렇게 호출을 하면 리듀서 함수가 스토어에 전달된다.




정리하면 다음과 같다.

  1. Action(액션) 객체가 dispatch() 메소드에 전달된다.

  2. dispatch(액션)를 통해 Reducer를 호출한다.

  3. Reducer는 새로운 State를 생성한다.





    위의 사진의 work flow를 살펴보자.



초기 상태

먼저 root reducer 함수를 사용하여 만들어진 리덕스 스토어가 있다.

스토어는 root reducer를 한번 호출하고 리턴 값을 초기 상태로 저장한다.

UI가 처음 렌더링될 때, UI 컴포넌트는 리덕스 스토어의 상태에 접근하여 그것을 렌더링에 활용한다.

또한 그것들은 후에 상태의 변화가 업데이트 되는 것을 구독한다.

업데이트

유저가 버튼을 클릭한다.

앱은 유저의 행동에 맞는 디스패치를 실행해 액션을 일으킨다.

스토어는 이전 상태와 현재 액션으로 리듀서 함수를 실행하고, 그 리턴 값을 새로운 상태로 저장한다.

스토어는 스토어를 구독하고 있던 UI들에게 업데이트 되었다고 알려준다.

스토어의 데이터가 필요한 각각의 UI들은 필요한 상태가 업데이트 되었는지 확인한다.

데이터가 변경된 각 구성요소는 새 데이터로 강제로 다시 렌더링하므로 화면에 표시되는 내용을 업데이트 할 수 있다.

profile
세상은 넓고 배울건 많다

0개의 댓글