const storage = FlutterSecureStorage();
String? accessToken = await storage.read(key: 'accessToken');
String? refreshToken = await storage.read(key: 'refreshToken');
// token 인터셉터 달기
final tokenInterceptor = TokenInterceptor(AuthApiService.dio, storage);
AuthApiService.dio.interceptors.add(tokenInterceptor);
// mainApp 실행 시 넘겨줄 bool 값
bool isAuthenticated = false;
if (accessToken != null) {
isAuthenticated = await AuthApiService.getProfile(accessToken: accessToken);
}
accessToken 이용해서 user Data 받아오기
(엑세스토큰 : 유효기간 12시간)
로직
유효 : 유저 데이터 받아옴
유효하지 않을 경우
1. 인터셉터 로직 실행
static Future<bool> getProfile({required String accessToken}) async {
try {
Response response = await dio.get(
'$baseUrl/auth/profile',
options: Options(headers: {
'Content-Type': 'application/json',
'authorization': 'Bearer $accessToken',
}),
);
return true;
} catch (e) {
print('getProfile failed: $e');
return false;
}
}
인터셉터 에러 로직
handler.resolve(response)handler.next(err)onError ChatGPT 설명
- 함수가 실행되면, 먼저 DioException 객체와 ErrorInterceptorHandler를 인자로 받습니다.
- DioException은 Dio에서 발생한 예외를 나타내며, ErrorInterceptorHandler는 예외 처리를 담당하는 핸들러입니다.
함수 내부에서는 먼저 받은 예외 객체에서 HTTP 응답 코드가 401(Unauthorized)인지 확인합니다. 이 때, 해당 응답의 데이터에 '토큰이 만료되었습니다.' 메시지가 포함되어 있는지도 함께 확인합니다.
- 만약 응답 코드가 401이고, 메시지가 '토큰이 만료되었습니다.'인 경우에는 refreshToken() 메서드를 호출하여 새로운 액세스 토큰을 갱신합니다.
4.새로운 액세스 토큰이 정상적으로 갱신되었다면, 해당 토큰을 이용하여 요청을 다시 보내기 위해 예외가 발생한 요청의 헤더에 새로운 액세스 토큰을 추가합니다.
- _retry() 메서드를 사용하여 새로운 헤더를 적용한 새로운 요청을 보냅니다. 이 때, 해당 요청이 성공하면 handler.resolve(response)를 호출하여 요청을 성공적으로 처리한 후에 종료합니다.
- 그러나 새로운 요청이 실패하면(DioException이 발생하면), 해당 예외를 다음 단계로 전달하기 위해 handler.next(e)를 호출합니다.
- 모든 예외 처리가 끝나면, super.onError(err, handler)를 호출하여 예외를 다음 단계로 전달합니다. 만약 이 함수에서 예외를 처리하지 않았거나 처리할 수 없는 경우에는 이 부분이 실행됩니다.
Future onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401) {
if (err.response?.data['message'] == '토큰이 만료되었습니다.') {
final newAccessToken = await refreshToken();
// 새로운 액세스 토큰이 있으면 요청 재시도
if (newAccessToken != '') {
err.requestOptions.headers['authorization'] =
'Bearer $newAccessToken';
try {
final response = await _retry(err.requestOptions);
return handler.resolve(response);
} on DioException catch (e) {
handler.next(e);
}
}
}
}
super.onError(err, handler);
}
요청이 실패한 경우에 요청을 다시 시도하는 데 사용되는 함수
// retry
Future<Response<dynamic>> _retry(RequestOptions requestOptions) async {
final options = Options(
method: requestOptions.method,
headers: requestOptions.headers,
);
// Retry the request with the new `RequestOptions` object.
return dio.request<dynamic>(requestOptions.path,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters,
options: options);
}
}
Future<String> refreshToken() async {
const storage = FlutterSecureStorage();
final refreshToken = await storage.read(key: 'refreshToken');
try {
Response response = await dio.post(
'$baseUrl/auth/refresh',
data: {'refreshToken': refreshToken},
options: Options(headers: {'Content-Type': 'application/json'}),
);
if (response.statusCode == 201) {
final newAccessToken = response.data['accessToken'];
await storage.write(key: 'accessToken', value: newAccessToken);
return newAccessToken;
}
} catch (e) {
storage.delete(key: 'accessToken');
storage.delete(key: 'refreshToken');
print('refresh token failed: $e');
}
return '';
}
플러터 인터셉터 달면서 작성한 코드 내용 토대로 정리해놓기