- [React.js] 리덕스 툴킷 createSlice 놀랍구나!2023년 09월 27일 17시 16분 50초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
1. 리덕스 좀 복잡하네?!
상태관리 라이브러리 리덕스(Redux)는 다음과 같은 구조로 되어 있다.
- Store: 애플리케이션 상태를 저장한다.
- Action: 상태 변경을 요청하는 객체로, type필드와 필요에 따라 payload를 가진다.
- Reducer: 현재 상태와 액션을 받아 새로운 상태를 생성하는 함수로, 불변성을 유지하면서 상태를 업데이트한다.
- Dispatcher: 액션을 리듀서로 보내 상태를 업데이트한다.
Store의 값은 모든 컴포넌트에서 사용할 수 있고, 변경하고자 하는 값을 Action에 담아 Dispatcher를 이용해 Reducer로 보내서 값을 변경할 수 있다. 이런 단순 깔끔한 구조이지만 코드로 작성하면 다소 복잡해진다.
2. 리덕스로 간단한 TODO리스트 구현, 뭔가 복잡?!
간단한 TODO리스트를 리덕스를 써서 구현해 보고자 한다. 2에서는 일반적인 리덕스를 다룬다. 그 이유는 3에서 다룰 createSlice 툴킷과 비교하기 위함이다.
2.1. 리덕스 설치
리덕스와 리액트 리덕스를 설치한다.
npm i redux react-redux
2.2. Action 타입 정의
액션 타입을 상수로 정의한다. 액션 타입은 액션을 식별하는 문자열이다.
// actionTypes.js export const ADD_TODO = 'ADD_TODO'; export const REMOVE_TODO = 'REMOVE_TODO';
2.3. Action 생성자 정의
액션 생성자(또는 생성기)는 액션을 생성하는 함수이다.
// actions.js import { ADD_TODO, REMOVE_TODO } from './actionTypes'; export const addTodo = (text) => ({ type: ADD_TODO, payload: { text, }, }); export const removeTodo = (id) => ({ type: REMOVE_TODO, payload: { id, }, });
2.4. Reducer 정의
initialState는 초기 상태이다. 리듀서와 함께 정의한다. 리듀서는 현재 상태(state)와 액션을 인수로 받는데, 이 액션을 통해 상태가 변경된다. 불변성을 유지하기 위해 스프레드 연산자를 사용하고 있음을 주목하자!
// reducers.js import { ADD_TODO, REMOVE_TODO } from './actionTypes'; const initialState = { todos: [], }; const rootReducer = (state = initialState, action) => { switch (action.type) { case ADD_TODO: return { ...state, todos: [ ...state.todos, { id: state.todos.length + 1, text: action.payload.text, }, ], }; case REMOVE_TODO: return { ...state, todos: state.todos.filter((todo) => todo.id !== action.payload.id), }; default: return state; } }; export default rootReducer;
2.5. Store 설정
스토어를 설정한다. store는 Provider 태그의 속성값이 되고 Provider 태그에 감싸인 컴포넌트는 중앙 집중식 저장소를 이용할 수 있게 된다.
// store.js import { legacy_createStore as createStore } from "redux"; import rootReducer from "./reducers"; const store = createStore(rootReducer); export default store;
2.6. 컴포넌트에서 리덕스 사용
아래 코드는 App 컴포넌트에서 중앙 집중식 저장소를 사용할 수 있게 한 것이다. useSelector는 현재 상태 값을 이용할 때 사용하는 훅이다. useDispatch는 현재 상태를 변경하기 위해 액션을 일으키는 훅이다.
// App.js import React, { useState } from 'react'; import { Provider, useSelector, useDispatch } from 'react-redux'; import store from './store'; import { addTodo, removeTodo } from './actions'; const App = () => { const [text, setText] = useState(''); const todos = useSelector((state) => state.todos); const dispatch = useDispatch(); const handleAddTodo = () => { if (text) { dispatch(addTodo(text)); setText(''); } }; const handleRemoveTodo = (id) => { dispatch(removeTodo(id)); }; return ( <div> <h1>Todo List</h1> <input type="text" value={text} onChange={(e) => setText(e.target.value)} /> <button onClick={handleAddTodo}>Add</button> <ul> {todos.map((todo) => ( <li key={todo.id}> {todo.text} <button onClick={() => handleRemoveTodo(todo.id)}>Remove</button> </li> ))} </ul> </div> ); }; const ReduxApp = () => { return ( <Provider store={store}> <App /> </Provider> ); }; export default ReduxApp;
3. 리덕스 툴킷 createSlice로 안구정화!
이상에서 알아본 리덕스 만으로도 상태 관리에 문제는 없다. 하지만 뭔가 복잡해 보인다?! 아니라고 생각할 수도 있겠지만 리덕스 툴킷인 createSlice를 경험하면 상큼해진다!
3.1. 리덕스 툴킷 설치
아래와 같이 툴킷을 설치해야 하고 만약 리액트 리덕스가 설치되어 있지 않다면 리액트 리덕스(react-redux)도 설치해야 한다.
npm i @reduxjs/toolkit
3.2. 초기 상태, 리듀서, 액션 생성자, 스토어를 한 번에~
말 그대로다. 그냥 다 함께 모아 두면된다. 그냥 그러면 된다!
// redux/store.js // 여기서는 redux라는 폴더를 생성하였다. import { configureStore, createSlice } from '@reduxjs/toolkit'; // 초기 상태 설정 const initialState = { todoList: [], }; // todoSlice 생성 const todoSlice = createSlice({ name: 'todos', initialState, reducers: { addTodo: (state, action) => { state.todoList.push(action.payload); }, removeTodo: (state, action) => { state.todoList = state.todoList.filter((todo) => todo.id !== action.payload); }, }, }); // 액션 생성자 내보내기 export const { addTodo, removeTodo } = todoSlice.actions; // 스토어 생성 export const store = configureStore({ reducer: todoSlice.reducer, });
3.3. 컴포넌트에서 사용
// App.js import React, { useState } from "react"; import { Provider, useDispatch, useSelector } from "react-redux"; import { addTodo, removeTodo, store } from "./redux/store"; function App() { const dispatch = useDispatch(); const todoList = useSelector((state) => state.todoList); const [newTodo, setNewTodo] = useState(""); const handleAddTodo = () => { if (newTodo) { dispatch( addTodo({ id: Date.now(), text: newTodo, }) ); setNewTodo(""); } }; const handleRemoveTodo = (id) => { dispatch(removeTodo(id)); }; return ( <div> <h1>Todo App</h1> <div> <input type="text" value={newTodo} onChange={(e) => setNewTodo(e.target.value)} /> <button onClick={handleAddTodo}>Add</button> </div> <ul> {todoList.map((todo) => ( <li key={todo.id}> {todo.text}{" "} <button onClick={() => handleRemoveTodo(todo.id)}>Remove</button> </li> ))} </ul> </div> ); } const ReduxApp = () => { return ( <Provider store={store}> <App /> </Provider> ); }; export default ReduxApp;
4. 결론은 createSlice!
보다시피 리액트 툴킷의 createSlice를 사용하면 리덕스 코드를 작성하는 과정이 매우 간결해진다. createSlice를 사용하면 액션, 액션 생성자, 리듀서가 한 곳에 정의되어 코드의 가독성이 향상되고, 반복적인 코드 작성을 줄일 수 있다. 또한, 불변성을 유지하는 과정이 간소화되어 리덕스 코드를 더 쉽게 유지 및 관리할 수 있다.
불변성을 유지하는 과정이 간소화된 이유는 리덕스 툴킷이 내부적으로 Immer 라이브러리를 사용하기 때문입니다. Immer는 불변성을 지키면서 상태를 업데이트할 수 있는 강력한 도구이다. Immer를 사용하면 기존 상태를 직접 수정하는 대신에 상태를 복제하고 변경 사항을 반영한 새로운 상태를 반환한다. 이것은 리덕스의 핵심 원칙 중 하나인 불변성을 유지하는 데 매우 유용하다.
따라서 createSlice를 통해 생성된 리듀서 내부에서 상태를 수정하는 코드를 작성할 때, 그냥 직접 변경하는 것처럼 작성하면 된다.
728x90반응형'언어·프레임워크 > React.js' 카테고리의 다른 글
[React.js] 웹 카메라 제어 : 사진 촬영 기능 위해 react-camera-pro 설치, Styled-Components도 필요! (0) 2023.10.12 [React.js] qr-scanner를 리액트에 적용하기! (1) 2023.10.05 [React.js] useState 상태값 초기화로 좋지 못한 사용자 경험 개선 (0) 2023.09.22 [React.js] 모바일에서도 작동하는 텍스트 클립보드 복사 (0) 2023.09.15 [React.js] Socket.io SSL적용 문제 (0) 2023.08.11 다음글이 없습니다.이전글이 없습니다.댓글