
이전 글: Next.js 13버젼 + 리덕스 툴킷
참고자료
Redux를 사용할 때 모든 상태관리는 reducer로 상태를 바꾸고 selector로 읽는다.
하지만 비동기를 사용 할 때 그대로 reducer로 처리하면 리듀서에 억지로 비동기 작업을 붙여야 하고 이것은 안티패턴이 된다.
이런 때에 Redux Thunk를 사용하여 비동기 요청을 처리한다.
사실 진짜 간단하니까 이 글을 통해 기본만 봐도 응용할 수 있을 것이다.
설치부터 하자고?
리덕스 툴킷에는 비동기 로직을 위한 Thunk 애드온이 기본적으로 포함되어 있다.

slice의 reducers에 정의하는게 아니라 createAsyncThunk()메서드를 통해 따로 먼저 선언해줘야 한다.
createAsyncThunk는 액션 타입 문자열과 프로미스를 반환하는 함수를 받아서
pending/fulfilled/rejected의 3가지 액션 타입을 디스패치해주는 thunk를 생성해준다.
src/redux/slices/auth-slice.ts
logIn: (State, action: PayloadAction<string>) => {
return {
value: {
isAuth: true, // 로그인 했으니까 true
username: action.payload,
uid: "uid",
isModerator: false,
},
};
위 코드가 저번 리덕스 툴킷에서 만들어둔 logIn 리듀서이다.
createSlice() 의 reducers: {}안에 선언했었지만 이제 밖으로 빼서 작성해야 한다.
src/redux/slices/auth-slice.ts
const logIn = createAsyncThunk(
'auth/logIn',
async (username: number, thunkAPI) => {
const response = await userAPI.fetchByUsername(username);
// 별도로 외부에 api 호출을 하는 함수를 만들자
return response.data;
}
);
파라미터는 2개이다.
1. 이름(string)
2. 비동기 payloadCreator 함수
이름은 통상적으로 "slice이름/함수이름"으로 선언한다.
그리고 비동기 함수는 thunkAPI는 기본으로 받되,
필요한 다른 파라미터를 같이 넣어주자.
userAPI는 src/api/userApi.ts 등에 별도로 비동기 호출을 하는 함수를 만들자.
이번 글은 thunk를 정리하기 위한 글이니 생략하지만,
Fake Store API를 사용해서 테스트 하면 쿠키 등이 없어도 바로 샘플 유저 데이터를 가져올 수 있는 등 편하다
이제 slice 안에 thunk reducer를 넣자.
기존처럼 reducers가 아니라 extraReducers에 추가해야 한다.
우선은 코드를 보면서 따라가자.
src/redux/slices/auth-slice.ts
const initialState = {
value: {
id: 0,
username: "",
email: "",
isAdmin: false,
isStaff: false,
} as IAuth,
status: "initial",
} as InitialState;
export const auth = createSlice({
name: "auth", // slice name
initialState, // initial state
reducers: { // 기존 비동기가 아닌 리듀서
logOut: () => {
return initialState; // 이전에 만든 로그아웃 기능
},
},
extraReducers: (builder) => { //비동기 리듀서들
builder.addCase(logIn.pending, (state) => {
state.status = "loading";
});
builder.addCase(logIn.fulfilled, (state, action) => {
state.value = action.payload;
state.status = "complete";
});
builder.addCase(logIn.rejected, (state) => {
state.status = "error";
});
},
});
${리듀서 이름}뒤에 비동기 리듀서의 3가지 상태에 따라 다른 행동을 정의해줄 수 있다.export default function LogIn() {
const [username, setUsername] = useState<string>("");
const [password, setPassword] = useState<string>("");
const dispatch = useDispatch<AppDispatch>();
const onClickLogIn = () => {
dispatch(logIn({ username: username, password: password }));
};
return (
<div className="items-center gap-2 ">
<input
className={style.inputStyle}
type="text"
onChange={(e) => setUsername(e.target.value)}
/>
<input
className={style.inputStyle}
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
<button className={style.buttonStyle} onClick={onClickLogIn}>
Log In
</button>
</div>
);
}

13 버전 글이 잘 없는데 좋은 글 잘 보고 갑니다 :)