[Redux] 상태 관리

jeyoon·2021년 6월 7일
0

Redux란?

  • Redux는 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너
    (= 상태 관리 라이브러리)

  • 리덕스를 쓰면, 상태 관리를 컴포넌트 바깥에서 한다!
    ➡️ 기존에는 부모에서 자식의 자식의 자식까지 상태가 흘렀는데, 리덕스를 사용하면 스토어를 사용하여 상태를 컴포넌트 구조의 바깥에 두고, 스토어를 중간자로 두고 상태를 업데이트하거나 새로운 상태를 전달받는다. 따라서, 여러 컴포넌트를 거쳐서 받아올 필요 없이 아무리 깊숙한 컴포넌트에 있다 하더라도 직속 부모에게서 받아오는 것처럼 원하는 상태값을 골라서 props를 편리하게 받아올 수 있다.

  • Redux는 React와 별개이다.
    리액트가 아닌 다른 라이브러리에서도 쓸 수 있다.

Redux의 3가지 규칙

1. 하나의 애플리케이션 안에는 하나의 스토어가 있다.

하지만 꼭 그래야만 하는 것은 아니다.
특정 업데이트가 너무 빈번하게 일어나거나, 애플리케이션의 특정 부분을 완전히 분리시킬 경우 여러 개의 스토어를 만들 수도 있다. 하지만 그렇게 하면, 개발 도구를 활용하지 못하게 된다.

2. State is read-only

기존 상태를 변경하지 않고 새로운 상태를 생성하여 업데이트한다.
이런 방식으로 업데이트를 하면, 나중에 개발자 도구를 통해 뒤로 돌릴 수도 있고 다시 앞으로 돌릴 수도 있다.

리덕스에서 불변성을 유지해야 하는 이유는 내부적으로 데이터가 변경되는 것을 감지하기 위하여 shallow equality 검사를 하기 때문이다. 이런 식으로 검사를 하면 객체의 변화를 감지할 때 깊숙한 안쪽까지 비교하는 것이 아니라 겉핥기 식으로 비교를 하므로 좋은 성능을 유지할 수 있다.

3. Changes are made with pure functions

변화를 일으키는 함수, 리듀서는 순수한 함수여야 한다.

  • 리듀서 함수는 이전 상태와, 액션 객체를 파라미터로 받는다.
  • 이전의 상태는 절대로 건드리지 않고, 기존 객체에서 변화가 일어난 새로운 상태 객체를 만들어서 반환한다.
  • 똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과값을 반환해야 한다.

3가지 사항을 주의하자. 동일한 인풋이라면 언제나 동일한 아웃풋이 있어야 한다. 실행할 때마다 다른 결과값이 나타나는 로직(예를 들어 new Date() 등)의 경우 리듀서 함수 바깥에서 처리해줘야 한다.

Redux의 장점

  1. 상태를 예측 가능하게 만들어준다.
  2. 유지보수가 쉬움
  3. 디버깅에 유리하다 (action과 state log 기록 시)
  4. 테스트를 붙이기 쉽다.

Redux의 기본 개념

Action

application의 데이터를 store에 운반해주는 역할을 하는 객체.
상태에 어떠한 변화가 필요하면 액션이 발생한다.
액션 객체는 다음과 같은 형식으로 되어 있다.

{
  type: 'TOGGLE_VALUE'
}

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

액션 생성 함수(action creator)

액션 객체를 만들어 주는 함수

function addTodo(data) {
   return {
      type: 'ADD_TODO',
      data
    };
}

const changeInput = text => ({
  type: 'CHANGE_INPUT',
  text
})

Reducer

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

const initialState = {
  counter: 1
};

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

Store

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

Dispatch

디스패치는 스토어의 내장 함수 중 하나이다. 디스패치는 '액션을 발생시키는 것'이라고 이해하면 된다. 이 함수는 dispatch(action)과 같은 형태로 액션 객체를 파라미터로 넣어서 호출한다.
이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜 새로운 상태를 만들어준다.

"Action 객체는 Dispatch에게 전달되고, Dispatch는 Reducer를 호출해서 새로운 state를 생성한다"

Subscribe

구독(Subscribe)도 스토어의 내장 함수 중 하나이다. subscribe 함수 안에 리스너 함수를 파라미터로 넣어서 호출해 주면, 이 리스너 함수가 액션이 디스패치되어 상태가 업데이트될 때마다 호출된다.

const listener = () => {
  console.log('상태가 업데이트됨');
}
const unsubscribe = store.subscribe(listener);

unsubscribe(); // 추후 구독을 비활성화할 때 함수를 호출

Presentational Component / Container Component

  • Presentational Component
    주로 상태관리가 이루어지지 않고, 그저 props를 받아와서 화면에 UI를 보여주기만 하는 컴포넌트

  • Container Component
    리덕스와 연동되어 있는 컴포넌트로, 리덕스로부터 상태를 받아오기도 하고 리덕스 스토어에 액션을 디스패치하기도 한다.

이렇게 분리하는 패턴을 사용하면 코드의 재사용성도 높아지고, 관심사의 분리가 이루어져 UI를 작성할 때 좀 더 집중할 수 있다.

useSelector(), useDispatch()

리덕스 스토어와 연동된 컨테이너 컴포넌트를 만들 때 connect 함수를 사용하는 대신 react-redux에서 제공하는 Hooks를 사용할 수도 있다.

useSelector로 상태 조회하기

const 결과 = useSelector(상태 선택 함수)

useDispatch를 사용하여 액션 디스패치하기

이 Hook은 컴포넌트 내부에서 스토어의 내장 함수 dispatch를 사용할 수 있게 해 준다.

const dispatch = useDispatch();
dispatch({ type: 'SAMPLE_ACTION' });

0개의 댓글