프론트엔드 개발에서 상태 관리는 애플리케이션의 성능과 직결되며, 특히 리액트와 같은 UI 라이브러리를 사용할 때 더욱 중요해집니다. 상태 관리 라이브러리 중에서 가장 널리 사용되는 것이 바로 Redux입니다. Redux는 애플리케이션의 상태를 중앙에서 관리할 수 있도록 도와주며, 이로 인해 복잡한 UI와 데이터 흐름을 간편하게 처리할 수 있습니다.
1. Redux란 무엇인가?
Redux는 JavaScript 애플리케이션을 위한 상태 관리 라이브러리로, 다음과 같은 주요 특성을 가지고 있습니다:
- Centralized State: 모든 상태를 하나의 저장소(store)에 저장하여 애플리케이션의 상태를 예측 가능하게 관리합니다.
- Immutable State: 상태는 직접 수정할 수 없으며, 항상 새로운 상태를 반환해야 합니다. 이는 상태의 변화를 명확하게 추적할 수 있게 해줍니다.
- Actions: 상태를 변경하는 유일한 방법은 action을 dispatch하는 것입니다. 액션은 상태 변화의 의도를 담고 있는 객체입니다.
- Reducers: 상태 변화의 로직을 포함하는 함수로, 현재 상태와 액션을 인자로 받아 새로운 상태를 반환합니다.
2. Redux의 기본 개념
2.1 Store
Redux의 Store는 애플리케이션의 전체 상태를 관리하는 단일 객체입니다. Store는 다음과 같은 3가지 주요 메서드를 제공합니다:
- getState: 현재 상태를 반환합니다.
- dispatch: 액션을 store에 전달하여 상태를 변경합니다.
- subscribe: 상태 변화에 대한 리스너를 등록합니다.
2.2 Actions
액션은 상태 변화를 유발하는 이벤트를 설명하는 객체입니다. 액션은 항상 type
속성을 가지며, 이 타입을 통해 어떻게 상태를 변경할지를 알 수 있습니다. 기본적인 액션의 형태는 다음과 같습니다:
{
type: 'ACTION_TYPE',
payload: { /* 필요한 데이터 */ }
}
2.3 Reducers
리듀서는 현재 상태와 액션을 받아서 새로운 상태를 반환하는 함수입니다. 상태는 불변성을 잃지 않도록 항상 새로운 객체를 반환해야 합니다. 리듀서는 다음과 같이 정의할 수 있습니다:
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'ACTION_TYPE':
// 새로운 상태 반환
return {
...state,
// 변경된 필드
};
default:
return state;
}
};
2.4 Dispatching Actions
액션을 디스패치하는 것은 상태를 변경하기 위한 수단입니다. 디스패치된 액션은 리듀서에 전달되며, 리듀서는 이를 처리하여 새로운 상태를 생성합니다. 다음은 액션을 디스패치하는 예제입니다:
store.dispatch({
type: 'ACTION_TYPE',
payload: { key: 'value' }
});
3. Redux의 상태 흐름
Redux의 상태 흐름은 다음과 같이 단순화할 수 있습니다:
- User Interaction: 사용자가 UI에서 어떤 액션을 수행합니다.
- Action: 이 액션이 액션 객체로 변환되어 dispatch됩니다.
- Reducer: 디스패치된 액션은 리듀서로 전달되어 새로운 상태를 생성합니다.
- Store Update: 새로운 상태가 Store에 저장됩니다.
- UI Update: React 컴포넌트가 새로운 상태를 기반으로 렌더링됩니다.
4. Redux 설치 및 설정
Redux를 프로젝트에 설치하려면 다음 명령어를 통해 readline과 redux, react-redux를 설치해야 합니다:
npm install redux react-redux
4.1 Store 생성하기
스토어는 애플리케이션의 상태를 관리하는 중앙 저장소입니다. 아래와 같이 간단한 스토어를 생성할 수 있습니다:
import { createStore } from 'redux';
const initialState = {
count: 0
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
const store = createStore(reducer);
4.2 Provider 설정
리액트 컴포넌트가 스토어에 접근할 수 있도록 Provider
로 감싸야 합니다. 아래 예시와 같이 할 수 있습니다:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
ReactDOM.render(
,
document.getElementById('root')
);
5. Redux 사용 예제
Redux의 사용을 보여주는 간단한 카운터 애플리케이션을 작성해 보겠습니다.
5.1 Project Structure
src/
├── App.js
├── Counter.js
├── index.js
└── store.js
5.2 store.js
import { createStore } from 'redux';
const initialState = {
count: 0
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return { count: state.count + 1 };
case 'DECREMENT':
return { count: state.count - 1 };
default:
return state;
}
};
const store = createStore(reducer);
export default store;
5.3 Counter.js
import React from 'react';
import { connect } from 'react-redux';
const Counter = ({ count, increment, decrement }) => {
return (
Count: {count}
);
};
const mapStateToProps = (state) => ({
count: state.count
});
const mapDispatchToProps = (dispatch) => ({
increment: () => dispatch({ type: 'INCREMENT' }),
decrement: () => dispatch({ type: 'DECREMENT' })
});
export default connect(mapStateToProps, mapDispatchToProps)(Counter);
5.4 App.js
import React from 'react';
import Counter from './Counter';
const App = () => {
return (
Redux Counter
);
};
export default App;
6. Redux DevTools
Redux DevTools는 상태를 추적하고 디버깅할 수 있는 훌륭한 도구입니다. 개발 중에는 다음과 같이 스토어를 생성할 수 있습니다:
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
7. 결론
Redux는 리액트 애플리케이션에서 상태를 관리하는 효과적인 도구입니다. 그 구조와 패턴을 이해하고 사용하면 복잡한 UI와 데이터 흐름을 추적하고 관리하는 데 큰 도움이 됩니다. 간단한 카운터 예제를 통해 Redux의 기본 개념을 이해하였다면, 더 복잡한 애플리케이션에서도 Redux를 활용할 수 있을 것입니다. Redux는 강력하지만 그 사용이 항상 필요한 것은 아니므로 애플리케이션의 복잡성에 따라 적절히 사용할 필요가 있습니다.