자바스크립트 애플리케이션에서 상태(state)관리를 효율적으로 하기 위한 라이브러리
(주로 React와 함께 사용되지만, 다른 자바스크립트 프레임워크나 라이브러리와도 함께 사용 가능)
우리가 state(상태), 즉 어플리케이션을 변경하고 화면에 표시하는 데이터를 관리하도록 도와줌
(ex. 사용자가 버튼을 클릭하면 그 데이터가 변경되는 등...)
Redux는 애플리케이션의 모든 state를 중앙에서 관리합니다. 이 중앙 상태 저장소(store)를 통해 애플리케이션의 다양한 컴포넌트가 state를 쉽게 공유하고 관리할 수 있도록 도와줍니다. 이를 통해 state가 여러 컴포넌트에 흩어져 있을 때 발생하는 문제를 줄일 수 있다는 장점이 있습니다.
Redux는 단방향 데이터 흐름(unidirectional data flow)을 사용합니다. 즉, 상태를 변경하려면 action이라는 객체를 만들어야 하고, 이 action은 reducer라는 함수를 통해 처리됩니다. reducer는 현재 state와 action을 받아서 새로운 state를 반환합니다. 이를 통해 상태 변경이 예측 가능하고, 디버깅이 쉬워진다는 장점이 있습니다.
Redux에서는 state가 변경될 때 기존 state를 직접 수정하지 않고, 새로운 state 객체를 반환합니다. 이를 통해 state의 변화를 추적하기 쉽고, 애플리케이션의 버그를 줄일 수 있습니다.
Redux는 미들웨어를 사용하여 action을 처리하는 과정을 확장할 수 있습니다. 예를 들어 비동기 작업(예: API 호출)을 처리할 때 미들웨어를 사용해 action을 dispatch할 수 있습니다. 대표적인 미들웨어로는 redux-thunk와 redux-saga가 있습니다.
: state가 하나의 컴포넌트에 종속되어 영향을 미치는 상태
useState(), useReducer() 등을 통해 관리함 ex. User input, Toggle button
: 다수의 컴포넌트에 영향을 미치는 state(상태)
prop chain을 구축하거나 prop drilling을 통해 state를 전달해주어야 함ex. Modal overlay
: state가 앱 전역에 영향을 미치는 상태
prop chain을 구축하거나 prop drilling을 통해 state를 전달해주어야 함React Context 사용ex. User authentication(사용자 인즏 = 로그인)
Q. 다수의 컴포넌트에 영향을 미치는 상태를 관리하는 리액트 컨텍스트가 이미 있는데 리덕스가 필요한가?
: 리덕스는 유동적인 상태 확산이 가능하지만 리액트 컨텍스트는 그렇지 않다.
// ex. 대형 어플리케이션
return (
<AuthContextProvider>
<ThemeContextProvider>
<UIInteractionContextProvider>
<MultiStepFormContextProvider>
<UserRegistration />
</MultiStepFormContextProvider>
</UIInteractionContextProvider>
</ThemeContextProvider>
</AuthContextProvider>
)
: 2018년 당시 도입된 이 컨텍스트는 테마를 변경하거나 인증 같은 저빈도 업데이트에는 아주 좋지만, 데이터가 자주 변경되는 경우에는 좋지 않으며, 유동적인 상태 확산을 대체할 수 없다는 리액트 팀원의 언급이 있었음.
"리액트 컨텍스트는 모든 시나리오와 모든 경우에서 리덕스를 대체할 수 없다."
: 리덕스는 애플리케이션에 존재하는 하나의 중앙 데이터 저장소(전체 애플리케이션의 모든 상태를 저장)
[순서]
1) 컴포넌트가 Action을 발송(dispatch)함
2) Redux는 그 Action을 Reducer로 전달하고 원하는 작업에 대한 설명을 읽게 됨
3) Reducer가 그 작업을 수행
4) Reducer가 새로운 상태를 뱉어내면 중앙 데이터 저장소에 있던 기존 데이터를 대체
5) 데이터 저장소의 상태가 업데이트 되면 구독중인 컴포넌트가 알림을 받게 되고 컴포넌트는 UI를 업데이트 할 수 있게 됨
Reducer(Old State, Dispatched Action)
=> Output: New State Object
리듀서 함수는 Side Effect를 발생시키지 않는 순수 함수가 되어야 함
↪ 동일한 입력값에는 동일한 결과값이 나와야 함
↪ http 요청 등의 효과는 일어나지 않아야 함
// [Setting - Vanilla Javascript]
const redux = require('redux')
// 1. Reducer
const counterReducer = (state = { counter: 0 }, action) => {
if (action.type === 'increment') {
return {
counter: state.counter + 1,
};
}
if (action.type === 'decrement') {
return {
counter: state.counter - 1,
};
};
return state;
};
// 2. Store
const store = redux.createStore(counterReducer);
// 3. Subscriber
const counterSubscriber = () => {
const latestState = store.getState();
};
// Subscribe 지정
store.subscribe(counterSubscriber);
// [Action 발송]
store.dispatch({ type: 'increment' })
store.dispatch({ type: 'decrement' })
// [Result]: { counter: 1 } → { counter: 0 }
※ Reducer 함수는 반드시 "순수 함수"이어야 한다
: 어떠한 side-effect(부수 효과)도 발생시키지 않고, 동기식이어야 한다.
Q. 그렇다면 리덕스로 작업할 때 보내야 하는 HTTP 요청과 같이 부수 효과가 수반되는 일부 작업을 전달할 이 코드는 어디에 넣어야 하는가?
1) 컴포넌트 안에 useEffect()를 사용하여 부수 효과가 발생하는 코드를 작성 → 해당 부수 효과가 완료된 후에만 작업을 전달
2) 자신만의 새로운 Action creater 함수를 생성 → 자동으로 생성된 리덕스 툴킷이 제공하는 것을 사용하지 않을 수 있음
↪ 리덕스는 해당 부수 효과에 대해 아무것도 알지 못함
※ redux-thunk란?
: 특정 작업을 나중에 하도록 미루기 위해 함수 형태로 감싼 것
1) 일반 액션 생성자
: 매개변수를 받아서 액션 객체를 생성하는 작업을 담당
↪ 만약 특정 액션이 몇초뒤에 실행하게 하거나, 현재 상태에 따라 무시되게 하는 것은 불가능
2) redux-thunk
: 조건에 따라 액션을 dispatch 하거나 무시할 수 있음
보통의 액션 생성자는 하나의 액션 객체를 생성할 뿐이지만 redux-thunk를 통해 만들게 되면 그 내부에서 여러가지 작업을 할 수 있기에 활용도가 높아짐