Mobx Review
네이버 인턴십 6주차 과제를 진행하고 나서 쓴 Mobx 리뷰
- 기존에 Redux를 사용하여 개발한 라이브 방송 다시보기 및 실시간 채팅 뷰어
- 같은 프로젝트를 Redux 대신 Mobx를 사용하여 개발해보았다
기본 구조
- Observable : 관찰받고 있는 STATE
- Computed: state 이용하여 연산된 값
- Action: 상태에 변화를 일으키는 행동
-
Reaction: observable의 변화에 반응하여 일어남
-
autorun : autorun 통해 observable 관찰하고 필요한 리액션 실행
- console 찍는 등 side effect 내포할 땐 computed 대신 autorun 사용
- 리액트 내에서는 리액트 내부 API로 해결 가능하기 때문에 빈번히 사용되진 않음
-
⇒ 개인적으로 Vue 및 Vue의 상태관리 라이브러리인 vuex와 비슷한 점이 많다고 생각.
Vuex가 리덕스와 Mobx를 짬뽕시켜놓은 느낌
Mobx ⇒ Vuex
Observable ⇒ State
Action ⇒ Action + Mutation
Autorun ⇒ Watch (Vue)
코드 비교 (video url fetching action)
redux ver.
const GET_VIDEO_URL = 'video/GET_VIDEO_URL';
const GET_VIDEO_URL_SUCCESS = 'video/GET_VIDEO_URL_SUCCESS';
const GET_VIDEO_URL_ERROR = 'video/GET_VIDEO_URL_ERROR';
// action
export const getVideoUrl = (vid) => async (dispatch) => {
dispatch({ type: GET_VIDEO_URL });
try {
const video = await api.getVideoURL(vid);
dispatch({ type: GET_VIDEO_URL_SUCCESS, video });
} catch (e) {
dispatch({ type: GET_VIDEO_URL_ERROR, error: e });
}
};
// reducer
export default function videoReducer(state = initialState, action) {
switch (action.type) {
case GET_VIDEO_URL:
return {
data: null,
error: false,
loading: true,
};
case GET_VIDEO_URL_SUCCESS: {
return {
data: action.video,
error: false,
loading: false,
};
}
case GET_VIDEO_URL_ERROR:
return {
loading: false,
data: null,
error: action.error,
};
default:
return state;
}
}
mobx ver.
class VideoStore {
@observable data = {};
@observable error = null;
@action
getVideoUrl = async (vid) => {
try {
const video = await api.getVideoURL(vid);
this.data = { ...video };
} catch (e) {
this.error = e;
}
};
}
내가 느낀 장점
코드가 간결, 쉽다
-
불변성 유지 불필요 ⇒ Immutable 이슈 X
- 이렇게 할 필요가 없다
const myReducer = (state, action) => ({
...state,
depth1: {
...state.depth1,
depth2: {
...state.depth1.depth2,
depth3: {
...state.depth1.depth2.depth3,
depth4: action.payload
},
},
},
})
- action type 선언 및 reducer 에서 action type에 따른 return state 정의할 필요 X
- 데코레이터를 사용한 간결한 코드 작성
-
리덕스보다 처음에 이해하기 쉽다
- 리듀서, 액션, 디스패치… 개념 파악하는게 어려웠는데 그에 비해 훨씬 쉽고 명확
객체지향 친화적
- store의 구성요소 및 역할 파악이 쉬움 ⇒ 가독성 👍🏻
-
java class와 비슷한 느낌
-
config 설정으로 state를 setter 통해서만 변경할 수 있도록 캡슐화 가능
- side effect 최소화
-
타 Library 필요성이 낮아짐
-
비동기 동작, 액션 내 액션을 실행시키기 위한 thunk 등의 미들웨어 불필요
- 비동기 동작은 액션에 async 처리,
- 액션 내 액션은 runInAction 등 내장된 기능을 활용하여 모두 해결 가능
-
Computed의 편리함 !
- state의 값을 이용해서 필터링하거나 연산된 값을 반환하기 위해 reselect 등의 라이브러리를 사용하거나 뷰단에서 연산하지 않아도 편리하게 스토어 내에서 연산값을 반환 가능하다
- 편하다…!!!!
똘똘한 성능 - memoization…
-
Memoization
-
computed 연산 시 캐싱
- state 값이 변하지 않을 시엔 캐싱 되어있던 기존 값을 반환
-
redux 사용 시 reselect를 이용하여 처리해야했던 부분들을 알아서 해준다
- observer는 자기가 쓰는 값이 무엇인지 알아서 기억하고, 렌더해야 할 요인이 되는 값들을 자동적으로 추적한다
-
-
액션 수행 시의 트랜잭션
- 상태 변경을 하나의 단위로 묶어 트랜잭션을 수행
- 리액션 내에 여러가지 observable를 참조하고 있고, 해당 observable 들의 변화를 일으키는 액션이 수행될 때, 각각의 observable이 변화할 때 마다 리액션이 일으켜지는게 아니라 한 액션에서 모든 동작이 끝났을 때 리액션을 일으킴
- 렌더링 성능(횟수)에서 큰 차이
내가 느낀 단점
레퍼런스 가뭄
- 특히 Hooks와 함께 사용하는 mobx v6 (mobx-react-lite) 는 레퍼런스가 너무 없다…
- 공식문서에도 예제가 친절하게 풍부하진 않은 느낌
버전 별 차이
- 5.x 이상버전: es6 환경에서 구동
- 프록시를 지원하는 브라우저에서만 사용가능 (IE 사용불가)
-
observable 오브젝트는 콘솔에 찍으면 proxy 오브젝트로 찍히는데, 디버깅 시에 조금 불편함
- toJS 내장함수 사용해서 변환시켜야 원래 object 모양으로 보임
-
브라우저 지원 문제에 따라 4.x 버전을 사용하게 되면 함수 컴포넌트와 호환 불가
- 결국 브라우저 지원을 포기하던가, 구조를 바꾸던가 해야함..ㅠㅠ
리덕스보다 덜 견고한 느낌
- 액션 단에서
this.어쩌구 = { ... }
이렇게 state를 바로 바꾸는게 어떻게 보면 엄청 편한데 나중에 유지 보수 하거나 디버깅시에는 어려울 수 있을 것 같음 - 리덕스는 구조가 딱 틀에 맞추어져 있고 거기에 맞게 코딩을 해야 하는게 번거롭지만, 그것때문에 코드의 견고함이나 안정성이 높아지는 듯
DevTool이 별로다
- redux devtool처럼 다양한 기능 제공 X
- 가독성도 ↓