-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapiInstance.ts
More file actions
140 lines (120 loc) · 3.63 KB
/
apiInstance.ts
File metadata and controls
140 lines (120 loc) · 3.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import { NavigateFunction } from 'react-router-dom';
import { API_BASE_URL } from '@/api/config';
import { ServerIntendedError } from '@/api/types';
import { PATH } from '@/routes/path';
import axios, {
AxiosError,
AxiosInstance,
AxiosRequestConfig,
AxiosResponse,
} from 'axios';
interface FailedRequest {
resolve: (
value: Promise<AxiosResponse<unknown>> | PromiseLike<AxiosResponse<unknown>>
) => void;
reject: (error: AxiosError<unknown, unknown>) => void;
config: AxiosRequestConfig;
}
let navigator: NavigateFunction | null = null;
let isReissuing = false;
let failedQueue: FailedRequest[] = [];
const processQueue = (error: AxiosError | null): void => {
failedQueue.forEach((prom) => {
if (error) {
prom.reject(error);
} else {
prom.resolve(apiInstance(prom.config));
}
});
failedQueue = [];
};
export const setNavigator = (nav: NavigateFunction) => {
navigator = nav;
};
export const navigate = (path: string) => {
if (navigator) {
navigator(path);
}
};
const apiInstance: AxiosInstance = axios.create({
baseURL: `${API_BASE_URL}/api/`,
withCredentials: true,
headers: {
'Content-Type': 'application/json',
},
timeout: 5000,
});
apiInstance.interceptors.request.use(
(config) => {
// 헤더가 남아있을 경우 삭제
if (config.headers?.Authorization) {
delete config.headers.Authorization;
}
return config;
},
(error) => Promise.reject(error)
);
apiInstance.interceptors.response.use(
(response) => response,
async (error: AxiosError<ServerIntendedError>) => {
/**
* 응답이 401이고, 401의 원인이 access token이 만료된 경우,
* refresh token을 통해 access token을 재발급 받은후,
* 재발급한 access token을 통해 재요청한다.
* access token을 재발급 받을 때, "이전 로그인 정보를 통해 재입장중입니다."라는 느낌의 토스트 메세지를 띄웠으면 좋겠음
* 대기시간이 길어지는 이유를 설명하고 싶음.
*/
const originalRequest = error.config;
if (!originalRequest) {
return Promise.reject(error);
}
if (error.response) {
const data = error.response.data;
const { code, message } = data;
// 팀 방장 일반회원 강등
if (message === '팀 매니저가 옳지 않습니다') {
navigate(PATH.TEAMS);
return Promise.reject(error);
}
if (error.response.status === 401) {
if (code === 100) {
navigate(PATH.ENROLL);
return Promise.reject(error);
}
if (code === 101) {
if (isReissuing) {
return new Promise<AxiosResponse>((resolve, reject) => {
failedQueue.push({
resolve,
reject,
config: originalRequest,
});
});
}
isReissuing = true;
return apiInstance
.post('v1/reissue')
.then(() => {
processQueue(null);
return apiInstance(originalRequest);
})
.catch((reissueError: AxiosError) => {
processQueue(reissueError);
navigate(PATH.LOGIN);
console.error('reissue error', reissueError);
return Promise.reject(reissueError);
})
.finally(() => {
isReissuing = false;
});
}
// 리프레시 토큰이 만료됨
// sessionStorage.removeItem('Authorization');
navigate(PATH.LOGIN);
return Promise.reject(error);
}
}
return Promise.reject(error);
}
);
export default apiInstance;