이전 글: 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 버전 글이 잘 없는데 좋은 글 잘 보고 갑니다 :)