[React Native] Redux-Toolkit์ด๋? (feat. ๊ธฐ์กด Redux์ ๋น๊ต)
์๋ ํ์ธ์. Foma ์ ๋๋ค!
์ ๋ฒ ๊ธ์์ Redux๊ฐ ๋ฌด์์ด๊ณ ์ด๋ป๊ฒ ์ฌ์ฉํ๋์ง์ ๋ํด ๋ค๋ค์๋๋ฐ์.
(์์ง ์ ๋ณด์ ๋ถ๋ค์ ์ฌ๊ธฐ ์์ ๋ณด์๋ฉด ๋ฉ๋๋ค!)
Redux์ ๋ํด ์ฐพ์๋ณด๋ Redux๋ฅผ ์ฌ์ฉํ๊ธฐ ์ฝ๊ณ ํจ์จ์ ์ธ Redux-Toolkit์ด ์๋๋ผ๊ตฌ์.
๊ทธ๋์ ์ค๋์ Redux-Toolkit์ด ๋ฌด์์ด๊ณ ์ ๋ ์ฌ์ฉํ๊ธฐ ์ฝ๊ณ ํจ์จ์ ์ธ์ง์ ๋ํด ์ ๋ฆฌํด ๋ณด๋ ค๊ณ ํฉ๋๋ค.
๋ฐ๋ก ์์ํ ๊ฒ์~
Redux-Toolkit์ด๋?
Redux-Toolkit์ Redux๋ฅผ ๋ง๋ ๊ณณ์์ ๊ณต์์ ์ผ๋ก ํจ์จ์ ์ธ Redux ๊ฐ๋ฐ์ ์ํด ๋ง๋ค์ด์ง ํดํท์ ๋๋ค.
Redux ์์ฑํ๋ ์ฝ๋๋ฅผ ๊ฐ๋จํ๊ฒ ์์ฑํ ์ ์๊ณ , Redux ๋ณด๋ค ํจ์ฌ ํจ์จ์ ์ด๋ฏ๋ก ์ฌ์ฉ์ ๊ณต์์ ์ผ๋ก ๊ฐ๋ ฅํ ๊ถ์ฅํฉ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ค ๋ฉด์์ ๋ ํจ์จ์ ์ด๋ผ๋ ๊ฒ ์ผ๊น์?
๊ธฐ์กด์ Redux์์ ์๋์ ๊ฐ์ 3๊ฐ์ง ๋ฌธ์ ์ ์ด ์์์ต๋๋ค.
- Redux ์คํ ์ด ๊ตฌ์ฑ์ด ๋๋ฌด ๋ณต์กํ๋ค.
- Redux์์ ์ ์ฉํ ์์ ์ ์ํํ๋ ค๋ฉด ๋ง์ ํจํค์ง๋ฅผ ์ถ๊ฐํด์ผ ํ๋ค.
- Redux์๋ ๋๋ฌด ๋ง์ ์์ฉ๊ตฌ ์ฝ๋๊ฐ ํ์ํ๋ค.
์ ๋ฌธ์ ์ ์ Redux-Toolkit์ ์ฌ์ฉํ๋ฉด ํด๊ฒฐํ ์ ์๋ ๊ฒ์ด์ฃ .
Redux๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด Redux-Toolkit์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒ์ ์๋์ง๋ง, ๋ชจ๋ Redux์ฑ์ Redux-Toolkit์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค๊ณ ๋งํฉ๋๋ค.
์๋ก์ด Redux ์ฌ์ฉ์์ด๋ ๊ธฐ์กด ์ ํ๋ฆฌ์ผ์ด์ ์ ๋จ์ํํ๋ ค๋ ์๋ จ๋ ์ฌ์ฉ์์ด๋ Redux-Toolkit์ ์ฌ์ฉํ๋ฉด ์ฝ๋๊ฐ ๋ ๊ฐ์ ๋๊ณ ์ ์ง ๊ด๋ฆฌ๊ฐ ์ฌ์ ์ง๋ค๊ณ ๋งํฉ๋๋ค.
Redux-Toolkit์ด ์ ๊ณตํ๋ ๊ฒ
configureStore
๊ฐ๋จํ๊ฒ Store๋ฅผ ๋ง๋ค ์ ์๋๋ก ๋์์ค๋๋ค,
createReducer
switch๋ฌธ์ ์์ฑํ๋ ๊ฒ ๋์ ๊ฐ๋จํ๊ฒ Reducer๋ฅผ ๋ง๋ค ์ ์๋๋ก ๋์์ค๋๋ค.
createAction
Reducer์ ์์ฑํ ๊ฒ๋ค์ ๊ธฐ๋ฐ์ผ๋ก Action๋ค์ ๋ง๋ค์ด ์ค๋๋ค.
createSlice
reducer์ ์ด๋ฆ, ์ด๊ธฐ์ํ, reducers ๋ฑ์ ๊ฐํธํ๊ฒ ๋ง๋ค ์ ์๋๋ก ๋์์ค๋๋ค.
createAsyncThunk
createAction์ ๋น๋๊ธฐ๋ก ๋ง๋ค ์ ์๋๋ก ๋์์ค๋๋ค.
createSelector
Store์์ ์ํ๋ฅผ ํจ์จ์ ์ผ๋ก ์ ์ฅํ๋๋ก ๋ ์๋๋ค.
๊ธฐ์กด Redux์ ๋น๊ต
์์์ ์ ๊ณตํ๋ ๋ชจ๋ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ง ๋ชปํ์ง๋ง ๊ธฐ์กด Redux์ Redux-Toolkit์ ์ด์ฉํ์ฌ Counter์ฑ๊ณผ Todo์ฑ์ ๋ง๋ค์ด ๋ณด์๋๋ฐ์.
๊ฐ๋จํ ์ฑ์ ๋ง๋๋ ๋ฐ๋ Redux-Toolkit์ด ๊ฐํธํ๋ค๋ ๊ฒ์ ๋๋ ์ ์์์ต๋๋ค.
1. Store
๊ธฐ์กด Redux๋ store๋ฅผ ์์ฑํ ๋ ์๋์ ๊ฐ์ด ์งํ๋ฉ๋๋ค.
Reducer๋ฅผ ์์ฑํ๊ณ combineReducers๋ก ์ฌ๋ฌ Reducer๋ฅผ ํฉ์ณ์ค ๋ค rootReducer๋ฅผ ๋ง๋ค๊ณ RootState ์ธํฐํ์ด์ค๋ฅผ ๋ฐ๋ก ๋ง๋ค์ด ์ค์ผ ํ์ต๋๋ค.
export const rootReducer = combineReducers({
counter: counterReducer,
todoList: todoReducer,
});
export interface RootState {
counter: Counter;
todoList: TodoState;
}
๊ทธ ๋ค์ createStore๋ก ๋ง๋ค์ด ์ค์ผ ํ์ต๋๋ค.
const store = createStore(rootReducer);
Redux-Toolkit์ ์ฌ์ฉํ๋ฉด ์๋์ ๊ฐ์ด ์งํ๋ฉ๋๋ค.
configureStore๋ก ์ฌ๋ฌ reducer๋ฅผ ๋ด์ ๋ง๋ค์ด ์ฃผ๊ณ ,
const store = configureStore({
reducer: {
counter: counterSlice.reducer,
todoList: todoSlice.reducer,
},
});
State ๋ํ store.getState๋ฅผ ํตํด ๊ฐ๋จํ๊ฒ ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค.
export type RootState = ReturnType<typeof store.getState>;
2. Action
๊ธฐ์กด Redux๋ ๋ฐ์ํ Action๊ณผ ActionType ๋ฑ์ ์ง์ ๋ง๋ค์ด ์ค์ผ ํ์ต๋๋ค.
export enum TodoActionType {
tapAdd,
tapDelete,
tapUpdate,
modalOpen,
modalClose,
}
export type TodoPayload = {
todo: Todo;
selectedIndex: number;
};
export type TodoAction = {
type: TodoActionType;
payload: TodoPayload;
};
Redux-Toolkit์์ createSlice์์ ๋ง๋ ๊ฒ์ ๋ฐํ์ผ๋ก action์ ์ถ์ถํด ๋ผ ์ ์๊ธฐ ๋๋ฌธ์ ์ง์ ๋ง๋ค ํ์๊ฐ ์์ต๋๋ค.
3. Reducer
๊ธฐ์กด Redux๋ ์ง์ Reducer๋ฅผ ๋ง๋ค์ด initialState ๋ฑ๊ณผ switch๋ฌธ์ผ๋ก action์ ํ์ ์ ๋ฐ๋ผ ๋๋ ์ค์ผ ํ๊ณ ๊ธฐ์กด state์ ๋ฐ๋์ ๋ค์ ์ ์ฉํด์ผ ํ์ต๋๋ค.
const todoReducer = (
state: TodoState = initialState,
action: TodoAction,
): TodoState => {
let newTodos = state.todos;
switch (action.type) {
case TodoActionType.tapAdd:
newTodos.push(action.payload.todo);
return {
...state,
todos: newTodos,
isModalVisible: false,
};
case TodoActionType.tapDelete:
newTodos.splice(action.payload.selectedIndex, 1);
return {
...state,
todos: newTodos,
};
case TodoActionType.tapUpdate:
newTodos[action.payload.selectedIndex] = action.payload.todo;
return {
...state,
todos: newTodos,
selectedIndex: action.payload.selectedIndex,
isModalVisible: false,
};
case TodoActionType.modalClose:
return {
...state,
isModalVisible: false,
selectedIndex: -1,
};
case TodoActionType.modalOpen:
return {
...state,
isModalVisible: true,
selectedIndex: action.payload.selectedIndex,
};
default:
return state;
}
};
Redux-Toolkit์ ์ฌ์ฉํ๋ฉด createSlice๋ก ๊ฐ Action์ ๋ฐ๋ฅธ reducer๋ฅผ ์์ฑํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
export const todoSlice = createSlice({
name: 'todoList',
initialState,
reducers: {
add: (state, action: PayloadAction<TodoPayload>) => {
let newTodos = state.todos;
newTodos.push(action.payload.todo!);
state.todos = newTodos;
state.isModalVisible = false;
},
delete: (state, action: PayloadAction<TodoPayload>) => {
let newTodos = state.todos;
newTodos.splice(action.payload.selectedIndex!, 1);
state.todos = newTodos;
},
edit: (state, action: PayloadAction<TodoPayload>) => {
state.todos[action.payload.selectedIndex!] = action.payload.todo!;
state.isModalVisible = false;
},
modalOpen: (state, action: PayloadAction<TodoPayload>) => {
state.isModalVisible = true;
state.selectedIndex = action.payload.selectedIndex!;
},
modalClose: state => {
state.isModalVisible = false;
state.selectedIndex = -1;
},
},
});
4. Dispatch
๊ธฐ์กด Redux๋ ์ก์ ์ Reducer์ ์ ๋ฌํ๊ธฐ ์ํด act๋ผ๋ ํจ์๋ฅผ ๋ง๋ค๊ณ
const act = (type: TodoActionType, selectedIndex: number) => {
const payload: TodoPayload = {
todo: todoState.todos[selectedIndex],
selectedIndex: selectedIndex,
};
const action: TodoAction = {
type: type,
payload: payload,
};
dispatch(action);
};
์ก์ ํ์ ์ ์ ๋ ฅํด ์ค์ผ ํ์ต๋๋ค.
act(TodoActionType.modalOpen, -1)
Redux-Toolkit์ ์ด์ฉํ๋ฉด Slice์์ actions๋ฅผ ์ถ์ถํด ๋ธ ๋ค,
const actions = todoSlice.actions;
์๋์ ๊ฐ์ด ์ก์ ์ ์ข ๋ฅ๋ฅผ ์ ๋ฌํ๋ฉด ๋์ต๋๋ค.
const payload: TodoPayload = {todo: null, selectedIndex: -1};
dispatch(actions.modalOpen(payload)
Source Code
์๋๋ก Repository๋ก ์ด๋ํ์ ์ Redux์ Redux-Toolkit์ Counter์ฑ๊ณผ Todo์ฑ์ ๋น๊ตํ ์ ์์ต๋๋ค.
๋๋ ์
์์ง ๋ชจ๋ ๊ธฐ๋ฅ์ ๋ค๋ค๋ณธ ๊ฑด ์๋์ง๋ง ์ค์ ๋ก ํธ๋ฆฌํ๊ณ ์ฝ๋ ์์ด ์ค์ด๋๋ ๊ฒ์ ๋๋ผ๊ฒ ๋์๋ค.
Redux์์ ์์ฃผ ๊ฐ๋ ฅํ๊ฒ ์ฌ์ฉ์ ์ถ์ฒํ๋ ์ ์ธ ์ด์ ๊ฐ ์๋ค๊ณ ์๊ฐํด์ ์์ผ๋ก๋ Redux๋ฅผ ์ฌ์ฉํ ๋ Redux-Toolkit์ ์ด์ฉํด ๊ฐ๋ฐํด์ผ๊ฒ ๋ค.
Reference