EPguy
[React, React-Native] RTK Query를 사용하여 AccessToken, RefreshToken 관리하기 본문
[React, React-Native] RTK Query를 사용하여 AccessToken, RefreshToken 관리하기
EPguy 2023. 1. 3. 12:10RefreshToken은 AccessToken을 갱신하기 위해 사용되는 토큰입니다. AccessToken은 보통 유효시간이 짧기 때문에 만료될때 마다 AccessToken을 갱신시킨 후 다시 API를 호출하는 로직이 필요합니다. 그리고 RefreshToken도 만료되었을 경우 로그인 페이지로 다시 이동시켜야되는 로직도 필요합니다. 이 복잡한 로직들을 RTK Query를 사용하여 한곳에서 관리할 수 있는 방법을 알아보겠습니다.
1. fetchBaseQuery로 서버와 통신할때마다 헤더에 accessToken 넣어주기
서버와 통신할 때 마다 Store에서 accessToken을 가져와서 헤더에 넣어줄 수 있도록 baseQuery를 만들어줍니다.
const baseQuery = fetchBaseQuery({
baseUrl,
credentials: 'include',
prepareHeaders: (headers, { getState }) => {
const token = (getState() as any).auth.accessToken;
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return headers;
},
});
2. AccessToken, RefreshToken 만료 로직이 들어가있는 BaseQuery 만들기
위에서 만든 baseQuery는 헤더에 accessToken을 넣어주는 로직만 있었다면 아래에 AxiosBaseQuery는
AccessToken, RefreshToken이 만료됐을 경우에 대한 처리 로직이 들어가 있습니다.
참고로 여기서 사용하는 mutex 라이브러리는 api 호출이 끝나기 전에 한번 더 호출 되는 경우 (ex. 버튼을 한번에 여러번 눌렀을 때 같은 api가 여러번 호출되는 경우)를 방지하기 위해 사용하였습니다.
export const AxiosBaseQuery: BaseQueryFn<
string | FetchArgs,
unknown,
FetchBaseQueryError
> = async (args, api, extraOptions) => {
await mutex.waitForUnlock();
let result = await baseQuery(args, api, extraOptions);
if (result.error?.status === 401) {
if (!mutex.isLocked()) {
const release = await mutex.acquire();
try {
const refreshResult = await baseQuery(
{ url: 'auth/refresh' },
api,
extraOptions,
);
if (refreshResult.data) {
api.dispatch(
setAccessToken(
(refreshResult.data as RefershTokenResponse).accessToken,
),
);
result = await baseQuery(args, api, extraOptions);
} else {
api.dispatch(setRefreshTokenExpired(true));
}
} finally {
release();
}
} else {
await mutex.waitForUnlock();
result = await baseQuery(args, api, extraOptions);
}
} else if (result.error) {
Toast.show({
type: 'error',
text1: (result.error.data as any).message,
});
}
return result;
};
3. AxiosBaseQuery 사용하기
이렇게 만든 AxiosBaseQuery를 createApi를 사용할때 baseQuery 속성에 넣어서 사용하면 됩니다.
export const userApi = createApi({
reducerPath: 'userApi',
baseQuery: AxiosBaseQuery,
tagTypes: ['User'],
endpoints: (builder) => ({
getUserInfo: builder.query<User, void>({
query: () => {
return {
url: 'user',
method: 'GET',
};
},
providesTags: () => [{ type: 'User' }],
}),
updateNickname: builder.mutation<User, UpdateNicknameRequest>({
query: (data) => {
return {
url: 'user/nickname',
method: 'PUT',
body: data,
};
},
invalidatesTags: () => [{ type: 'User' }],
}),
}),
});
만약 예제 코드가 필요하시다면 아래 소스를 참고해주세요.
GitHub - EPguy/social-login-fullstack-boilerplate-RN-nestjs: 소셜로그인 + 일반로그인 FullStack Boilerplate (React-Nati
소셜로그인 + 일반로그인 FullStack Boilerplate (React-Native, Nestjs) - GitHub - EPguy/social-login-fullstack-boilerplate-RN-nestjs: 소셜로그인 + 일반로그인 FullStack Boilerplate (React-Native, Nestjs)
github.com