1. Redux 란?
:자바스크립트 앱을 위한 상태 관리 라이브러리.
프론트엔드 개발에서 상태(state)란, UI에 동적으로 표현되는 데이터를 의미합니다.
토글 스위치의 on/off 상태부터, 렌더링되는 데이터들까지 프론트엔드 화면은 상태 / 상태 변경이 일어나는 곳 / 상태 변경의 영향을 받는 곳으로 구성되어 있습니다. 이러한 상태들을 React로도 충분히 관리할 수 있기는 하지만, 그것이 상당히... 골치가 아플 때가 있습니다.
예를 들어, props drilling이 5회 이내로 많지 않다면 큰 문제가 되지 않을 수도 있지만, 규모가 크고 구조가 복잡한 서비스의 경우에는 React만으로 상태관리를 하는 경우
1. 코드의 가독성이 매우 나빠짐.
2. 코드의 유지보수 또한 힘들어짐.
3. state 변경 시, props 전달 과정에서 불필요하게 관여된 컴포넌트들 또한 리렌더링되어 성능 저하.
라는 단점이 발생합니다.
이를 해결하기 위해 Redux에서 전역 상태 저장소를 제공하여 이곳에서 상태를 저장하고, 꺼내 쓰는 것입니다.
이러한 상태 관리 툴을 이용함으로써 컴포넌트 안의 복잡한 상태 변경 로직을 분리하여, 보다 단순한 함수 컴포넌트를 만들 수 있게 됩니다.
2. Flux 패턴이란?
: (구) 페이스북에 고안한 단방향 데이터 흐름 아키텍쳐 (unidirectional data flow)
기존의 MVC 아키텍쳐가 갖는 복잡성과 오류 발생 가능성으로 인해, 단방향 데이터 흐름이라는 해결책을 들고 나타난 페북 개발진들...!
이것이 바로 Flux 패턴인데,
- Action: type과 payload data를 액션 객체로 가짐.
- Dispatcher: 액션 발생 시, 디스패처로 액션 객체가 전달되고 콜백 함수를 통해 스토어로 전달함
- Store: 상태 저장소. 모든 상태 변경은 Store에서 일어나며, 직접 변경할 수 없음. 반드시 디스패처 단계를 거친 후 액션을 보내야만 상태값 변경이 가능함.
- View: 상태를 가져와서 보여주고, 사용자로부터 입력 받을 화면을 보여줌.
이러한 구조로 이루어져 있습니다. MVC 아키텍쳐와 달리, 데이터가 단방향으로 흐르는 특징이 있습니다.
2-1. 실제 코드로 구현
이러한 Flux 패턴의 데이터 흐름과 reducer 함수 개념을 도입한 것이 Redux.
Redux로 상태관리하는 코드를 구현한 것.
1. Store 생성하기
import { createStore } from 'redux';
// reducer와 연결하여 store 생성
const store = createStore(rootReducer)
// store를 손쉽게 사용할 수 있게 하는 컴포넌트인 Provider로 store를 사용할 컴포넌트들을 감싸줌.
import { Provider } from 'react-redux';
root.render() {
<Provider store={store}>
<App />
</Provider<
// <App> 컴포넌트 내의 모든 컴포넌트에서 store에 접근이 가능해짐.
2. Reducer 작성하기
const count = 1;
// 첫번째 인자: 초기 상태를 지정한 상태 (지정하지 않을 경우, undefined)
// 두번째 인자: action 객체
// (state, action) => newState (새로운 상태값을 리턴함)
const counterReducer = (state = count, action) => {
// action 객체의 type에 분기하는 switch 조건문
switch(action.type) {
// action === 'INCREASE'일 경우
case 'INCREASE':
return state + 1
// action === 'DECREASE'일 경우
case 'DECREASE':
return state - 1
// action === 'SET_NUMBER'일 경우
case 'SET_NUMBER':
return action.payload
// 해당 되는 경우가 없을 땐, 기존 상태를 그대로 리턴
default:
return state;
}
}
-> reducer는 dispatcher가 action 객체를 넘겨주었을 때 (action이 발생했을 때) 전달받은 type에 따라서 상태를 변경시키는 함수이다. 외부 요인으로 인해 기대한 값이 아닌 값으로 변경되는 영향을 받지 않도록, 순수함수로 작성해야 한다.
3. Action 작성하기
// 각각 payload가 필요 없는 경우, 필요한 경우 (직접 작성)
{type : 'INCREASE'}
{type : 'SET_NUMBER', payload: 5}
// action creator 를 통해 작성하는 경우
const increase = () => {
return {
type: 'INCREASE'
}
}
const setNumber = (num) => {
return {
type: 'SET_NUMBER',
payload: num
}
}
action 객체에서 type은 해당 action 객체가 어떤 동작을 하는지 명시해주는 역할로, 필수로 지정해주어야 한다. 그리고 필요에 따라 payload를 작성해 구체적인 값을 전달한다.
4. Dispatch 작성하기
import { useDispatch } from 'react-redux';
// 컴포넌트 내부에서 작성해주어야 함
const dispatch = useDispatch()
dispatch(increase())
dispatch(setNumber(5))
// dispatch 함수는 이벤트 핸들러 안에서 사용됨. 그리고, action 객체를 reducer 함수로 전달해줌.
const plusNum = () => {
dispatch(increase())
};
<button className='plusBtn' onClick = {plusNum}> + </button>
dispatch는 reducer로 action 객체를 전달해준다. 전달인자로 action 객체를 받는다. useDispatch에서 dispatch 함수를 반환하는 메서드를 이용해 위와 같이 불러와준다.
5. useSelector()로 컴포넌트와 state 연결하기
import { useSelector } from 'react-redux';
// useSelector의 콜백 함수의 인자에 store에 저장된 모든 상태가 담김
// 그대로 리턴을 하게 되면, store에 저장된 모든 state를 사용할 수 있음.
// 컴포넌트 내부에 작성해주어야 함
const counter = useSelector(state => state)
3. Redux의 세 가지 원칙
- Single source of truth: 동일한 데이터는 항상 같은 곳에서 가지고 와야 한다. (Store)
- State is read-only: 상태는 읽기 전용! (Action 객체)
- React에서 상태갱신함수로만 상태를 변경할 수 있었던 것처럼, Redux도 상태를 직접 변경할 수 없다.
- Changes are made with pure functions: 변경은 순수함수로만 가능하다. (Reducer)
참고 자료
https://bestalign.github.io/translation/cartoon-intro-to-redux/
https://velog.io/@alskt0419/FLUX-%EC%95%84%ED%82%A4%ED%85%8D%EC%B3%90%EB%9E%80
https://facebook.github.io/flux/docs/in-depth-overview/
향후 읽어볼 글
https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367