2024. 7. 10. 03:11ㆍ상태관리
Redux Overview and Concepts
Redux란?
Redux는 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너입니다. 컴포넌트 각각에서 상태가 수정될 경우에는 언제, 어디서 버그가 발생한 건지 추적하기 어렵습니다. 물론 앱의 규모가 작다면 어렵지 않겠지만요. Redux는 흩어져 있는 상태 관리 로직을 중앙화하여 디버깅과 테스트가 쉬워지게 합니다. 이것이 Redux를 사용하는 이유입니다.
Redux는 애플리케이션의 여러 부분에 걸쳐 필요한 "전역" 상태를 관리하는 데 도움이 됩니다. Redux에서 제공하는 패턴과 도구를 사용하면 애플리케이션의 상태가 언제, 어디서, 왜, 어떻게 업데이트되는지, 그리고 이러한 변경이 발생할 때 애플리케이션 로직이 어떻게 작동하는지 쉽게 파악할 수 있습니다. Redux는 예측 가능하고 테스트 가능한 코드를 작성하도록 안내하므로 애플리케이션이 예상대로 작동할 것이라는 확신을 가질 수 있습니다.
Redux 핵심 개념 및 기본 예제
- 보통의 Redux 앱에는 하나의 루트 리듀서 함수를 가진 단 하나의 저장소(store)가 있습니다.
- 앱의 모든 상태는 저장소 내의 객체 트리에 저장됩니다.
- 객체 트리의 값은 직접 변경할 수 없으며, 액션 객체를 통해서만 변경할 수 있습니다.
- 액션이 객체 트리의 값을 어떻게 바꿀 것인가는 리듀서 함수에 정의되어 있습니다.
- 앱이 커짐에 따라 루트 리듀서를 상태 트리의 서로 다른 부분에서 개별적으로 동작하는 작은 리듀서들로 나눌 수 있습니다.
//https://ko.redux.js.org/introduction/getting-started
//기본 예제
import { createStore } from 'redux'
/**
* 이것이 (state, action) => state 형태의 순수 함수인 리듀서입니다.
* 리듀서는 액션이 어떻게 상태를 다음 상태로 변경하는지 서술합니다.
*
* 상태의 모양은 당신 마음대로입니다: 기본형(primitive)일수도, 배열일수도, 객체일수도,
* 심지어 Immutable.js 자료구조일수도 있습니다. 오직 중요한 점은 상태 객체를 변경해서는 안되며,
* 상태가 바뀐다면 새로운 객체를 반환해야 한다는 것입니다.
*
* 이 예제에서 우리는 `switch` 구문과 문자열을 썼지만,
* 여러분의 프로젝트에 맞게
* (함수 맵 같은) 다른 컨벤션을 따르셔도 좋습니다.
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 앱의 상태를 보관하는 Redux 저장소를 만듭니다.
// API로는 { subscribe, dispatch, getState }가 있습니다.
let store = createStore(counter)
// subscribe()를 이용해 상태 변화에 따라 UI가 변경되게 할 수 있습니다.
// 보통은 subscribe()를 직접 사용하기보다는 뷰 바인딩 라이브러리(예를 들어 React Redux)를 사용합니다.
// 하지만 현재 상태를 localStorage에 영속적으로 저장할 때도 편리합니다.
store.subscribe(() => console.log(store.getState())))
// 내부 상태를 변경하는 유일한 방법은 액션을 보내는 것뿐입니다.
// 액션은 직렬화할수도, 로깅할수도, 저장할수도 있으며 나중에 재실행할수도 있습니다.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
불변성을 지키기 위해 다음과 같이 수동으로 객체를 복사(spread)하고 배열을 조작해야 합니다.
//https://ko.redux.js.org/introduction/why-rtk-is-redux-today
//Legacy hand-written Redux usage
const ADD_TODO = 'ADD_TODO'
const TODO_TOGGLED = 'TODO_TOGGLED'
export const addTodo = text => ({
type: ADD_TODO,
payload: { text, id: nanoid() }
})
export const todoToggled = id => ({
type: TODO_TOGGLED,
payload: id
})
export const todosReducer = (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return state.concat({
id: action.payload.id,
text: action.payload.text,
completed: false
})
case TODO_TOGGLED:
return state.map(todo => {
if (todo.id !== action.payload.id) return todo
return {
...todo,
completed: !todo.completed
}
})
default:
return state
}
}
Redux Toolkit이란?
Redux Toolkit은 효율적인 Redux 개발을 위한 모든 것을 제공하는 공식 도구입니다. Redux 코어뿐 아니라 Redux 앱을 만들 때 필수라고 여겨지는 패키지들(Redux Thunk나 Reselect 등)을 포함하고 있습니다. 또한 저장소 준비, 리듀서 생산과 불변 수정 로직 작성, 상태 "조각" 전부를 한 번에 작성 등의 작업들을 단순화시키는 유틸리티를 포함합니다. 이를 통해 복잡했던 대부분의 작업이 단순해져, Redux 앱을 더 쉽게 만들 수 있습니다.
Redux Toolkit 핵심 개념과 기본 예제
- configureStore는 한 번의 호출로 Redux 스토어를 설정하며, 리듀서를 결합하고 thunk 미들웨어를 추가하고, Redux DevTools 통합을 하는 등의 작업을 수행합니다.
- createSlice는 Immer 라이브러리를 사용하는 리듀서를 작성할 수 있게 해 줍니다. 이를 통해 state.value = 123과 같은 "변형 (mutating)" JS 문법을 spreads 없이도 불변성을 유지하며 업데이트할 수 있습니다.
- Redux Toolkit을 사용하면 모든 액션 생성자와 액션 타입은 자동으로 생성되며, 리듀서 코드는 더 짧고 이해하기 쉬워집니다. 또한, 각 케이스별로 무엇이 업데이트되는지 더 명확히 알 수 있습니다.
//https://ko.redux.js.org/introduction/why-rtk-is-redux-today
//features/todos/todosSlice.js
const todosSlice = createSlice({
name: 'todos',
initialState: [],
reducers: {
todoAdded(state, action) {
state.push({
id: action.payload.id,
text: action.payload.text,
completed: false
})
},
todoToggled(state, action) {
const todo = state.find(todo => todo.id === action.payload)
todo.completed = !todo.completed
}
}
})
export const { todoAdded, todoToggled } = todosSlice.actions
export default todosSlice.reducer
//app/store.js
import { configureStore } from '@reduxjs/toolkit'
import todosReducer from '../features/todos/todosSlice'
import filtersReducer from '../features/filters/filtersSlice'
//configureStore를 사용하여 스토어 설정을 간단하게 줄일 수 있습니다
export const store = configureStore({
reducer: {
todos: todosReducer,
filters: filtersReducer
}
})
이렇듯 Redux Toolkit은 대부분의 Redux 작업을 간소화하고, 일반적인 실수를 방지하며, Redux 애플리케이션을 더 쉽게 작성할 수 있게 해 줍니다. 이것이 바로 Redux 팀에서 RTK를 사용하라고 권장하는 이유입니다.
Redux 애플리케이션 데이터 흐름
리덕스의 데이터 흐름을 이해하기 쉽게 그림으로 표현한 자료가 있습니다. 이를 글로 풀어 설명하면 다음과 같습니다.
1. UI에서 버튼 클릭 이벤트가 발생합니다.
2. 이벤트 핸들러에서 액션 객체를 스토어로 디스패치합니다.
3. 디스패치된 액션은 리듀서 함수에 전달됩니다. 리듀서 함수는 현재 상태(state)와 액션 객체를 인수로 받아 새로운 상태를 계산하고 반환합니다.
4. 스토어의 상태가 변경되면 스토어는 구독 중인 모든 UI 컴포넌트에 상태 업데이트를 알립니다.
5. 각 UI 컴포넌트는 스토어에서 자신이 필요로 하는 상태의 일부가 변경되었는지 확인합니다.
6. 상태가 변경된 것을 감지한 각 UI 컴포넌트는 업데이트된 데이터를 반영하여 다시 렌더링 됩니다.
이상으로 Redux의 핵심 개념과 데이터 흐름에 대해 알아보았습니다. 이어지는 글에서는 Next.js에서 Redux Toolkit 설정하는 법에 대해 정리해보겠습니다.
'상태관리' 카테고리의 다른 글
[Redux] Next.js(app router)에서 Redux Toolkit 사용하기 (0) | 2024.07.13 |
---|