일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- JavaScript
- React Testing Library
- 첫 코딩
- nest.js 순환 종속성
- react testing library 비동기 테스트 사용법
- 비동기 테스트
- mock api를 이용한 react 테스트
- mock api 사용법
- react jest
- CSS
- 프로젝트 배포하기
- TodoList 테스트 코드
- jest
- 리액트 테스트
- ScrollView 무한 스크롤
- 첫코딩
- nest.js 순환 참조
- react
- react native 무한스크롤
- 개발
- 리액트 테스트 코드
- nest.js circular Dependency
- TDD방식으로 리액트 테스팅
- FlatList 무한 스크롤
- javascript 테스트
- Testing-library/react
- react 테스트
- React 테스트코드
- HTML
- nest.js forwardRef
- Today
- Total
성장을 위한 기록
redux toolkit 파해쳐보기 기본 개념 정리 및 사용법 본문
Redux-toolkit은 왜 나왔나?
Redux-toolkit은 새로운 개념이 아닌 redux로직을 작성하는 하나의 방법이다. 그럼 기존의 redux와는 어떤 차이가 있을 까?
사람들이 말하길, 그리고 사용하면서 느낀 바로는 redux는 3가지 문제점이 있다.
- redux store 환경이 복잡하다ㅏ.
- redux를 유용하게 사용하려면 많은 패키지를 사용해야한다.
- 하나의 일을 수행하기 작성하는 코드가 너무 많다.
이런 문제점을 해결하기 위해 toolkit이 나왔고, toolkit을 이용하여 코드를 단순하고 간단하게 작성할 수 있다.
toolkit API
configureStore()
import { configureStore } from "@reduxjs/toolkit";
import thunk from "redux-thunk";
import memoReducer from "./modules/memoSlice";
const middlewares = [thunk];
const store = configureStore({
reducer: {
memo: memoReducer,
},
middleware: [...middlewares],
preloadedState,
});
export default store;
위 예제는 간단하게 작성했지만, 더 많은 정보를 전달한다. (reducer
,middlewware
,devTools
,preloadedState
,enhancer
)
- reducer
- 단일함수를 전달하여 스토어의 root reducer로 바로 사용 가능( 위 예제가 그런방식)
- slice reducer들로 구성된 객체를 전달 할 수 있다. 이 경우 cinfigureStore 내부에
combineReducers
함수를 통해 자동적으로 병합하여 reducer를 생성하게 된다.
- middleware
미들웨어 함수를 담는 배열, 사용할 모든 미들웨어를 배열에 담아서 명시할 수 있다.
생략할 경우에는getDefaultMiddleware
를 호출한다.
위 예제에는 thunk 함수를 연결했지만, redux-thunk는 default로 제공한다. - devTools
불리언 값으로 리덕스 개발자 도구를 끄거나 킬 수 있다. default값은 true이다. - preloadedState
스토어의 초기값을 설정할 수 있다. proloadedState에 변수를 담아 연결하면 store에 초깃값 설정이 된다. - enhancers
redux store enhancers의 배열이며, 콜백 함수로 정의할 수도 있다.- 배열로 정의된 경우, 리덕스의 compose 함수로 전달되어 병합된 최종적 enhacer가
createStore
함수로 전달된다. - 콜백 함수로 전달한 경우
applyMiddleware
보다 앞서 추가할 수 있다. 즉 middleware보다 적용되는 순서를 앞서 정할 수 있다.const store = configureStore({ ... enhancers: (defaultEnhancers) => [offline, ...defaultEnhancers], })
- 배열로 정의된 경우, 리덕스의 compose 함수로 전달되어 병합된 최종적 enhacer가
createReducer()
리듀서 함수를 생성하는 util 함수다.
특징으로 immer 라이브러리를 사용하여 mutative
한 형태로 작성해도 immutability
을 유지할 수 있다.
쉽게 생각해서 기본 redux에서는 store값에 불변성을 유지하기 위해 전체 데이터를 새로 저장하는 느낌의 방식을 사용했다. (객체나 배열 값을 변경할 수 없었다.) 하지만 toolkit에서는 immer라는 친구가 이를 알아서 처리해주기 때문에 store에 저장된 객체나 배열을 바로 변경할 수 있다.
또한 리덕스에서는 보통 switch-case문을 사용해 type별로 case를 나눠 관리했고, 중첩된 상태에 대해서는 값을 업데이트할 때 모든 단계에 복사가 필요했다. 이는 코드가 길어지는 범인이자 가독성을 떨어트렸다.
redux-toolkit에서는 액션을 처리하는 방법이 2가지가 있다
- builder callback
- map object
2가지 역할은 동일하지만, typescripte와의 호환성을 위해 첫번째 방법인 builder callback
을 더 선호한다.
나도 이 방법만 사용해봤고, 여기서도 이 방법만 정리해보려한다.
bulder callback은 2가지 방법으로 표기할 수 있다.
extraReducers: (builder) => {
builder
// 연속적으로 작성
.addCase(__getMemo.pending, (state) => {
state.loading = true;
})
.addCase(__getMemo.fulfilled, (state, action) => {
state.loading = false;
state.text = action.payload;
})
/// 라인마다 빌더 메서드를 호출
extraReducers: (builder) => {
builder.addCase(__getMemo.pending, (state) => {
state.loading = true;
});
builder.addCase(__getMemo.fulfilled, (state, action) => {
state.loading = false;
state.text = action.payload;
})
Builder 메서드는 3가지가 있다
- builder.addCase(actionCreator, reducer)
특정 action type과 맵핑되는 case reducer를 추가한다. 다른 2가지 메서드 보다 앞서 작성해야한다. - builder.addMatcher(matcher, reducer)
새로 들어오는 모든 액션에 대해서 사용자가 작성한 matcher 함수와 일치한는지 확인하고 리듀서를 실행한다. - builder.addDefaultCase
그 어떤 case reducer나 matcher 리듀서도 실행되지 않았다면 기본 케이스 리듀서가 실행된다.
위 예제에는 addCase
만 사용했고 아직 addmatcher
을 활용하는 방법을 잘 모르겠다.addDefaultCase
는 addCase에서 pending, error 부분을 합쳐 작성 할 수 있다.
.addDefaultCase((state, action) => {
if (action.meta?.requestStatus === "pending") {
//....
}
if(action.meta?.requestStatus === "rejected"){
//....
}
});
이런 방식으로 사용해봤는데, addCase에서 pending과 rejected가 없다면 그 상황에 defaultCase가 발생하고 그에 따른 로딩과 에러 처리를 한 번에 할 수 있다.
createAction()
앞서 설명하진 않았지만, creactAction과 reacteReducer를 합칠 수 있는 방식이다.
const initialState = { value: 0 } as CounterState
const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment(state) {
state.value++
},
decrement(state) {
state.value--
},
incrementByAmount(state, action: PayloadAction<number>) {
state.value += action.payload
},
},
extraReducers:.....
})
개념적으로 보자면 Slice는 initialState, reudcer 함수들의 객체, slice이름을 받아 이에 맞는 action creator와 action type을 자동으로 생성한다. 따라서 우리는 코드를 더 간단하게 작성할 수 있게 된다.
createAsyncThunk
createAction
의 비동기 버전을 위해 만들었다고 한다.
export const __addMemo = createAsyncThunk(
"memos/ADD_MEMO",
async (payload, thunkAPI) => {
const memoData = await addDoc(collection(db, "memolist"), payload);
return { id: memoData.id, text: payload.text };
}
);
첫 번째 인자로는 action 타입 문자열과 두 번째 인자로는 프로미스를 반환하는 콜백 함수를 인자로 받아서 사용한다.payload
는 dispatch할 때 전달한 data가 들어가게 되고, thunkAPI
는 다양한 내장 함수를 받아 사용할 수 있다.
만약 에러가 발생한다면, Error 인스턴스를 포함하는 rejected promise를 반환하거나 thunkAPI.rejectWithValue
메서드를 사용해 에러를 처리할 수 있다.
createAsyncThunk를 사용하면 비동기 처리를 편하기 할 수 있지만 thunk만 지원한다.
createSelector
데이터를 추출할 때 사용할 수 있다. 나는 지금까지 useSelector
를 사용했다. createSelector
는 이 결점을 보완하기 위한 좋은 방법이다.
우선 createSelector는 memoization을 기반으로 동작한다. 이 의미는 입력 값을 받을 때 저장하고 다음 호출에도 입력 값이 동일하다면 함수가 한 번만 실행되도록 하는 것을 보장한다.
작성하면서
누구한테 정보를 전달하기 보다는 개념을 정리하고 싶은 생각에 작성해봤다. 아직 모르는 부분이 많다. 이해하지 못한 부분도 많았지만, toolkit을 사용하는 방법에 대한 개념을 정리할 수 있었다.
앞으로 얼마나 toolkit을 이용할 상황이 있을지는 모르겠지만, 혼자 사용할 때는 애용할 것 같은 생각이 든다.
참고
Redux-toolkit 공식문서 : https://redux-toolkit.js.org/
Redux Toolkit (리덕스 툴킷)은 정말 천덕꾸러기일까? : http://blog.hwahae.co.kr/all/tech/tech-tech/6946/
'FE (Front End) (구) > React' 카테고리의 다른 글
React에서 Link 사용하여 데이터 전송하기 ! (0) | 2022.06.14 |
---|---|
React(리액트)에서 textarea 줄바꿈 표현하는 방법, splice/ map (0) | 2022.06.10 |
React.memo vs useMemo vs useCallback 간단 정리 차이점 (0) | 2022.06.08 |
React(리액트)에서 meta 태그 사용하기 (helmet) (0) | 2022.06.07 |
React (리액트) 커스텀 훅 사용하기 간단 예제 (0) | 2022.06.03 |