[TIL] 20210519_Redux Again

BANSEOK SUH·2021년 5월 19일
0

TIL

목록 보기
17/22
post-thumbnail

리덕스를 배우면서 중요한 개념들을 코드 예시와 함께 남겨보려 합니다.

Store

애플리케이션의 상태 관련 모든 로직을 담당하는 저장소라고 볼 수 있습니다.

src 디렉토리 아래에 store.js파일을 생성하고, store관련 로직들을 이 곳에 구현했습니다.

구현하면서 중요하다고 생각됐던 키워드 중심으로 살펴보겠습니다.


createStore

먼저 store라는 공간을 만들어야 합니다. 상태를 관리하는 곳이죠. Redux 라이브러리의 내장 메서드인 createStore 메서드를 사용합니다. 그 과정은 다음과 같습니다.

  1. redux 라이브러리로부터 createStore를 받아옵니다.
  2. 그리고 createStore 메소드를 통해 store를 만들어줍니다.
// createStore 받아오기
import { createStore } from 'redux';

...

// store 생성
const store = createStore(reducer);

...

// store export하기
export default store;

2번 과정에서 createStore 메서드를 사용할 때, 그 인자로 reducer 함수가 전달되어야 합니다.
그렇지 않으면 브라우저에서 에러를 던져줍니다.


그렇다면 createStore 메서드가 요구하는 reducer 함수를 만들어봐야겠지요?


reducer

action객체가 store에 전달될 때에, 어떻게 상태를 업데이트해야 하는 지를 정의해주는 함수입니다.
그리고 새롭게 정의된 state를 리턴해주죠.

reducer 함수를 만드는 것이 redux의 꽃이라고 할 수 있습니다. 매우 중요하면서 복잡할 수 있기 때문입니다.


reducer 함수는 인자로 state와 action객체를 전달받습니다.
기존의 state와 action 객체를 결합하여 새로운 상태를 만들어주는데요,


여기에서 매우매우매우 중요!!!!!한 것은,

새로운 상태를 리턴한다는 것입니다.


아래의 코드를 살펴보시죠!

// state와 action을 인자로 받습니다. state가 없으면 빈 배열을 사용합니다.
const reducer = (state = [], action) => {
  
  switch (action.type) {
    case ADD: 
      return [{ text: action.text, id: Date.now() }, ...state];
      
    case DELETE:
      return state.filter(toDo => toDo.id !== action.id);
      
    default:
      return state;
  }
}

reducer 함수는 어떻게 상태를 업데이트해야 하는 지를 정의한다고 했었죠?
위와 같이 action의 type에 따라 switch-case 구문을 사용하여 분기처리를 해줍니다.
action 객체의 type이 ADD라면 그에 해당하도록 상태를 새로 만들어주고, 새로 정의된 상태를 리턴합니다.

기존 state에 push나 unshift와 같은 메서드를 사용하지 않고, 새로운 상태를 만들어서 리턴합니다.
이게 포인트입니다!


🤔 그런데 reducer는 기존의 state와 action 객체를 전달받는데, action 객체는 1) 어디에서 어떻게 만들어지고, 2) 어떻게 전달되는 걸까요?


흔히 actionCreator라고 불리는 함수를 직접 정의합니다. action 객체를 리턴해주는 함수죠.


actionCreator

타입에 따라 다른 action을 리턴시키는 함수들을 만들어보겠습니다.

// 추가할 때의 action
const addToDo = (text) => {
  return {
    type: ADD,
    text
  }
}

// 삭제할 때의 action
const deleteDoTo = (id) => {
  return {
    type: DELETE,
    id: parseInt(id)
  }
}

위 두 함수는 해당 인자를 전달받으면 action 객체를 리턴해줍니다. 그리고 전달받은 action 객체를 reducer로 전달하죠. 그래서 actionCreator라고 불리는 것 같습니다.


위 1)에 해당하는 질문에 대한 답이 해결되었습니다. 답은 actionCreator였지요.
하지만 2)에 해당하는 질문, 어떻게 전달되는 건 지에 대해서는 아직 해결되지 않았죠.


그 해답의 열쇠는 dispatch 함수가 갖고 있습니다.


dispatch

코드에 등장하는 mapDispatchToProps에 대해서는 mapStateToProps와 함께 따로 정리하겠습니다.

// 게시글 추가 이벤트
function mapDispatchToProps(dispatch, ownProps) {
  return {
    addToDo: (text) => dispatch(actionCreator.addToDo(text))
  };
}

...
// 게시글 삭제 시 이벤트
function mapDispatchToProps(dispatch, ownProps) {
  return {
    onBtnClick: () => dispatch(actionCreator.deleteDoTo(ownProps.id))
  }
}

위의 코드는 버튼 클릭 시에 게시글이 추가, 삭제되는 이벤트를 처리해준 코드입니다.

쉽게 설명하면, 버튼 클릭 시에 dispatch 메서드를 호출하는데요, 그 인자로는 action 객체가 전달되어야 합니다.

dispatch 메서드가 action 객체를 안고 호출이 되면, 위에서 설명한 reducer 함수가 실행이 됩니다.
신기하게 이어지는데, 그 내부 사정이 궁금하긴 하네요.


그저 받아들여야 할 것은, dispatch 함수는 action 객체를 reducer 함수로 전달해준다는 것! 입니다.


getState

store의 현재 상태를 조회할 때 사용됩니다.

예시

store.getState()

Tips

  • action을 만들어 줄 때, type을 문자열로 하기보단 변수로 선언을 해줘서 변수를 사용하는 것이 좋습니다. 오타로 인한 에러를 잡기에 편하고, 유지보수에도 용이합니다.
// type을 변수로 선언
const ADD = 'ADD';
const DELETE = 'DELETE';

// actionCreator에서 type을 위에서 지정한 변수로 설정해줍니다.
const addToDo = (text) => {
  return {
    type: ADD,
    text
  }
}
  • reducer 함수 안에서 타입에 따른 분기처리를 할 때, if ~ else 구문으로 할 수도 있습니다. 하지만 switch ~ case 구문을 사용하면 가독성에 더 좋습니다.



또 중요하게 생각됐던 connect 메서드, mapStateToProps, mapDispatchToProps 함수에 대해서는 따로 다루도록 하겠습니다!

profile
HelloBanny

0개의 댓글