리덕스란?
전역 상태를 전부 하나의 저장소(store) 안에 있는 객체 트리에 저장되며,
상태를 변경하는 것은 어떤 일이 일어날지를 서술하는 **액션(action)**을 내보내는 디스패치(dispatch) 것이 유일한 방법
액션이 전체 애플리케이션의 상태를 어떻게 변경할지 명시하기 위해서는 **리듀서(reducer)**의 작성이 필요하다.
reducer는 변화를 일으키는 함수로써 전달받은 액션을 가지고 새로운 상태를 만들어서 스토어에 전달한다
이 모든 설계는 데이터가 단방향으로 흐른다는 전제하에 데이터의 일관성을 향상시키고 버그 발생 원인을 더 쉽게 파악하려는 의도에서 출발했다.
그러면 Redux Toolkit는 무엇일까?
- 리덕스를 위한 도구 모음(키트)
- 리덕스를 더 쉽게 사용하기 위해서 나왔다
Redux Toolkit은 Redux 로직을 작성하는 표준 방식으로 만들어졌다.
Redux를 더 쉽게 사용하기 위해? 어떤 문제점이 있었을까?
- Redux의 복잡한 저장소 구성
- Redux에서 유용한 작업을 수행하려면? 많은 패키지 추가
- 많은 상용구 코드 필요
설치를 해보자
yarn add @reduxjs/toolkit
RTK에 포함되어 있는 API는 다음과 같다.
- configureStore()
- createStroe를 추상화한것 단순화된 구성 옵션과 좋은 기본값을 제공하기 위해 래핑
- 자동으로 슬라이드 리듀서를 결합, 제공하는 모든 Redux미들웨어 추가
- redux-thunk 기본적으로 포함, Redux Devtools Extension
- createReducer()
- switch문을 작성하는 대신, 대소문자 감소 함수에 작업 유형의 조회 테이블 제공할 수 있음
- createAction()
- 주어진 액션 유형 문자열에 대한 액션 생성자 함수를 생성한다
- 함수 자체가 toString() 정의되어 있으므로 형식 상수 대신 사용할 수 있음
- createSlice()
- 리듀서 함수의 객체, 슬라이스 이름, 초기 상태 값을 받아 해당 액션 생성자와 액션 유형을 가진 슬라이드 리듀서를 자동으로 생성할 수 있다.
- createAction, createAction 없이 Slice로 액션과 리듀서를 생성 할 수 있다.
즉, Redux에서는 Reducer과 Action을 각각 써주어야했지만, Redux-toolkit은 createSlice만 이용해서 액션과 리듀서를 생성할 수 있다!
/* eslint-disable no-param-reassign */
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
export type NoticeState = {
isShowNotice: boolean;
noticeMessage: string;
};
const initialState: NoticeState = { isShowNotice: false, noticeMessage: '' };
const noticeSlice = createSlice({
name: 'notice',
initialState,
reducers: {
isNotShow: state => {
state.isShowNotice = false;
},
Message: (state, action: PayloadAction<string>) => {
state.isShowNotice = true;
state.noticeMessage = action.payload;
},
},
});
export const { isNotShow, Message } = noticeSlice.actions;
export default noticeSlice.reducer;
위 코드는 createSlice를 이용하여 리듀서, 액션을 한번에 정의한 코드이다.
Notice를 보여주는 변수와, Notice의 안의 메시지 내용을 state를 만들어 props drilling을 방지하기 위해, 전역상태로 빼주었다.
reducer에서 isNotShow의 디스패치는 toastMessage가 안보이는 false값을 뜻한다.
Message가 보일 때는, action 값을 받기 위해 payload를 이용하였다.
즉, dispatch를 사용할 때, Message를 받을 수 있다. (맨 마지막 코드 참고)
isNotShow와 Message는 액션 이름이기 때문에 액션 이름을 export해주고,
만든 리듀서를 export default를 해주었다.
/* modules 내에서 정의한 모듈들을 합쳐주는 역할을 한다. */
import { AnyAction, combineReducers } from '@reduxjs/toolkit';
import { HYDRATE } from 'next-redux-wrapper';
import counter, { CounterState } from './counter';
import notice, { NoticeState } from './notice';
export type State = {
counter: CounterState;
notice: NoticeState;
};
const reducer = (state: State | undefined, action: AnyAction): State => {
switch (action.type) {
/*
SSR작업 수행 시 HYDRATE라는 액션을 통해서 서버의 스토어와 클라이언트의 스토어를 합쳐주는 작업을 수행
next.js에선 서버 사이드와 클라이언트 사이드에서 각각 스토어가 생성되기 때문에 이 둘을 합쳐주는 작업이 필요한데 이것을 hydrate라고 한다
*/
case HYDRATE:
return action.payload;
default: {
return combineReducers({
counter,
notice,
})(state, action);
}
}
};
export default reducer;
const dispatch = useAppDispatch();
const message = useAppSelector(({ notice }) => notice.noticeMessage);
const handleToastMessage = useCallback(() => {
dispatch(noticeActions.Message('테스트입니다. 여기에 메시지 띄울 문구를 작성하면 됩니다.'));
}, [dispatch]);
handleToastMessage 라는 함수를 만들었기 때문에, 온클릭이벤트로 함수를 실행시켜주면 토스트 메시지가 잘 뜨는 것을 확인할 수 있다.