리덕스Redux를 사용하면 dispatch() 훅(useDispatch)을 이용해서 리듀서에 액션을 전달하게된다. 이 때 스테이트state를 업데이트하는 것 만으로 동작이 마무리된다면 리듀서에 액션을 전달하는 것으로 충분하지만 스테이트 업데이트 후 다른 동작을 이어서 해야 한다면 어떻게 해야 할까? 가장 무난한 방법은 Thunk 함수를 이용해서 비동기 액션 생성 함수를 구성하고 프로미스를 반환하는 것이다.
썽크를 사용하는 비동기 액션 생성 함수는 프로미스를 반환할 수 있다. 예를 들어 서버는 JSON 객체를 전달하고 이 JSON 객체를 json() 메서드를 이용해 프로미스로 가져올 수 있다(서버 응답이므로 데이터가 도착하는 시점에 전달하겠다는 프로미스다).
export const checkAuth = (authToken: string|null): ThunkAction<Promise<any>, RootState, unknown, AnyAction> => {
return async function checkAuthThunk(dispatch, getState) {
const response = await fetch('/api/user/checksignin', {
method: 'POST',
headers: {"content-type": 'application/json'},
body: JSON.stringify(authToken)
});
return response.json();
}
}
dispatch() 함수는 전달받은 함수의 반환값(예에서는 프로미스)을 그대로 전달하기 때문에 dispatch() 함수에 then() 메서드를 사용할 수 있다. 즉, dispatch(checkAuth(authToken)) 호출 결과는 checkAuth(authToken)의 반환값인 프로미스를 반환한다.
function WithCheckAuth({InputComponent, loginRequired, props}: WithCheckAuthProps) {
const dispatch = useDispatch<AppDispatch>();
let authToken: string|null = useSelector<RootState, string|null>(state => state.auth.authToken);
useEffect(() => {
dispatch(checkAuth(authToken)).then((resBody) => {
let authenticated: boolean = resBody.authenticated;
const data: {
authenticated: boolean,
authToken: string,
email: string,
username: string,
isLoggedIn: boolean
} = {
authenticated: authenticated,
authToken: resBody.authToken,
email: resBody.email,
username: resBody.username,
isLoggedIn: authenticated
};
dispatch(refreshAuth(data));
if(loginRequired === true){
if(authenticated != true){
navigate('/home');
}
}
});
}, []);
return <InputComponent {...props}></InputComponent>;
}
컴포넌트에서는 전달받은 프로미스를 이용해 DOM에 데이터를 반영하거나 추가적으로 액션 생성 함수를 호출할 수도 있다. 예에서는 서버에 사용자의 로그인 여부를 질의한 후 그 결과를 액션 생성 함수인 refreshAuth()에 전달(dispatch)하고 로그인이 되어 있지 않은 경우 홈(home) 페이지로 이동시키고 있다.
컴포넌트에서 dispatch() 함수의 결과를 프로미스로 받아서 사용하는 경우 비동기 처리(예, 서버에 데이터 요청 등), 처리결과 사용, 스테이트 갱신 과정이 아래와 같이 진행된다고 봐야 한다.
컴포넌트: dispatch(비동기 요청 함수) ↓ 비동기 요청 함수: 비동기 처리&프로미스 반환 ↓ 컴포넌트: 처리결과 사용 ↓ 컴포넌트: dispatch(액션 생성 함수) ↓ 액션 생성 함수: 액션 생성&반환 ↓ 리듀서: 스테이트 업데이트 |
썽크 함수를 사용할 경우 dispatch의 입력값은 액션 객체가 될 수도 있고 썽크 함수가 될 수도 있다. 따라서 이와 같은 입력 타입을 타입 스크립트에 알려주어야 한다. 이를 위해 dispatch를 'useDispatch<AppDispatch>()'와 같이 지정하였다. AppDispatch는 스토어가 생성된 파일 안에서 아래와 같이 정의할 수 있다.
// index.tsx
import { configureStore } from '@reduxjs/toolkit';
const storeWithMiddleware = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(thunk),
devTools: true
});
export type AppDispatch = typeof storeWithMiddleware.dispatch
이 과정을 거쳐 AppDispatch 타입은 rootReducer의 액션 객체에 썽크 미들웨어가 포함되어 정의된다. 따라서 dispatch() 함수에 썽크 함수가 전달되어도 타입스크립트 컴파일러는 오류를 발생시키지 않는다.
■
'프로그래밍 언어 | 컴퓨터 관련' 카테고리의 다른 글
[SQL] SQL PROCEDURE 사용해보기 (0) | 2022.05.29 |
---|---|
프레임워크와 라이브러리 (0) | 2022.02.28 |
[React] React Router v5 → v6 변경 내용 적용 예 (0) | 2021.11.15 |
비대칭 키 암호화 구조 (0) | 2021.10.22 |
[React] 커링(Currying) (0) | 2021.09.13 |
댓글