CodeStates/React

[Redux] 상태 관리 라이브러리

디스페어 2022. 4. 11.

프론트엔드 개발에서 상태의 중요성

1. 상태

  • 상태 : UI에 동적으로 표현될 데이터
  • 로컬 상태 : 특정 컴포넌트 안에서만 관리되는 상태
    *다른 컴포넌트와 데이터를 공유하지 않는 폼(form) 데이터 : input box, select box 등과 같이 입력값을 받는 경우
  • 전역 상태 : 프로덕트 전체 혹은 여러 컴포넌트에서 관리되는 상태
    *다른 컴포넌트와 상태를 공유하고 영향을 끼치는 상태 : 데이터 로딩 여부(로딩중), 다크모드, 국제화(Globalization) 설정 등

 

2. 데이터 무결성

  • 전역 상태에서의 데이터 무결성 : 데이터의 정확성을 보장하기 위해 데이터의 변경이나 수정 시 제한을 두어 안정성을 저해하는 요소를 막고 데이터 상태들을 항상 옳게 유지하는 것
  • 무결성을 위한 방법론 : Single source of truth(신뢰할 수 있는 단일 출처) 원칙

 

3. 상태 관리를 위한 툴

  • React Context, Redux, MobX
  • 기능 : 전역 상태 저장소 제공, props drilling(프로퍼티 내려꽂기) 문제 해결

 

 

Redux

  • 자바스크립트 앱에서 예측 가능한 상태 관리를 해주는 상태 관리 라이브러리

 

1. Redux의 세 가지 원칙

1-1. Single source of truth : Store

  • 어플리케이션 상태는 모두 한 곳에서 관리됨
  • 모든 상태(state)는 하나의 저장소(store) 안에 하나의 객체 트리 구조로 저장됨
  • Store : 컴포넌트와는 별개로 상태가 관리되는 오직 하나의 공간
    *컴포넌트에서 state 정보가 필요할 때 store에 접근하여 정보를 가저옴

 

1-2. State is read-only : Action

  • 상태(state)는 읽기 전용(read-only) 데이터이며, 오직 액션만이 상태 교체를 요청할 수 있음
  • react에서 setState 메소드와 비슷하게 action이라는 객체를 통해 state 변경 가능
  • Action : 자바스크립트 객체로 객체 안엔 type을 비롯한 다양한 데이터들이 담김(type 지정 필수)
    *Store에 앱의 데이터를 운반해 주는 역할 : Reducer를 거침
{
  type: "ORDER",
  drink: {
    menu: "Americano",
    size: "Tall",
    iced: true
  }
}

 

1-3. Changes are made with pure functions : Reducer

  • 변경은 순수 함수로 작성되어야 하며, 리듀서(순수함수)를 통해 상태의 최종 값만 설정함
  • Reducer : store에 있는 현재 상태와 Action을 이용해 다음 상태를 만들어 냄
  • Action 객체는 Dispatch에 전달되고, Dispatch는 Reducer를 호출해 새로운 state 생성
    *데이터가 한 방향으로만 흐르게 하기 위한 장치(단방향 데이터흐름)
  • Dispatch : store 관리자

 

2. Redux의 장점

  • 상태를 예측 가능하게 해줌
  • 유지 보수에 유리
  • action과 state log 기록 시 디버깅에 유리
  • 순수함수를 사용하기 때문에 테스트 용이

 

3. action과 state log 기록 확인하기

3-1. Redux DevTools

 

3-2. redux-logger

npm install redux-logger

// store.js
import logger from "redux-logger"

const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(logger))
);
  • redux-logger : 이전 state와 변경 후 state 확인 가능

 

4. Action

export const addToCart = (itemId) => {
  return {
    type: ADD_TO_CART,
    payload: {
      itemId,
      quantity: 1,
    },
  }
}
  • 어떤 액션을 취할 것인지 정의해 놓은 객체
  • type은 필수로 지정, 그 외의 것들은 선택적으로 사용

 

5. Dispatch

const dispatch = useDispatch()

dispatch(addToCart(item.id))
dispatch(removeFromCart(itemId))
dispatch(notify(`장바구니에 ${item.name}이(가) 추가되었습니다.`))
  • Action을 전달하는 메소드
  • dispatch의 전달인자로 Action 객체가 전달
  • Reducer를 호출해 state의 값을 바꾸는 역할 수행

 

6. Store

const store = createStore(rootReducer)
// createStore 메소드를 활용해 rootReducer를 연결
  • state가 관리되는 오직 하나뿐인 저장소
  • createStore 메소드를 활용해 reducer를 연결할 수 있음
  • createStore와 더불어 다른 리듀서의 조합을 인자로 넣어 스토어 생성 가능

 

7. Reducer

Reducer의 Immutability(불변성):
Redux는 변경된 state를 로그로 남기기 위해 immutable한 방식으로 변경해야 함
*mutable하게 고치면 기존 state가 어땠는지에 대한 트래킹 불가
const itemReducer = (state, action) => {
  switch (action.type) {
    case ADD_TO_CART:
      return Object.assign({}, state, {
        // Object.assign() : immutable한 방식으로 변경하기
        cartItems: [...state.cartItems, action.payload]
      })
    default:
      return state;
  }
}
  • 현재의 state와 Action을 이용해서 새로운 state를 만들어 내는 pure function
  • switch문 또는 if문을 통해 코드 작성

 

 

Redux hooks

1. useSelector()

const state = useSelector((state) => state.itemReducer)
  • 컴포넌트와 state를 연결하는 역할 수행
  • 컴포넌트에서 useSelector 메소드를 통해 store의 state에 접근
  • useSelector의 전달인자로는 콜백 함수를 받으며 콜백 함수의 전달인자로는 state 값이 들어감

 

2. useDispatch()

  • Action 객체를 Reducer로 전달해주는 메소드
  • Action이 일어날만한 곳은 클릭 등의 이벤트가 일어나는 컴포넌트
반응형

댓글