리덕스(Redux)란?
자바스크립트 애플리케이션을 위한 상태 관리 라이브러리.
state를 전역으로 관리해주기 위해 사용한다고 한다
리덕스에서 데이터 흐름은 이렇다.
컴포넌트에서 어떤 이벤트가 발생 -> 리듀서에게 특정 액션을 실행하도록 이야기함(dispatch) -> 리듀서가 dispatch로 받은 액션을 실행하고 스토어 내부 상태를 업데이트 -> redux store은 새로 업데이트된 상태를 이용해 컴포넌트를 리렌더링
그래서 Redux Store이 뭔데
객체 저장소. 전체 상태 트리를 보유한다.
클래스가 아니라 메소드를 보유한 객체이다.
리덕스의 state는 읽기 전용이다. 이를 변경하려면 해당 상태에 대한 action을 전달해야 한다.
Reducer?
상태 변경 사항을 결정하고 업데이트된 상태를 반환하는 함수.
state와 action을 가진다.
state는 다 아는 그 state고
그럼 action은 뭐지
Action은 간단한 자바스크립트 객체로, 말 그대로 state에 어떤 액션을 취할지 정해주는 것.
아래에서 만들어볼 간단한 카운터 앱을 생각해보면, + 버튼을 누르면 숫자가 올라가야 하고 - 버튼을 누르면 숫자가 내려가야 한다. 이 두 동작을 어떻게 구분할 거냐 -> Action으로.
action의 type에 따라 동작을 구분하여 리듀서를 작성하고, dispatch를 할 때 그 액션의 타입을 지정해주는 식.
그래서 디스패치가 뭐냐하면
Dispatch
그 액션을 전달해주는 함수이다.
리듀서에게 해당 액션을 발생시켜라! 라고 시키는 역할.
여기까지 보고 간단한 카운터 앱을 만들어보면
일단 리액트 쓸거니까 create react app 해주고(--template typescript로) npm i redux도 설치
카운터를 보여줄 숫자, +와 - 버튼이 필요하니 먼저 App.tsx를 수정해주자
function App() {
return (
<div className="App">
Clicked: times
<button>+</button>
<button>-</button>
</div>
);
}
src 폴더에 리듀서를 관리할 폴더 reducer를 만들어주고 index.tsx를 생성해준다
const counter = (state=0, action: {type: string}) => {
switch(action.type) {
case "INCREMENT":
return state + 1
case "DECREMENT":
return state - 1
default:
return state
}
}
export default counter;
리듀서에는 전술했듯 state와 action이 필요하다.
state는 말 그대로 관리할 state고, 여기서 action은 카운터의 증가와 감소로 두 가지가 필요하니 case문을 사용해 두 액션을 각각 작성한다.
리듀서를 만들었으니 이제 해야 하는 건 해당 리듀서를 redux store로 관리해줘야 한다.
index.tsx로 와서 (리듀서 폴더 안의 index.tsx가 아닌 src 폴더 안의 index.tsx다)
import counter from './reducers'
const store = createStore(counter)
createStore()를 통해 앱의 전체 상태 트리를 보유하는 redux store 저장소를 만들어준다.
앱에는 하나의 스토어만 존재해야 한다고 한다
스토어도 만들었고 그럼 해야되는건 App 컴포넌트에 props를 내려줘야지
index.tsx의 렌더 부분을 수정해주자
const render = () => root.render(
<React.StrictMode>
<App
value={store.getState()}
onIncrement={() => store.dispatch({type: "INCREMENT"})}
onDecrement={() => store.dispatch({type: "DECREMENT"})}
/>
</React.StrictMode>
);
props를 내려줄 때 사용한 함수들
getState() 는 애플리케이션의 현재 상태 트리를 반환한다. 스토어의 리듀서가 반환한 마지막 값과 같다. value는 업데이트되는 카운터 숫자값을 의미하니 스토어에서 getState 하여 App 컴포넌트에 내려준다.
dispatch는 이야기했듯 스토어에게 이 액션으로 state를 변경해라! 하고 명령하는 친구다.
onIncrement에서는 INCREMENT라는 타입의 액션을 취하라고 명령하는 거고,
onDecrement는 DECREMENT라는 타입의 액션을 취하라고 명령하는 것.
각 액션이 state를 어떻게 변경하는지는 리듀서에서 정의했었다.
암튼 이렇게 하고 실행을 하면
동작하지 않는다!
이때 필요한 개념이 상태구독 즉 subscribe이다.
subscribe는 말 그대로 state의 변화를 지켜보는 메소드이다.
store에 subscribe하면 해당 스토어의 현재 상태 트리를 지켜보게 되고, subscribe에 매개변수로 함수를 전달하여 '상태변화가 감지되었을 때 취할 행동'을 지정할 수 있다.
카운터 애플리케이션에서는 state가 변경이 되면(=버튼을 누르면) 컴포넌트 렌더링을 다시 해주면 되니까 index.tsx의 렌더를 다음과 같이 수정해준다.
const render = () => root.render(
<React.StrictMode>
<App
value={store.getState()}
onIncrement={() => store.dispatch({type: "INCREMENT"})}
onDecrement={() => store.dispatch({type: "DECREMENT"})}
/>
</React.StrictMode>
);
render()
store.subscribe(render)
그럼 이제 정상적으로 동작한다. 와!
리덕스에서 데이터가 어떻게 흐르는지는 잘 알아두도록 하자 . . .
리덕스 store은 전체 상태 트리를 가짐
reducer는 state와 action 가짐
createStore(리듀서 함수)를 통해 상태 리덕스 저장소를 만든다
앱에는 하나의 스토어만 존재해야 하고, 만약 리듀서가 여러 개라면 combineReducers로 루트 리듀서를 만들어줄 수 있다. 이건 나중에...
dispatch로 상태 변화를 시킬 수 있다. 액션의 타입을 지정할 수 있다
subscribe로 상태 변화를 구독할 수 있고, 변화가 생겼을 때 취할 행동을 지정할 수 있다
그렇다...
'공부' 카테고리의 다른 글
[Dart] 1. 기본 (1) | 2024.03.20 |
---|---|
[Flutter] Pomodoro app (0) | 2024.03.12 |
타입스크립트(TS) (3)함수, 클래스, 제네릭 (1) | 2024.01.28 |
타입스크립트(TS) (2) 인터페이스, 타입 별칭 (0) | 2024.01.28 |
타입스크립트(TS) (1) 정의, 타입(종류/추론/단언/가드) (0) | 2024.01.28 |