redux 사용하기

국물빌런·2021년 1월 19일
0

리덕스는 상태관리 라이브러리다.

리액트에서 state로 상태를 관리하면 컴포넌트간에 state이동이 복잡해져서 힘듬.

이 문제를 해결해주기 위해 상태관리 라이브러리가 나옴.

즉 state이동이 많이 일어나는 상황일수록 리덕스가 빛남.

그럼 state를 어떻게 리덕스에서 관리해주는지 알아보자.

store

일단 리액트의 state들을 모두 모아놓는다는 컨셉을 사용했다.

이 모여있는 은행을 store라고부른다.

당연히 store에는 관리하고 싶은 state들이 들어있다.

reducer

store에 관리하고 싶은 state들이 모여있다.

리액트에서는 state를 변경하려면 useState를 이용해 상태를 변경하는 함수를 사용했다.

즉 state를 직접적으로 바꾸지 못하게 한것이다.

리덕스도 마찬가지로 store내에 state를 직접적으로 변경하지 못한다.

그럼 리액트처럼 state를 변경하는 함수를 제공해주나?

직접 구현해야한다. 이 구현체를 reducer라고 부른다.

즉 reducer는 store의 내용을 변경하는 구현체이다.

store만들기

그럼 바로 store를 만들어보자.

const store = createStore(reducer, initialState);

위와 같이 createStore로 store를 만든다.

인자는 아래에서 설명하겠다

리덕스 규칙중에서 store는 하나만 만들라고 한다.

아직 깊이 안써봐서 왜그런지는 잘 모르겠다.아마 관리하기가 힘들어지나봄

store를 하나만 쓴다는건 모든 상태관리요소들이 모여있다는 의미이다.

그리고 store는 동적으로 생성되는 개념이 아니다.

라우터처럼 어플리케이션 초기 실행시 store를 미리 적용시켜야한다.

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.querySelector('#root'),
);

위와 같이 render시에 Provider로 감싸준다.

이 의미는 store에 저장할 state들을 미리 정의해주어야 한다.

그것이 바로 스토어를 만들때 두번째 인자로 들어가는 initialState이다.

이름이 중요한건 아니고 여튼 관리하고 싶은 state의 초기값들의 집합니다.

아래와 같다.

const initialState = {
  compA: 'a',
  compB: 12,
  compC: null,
};

그럼 다시 논리적으로 생각해보자.

state를 미리 다 정의했다. 그럼 당연히 state를 변경하는 api도 미리 정의가 되어 있어야한다.

이것이 바로 첫번째 인자로 넣은 reducer이다. state가 하나의 객체이니 reducer도 하나의 함수이다.

훅스의 useState같은 형태가 아니라 클래스의 setState같은 형태가 된다.

그럼 setState처럼 comA만 바꾸고 싶어도 comB,comC를 다 바꾸어 주어야 하는가?

맞다. comA만 바꿔고 싶어도 initialState와 같은 형태로 객체의 형태를 모두 유지해주어야 한다.

리듀서는 리턴한 값으로 store의 값을 변경시켜주도록 약속이 되어 있다.

그럼 리듀서는 아래와 같은 형태가 될 수밖에 없다

const reducer = (prevState, action) => { // 새로운 state 만들어주기
  switch (action.type) {
    case 'CHANGE_COMP_A':
      return {
        ...prevState,
        compA: action.data,
      };
    case 'CHANGE_COMP_B':
      return {
        ...prevState,
        compB: action.data,
      };
    case 'CHANGE_COMP_C':
      return {
        ...prevState,
        compC: action.data,
      };
    default:
      return prevState;
  }
};

리듀서는 위와같은 포멧으로 만들어야 한다. prevState는 기존 store에 들어있는 state들의 값이고

action은 바꾸고 싶은 값이다.

action은 객체형태로 되어있는데 보통 type, data로 구성되어 있다.

그럴수밖에 없는게 바꾸고 싶은값만 바꾸는 로직을 하나의 함수에서 분리해서 구현해야하니 그럴수밖에..

switch문으로 type에 따라 바꿀 data와 나머지 state를 구조분해할당으로 분해하도록 되어있다.

어떻게 보면 무식한 방법이다. 아무튼 이제 store를 만들었고 store를 변경할 reducer도 만들었다.

그럼 이제 component에서 store의 state를 가져와보고 변경도 시켜보자.

먼저 store의 compA를 가져와보자

const comA = useSelector((state) => state.comA);

간단하다. 훅스에서는 저렇게 가져오면 된다. class는 좀 복잡하다. 그냥 hooks써라

useSelector는 react-redux모듈에서 제공해주는 hooks를 위한 api이다.

useSelector를 쓰면 아주 간편히 store의 값을 빼올수 있다.

거의 다 왔다. 이제 comA를 변경시키는것만 하면 된다.

일단 소스부터 보면 아래와 같다.

const dispatch = useDispatch();
dispatch({ // action
  type: 'CHANGE_COMP_A',
	data: 'b'
});

위의 설명대로라면 reducer를 호출해주어야 한다.

위에서 설명 안한것이 있는데 우리가 구현한 reducer는 직접적으로 호출하지 않고 dispatch라는 함수가

reducer를 대신 호출해준다. reducer의 인자를 기억하는가?

reducer의 인자는 prevState와 action이었다. 저건 우리가 정하는게 아니라 사전 약속이다.

만약 reducer를 직접 호출한다면 prevState를 계속 불러와야만 한다.

그 이외에도 여러 편의를 위해 dispatch라는 랩핑함수를 호출하여 reducer를 대신 호출하도록 한다.

우리는 reducer에 맞추어 type과 변경할 data를 action이라고 부른다.

근데 저 action은 보통 저렇게 객체로 직접 넣어주기보다는 보통 함수형태로 만든다.

이걸 action Creator라고 부른다

const comAAction = (data) => {
  return { // action
    type: 'CHANGE_COMP_A',
		data: data
  };
};
const dispatch = useDispatch();
dispatch(comAaction('b'));

저렇게 액션함수로 만들면 data가 길어져도 소스가 더 깔끔해진다(사실 잘 못느끼겠지만..)

결국 아래 그림과 같은 구조가 된다.


결국 다른 블로그에서는 일단 용어부터 쭉~설명을 하고 설명이 되어 있다.

그럴수밖에 없을것 같다.근데 당장에 용어부터 보니 더 어렵게 느껴졌다.

핵심 요약

  1. action create를 통해서 action을 생성하여 dispatch에 넣어줌

아 또 이렇게 요약하니 더 어려워진다.ㅋㅋ(dispatch(comAaction('b'));)

  1. dispatch는 reducer를 대신 호출해줌

  2. reducer는 action에서 넘어온 type에따라 동작하여 새로운 state를 리턴

  3. store가 새로운 state로 update됨

  4. 값을 가져올때는 useSelector로~ (어차피 다 리액트에서 쓰잖아?)

profile
국물을 달라

0개의 댓글