feat(app): add manual entry and sharing flows

This commit is contained in:
JiWoong Sul
2025-11-19 16:36:39 +09:00
parent 5ade584370
commit 947fe59486
110 changed files with 5937 additions and 3781 deletions

View File

@@ -2,7 +2,7 @@ import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
/// 로깅 인터셉터
///
///
/// 네트워크 요청과 응답을 로그로 기록합니다.
/// 디버그 모드에서만 활성화됩니다.
class LoggingInterceptor extends Interceptor {
@@ -12,35 +12,35 @@ class LoggingInterceptor extends Interceptor {
final uri = options.uri;
final method = options.method;
final headers = options.headers;
print('═══════════════════════════════════════════════════════════════');
print('>>> REQUEST [$method] $uri');
print('>>> Headers: $headers');
if (options.data != null) {
print('>>> Body: ${options.data}');
}
if (options.queryParameters.isNotEmpty) {
print('>>> Query Parameters: ${options.queryParameters}');
}
}
return handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
if (kDebugMode) {
final statusCode = response.statusCode;
final uri = response.requestOptions.uri;
print('<<< RESPONSE [$statusCode] $uri');
if (response.headers.map.isNotEmpty) {
print('<<< Headers: ${response.headers.map}');
}
// 응답 본문은 너무 길 수 있으므로 처음 500자만 출력
final responseData = response.data.toString();
if (responseData.length > 500) {
@@ -48,32 +48,32 @@ class LoggingInterceptor extends Interceptor {
} else {
print('<<< Body: $responseData');
}
print('═══════════════════════════════════════════════════════════════');
}
return handler.next(response);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (kDebugMode) {
final uri = err.requestOptions.uri;
final message = err.message;
print('═══════════════════════════════════════════════════════════════');
print('!!! ERROR $uri');
print('!!! Message: $message');
if (err.response != null) {
print('!!! Status Code: ${err.response!.statusCode}');
print('!!! Response: ${err.response!.data}');
}
print('!!! Error Type: ${err.type}');
print('═══════════════════════════════════════════════════════════════');
}
return handler.next(err);
}
}
}

View File

@@ -5,36 +5,38 @@ import '../network_config.dart';
import '../../errors/network_exceptions.dart';
/// 재시도 인터셉터
///
///
/// 네트워크 오류 발생 시 자동으로 재시도합니다.
/// 지수 백오프(exponential backoff) 알고리즘을 사용합니다.
class RetryInterceptor extends Interceptor {
final Dio dio;
RetryInterceptor({required this.dio});
@override
void onError(DioException err, ErrorInterceptorHandler handler) async {
// 재시도 카운트 확인
final retryCount = err.requestOptions.extra['retryCount'] ?? 0;
// 재시도 가능한 오류인지 확인
if (_shouldRetry(err) && retryCount < NetworkConfig.maxRetries) {
try {
// 지수 백오프 계산
final delay = _calculateBackoffDelay(retryCount);
print('RetryInterceptor: 재시도 ${retryCount + 1}/${NetworkConfig.maxRetries} - ${delay}ms 대기');
print(
'RetryInterceptor: 재시도 ${retryCount + 1}/${NetworkConfig.maxRetries} - ${delay}ms 대기',
);
// 대기
await Future.delayed(Duration(milliseconds: delay));
// 재시도 카운트 증가
err.requestOptions.extra['retryCount'] = retryCount + 1;
// 재시도 실행
final response = await dio.fetch(err.requestOptions);
return handler.resolve(response);
} catch (e) {
// 재시도도 실패한 경우
@@ -48,10 +50,10 @@ class RetryInterceptor extends Interceptor {
}
}
}
return handler.next(err);
}
/// 재시도 가능한 오류인지 판단
bool _shouldRetry(DioException err) {
// 네이버 관련 요청은 재시도하지 않음
@@ -60,7 +62,7 @@ class RetryInterceptor extends Interceptor {
print('RetryInterceptor: 네이버 API 요청은 재시도하지 않음 - $url');
return false;
}
// 네트워크 연결 오류
if (err.type == DioExceptionType.connectionTimeout ||
err.type == DioExceptionType.sendTimeout ||
@@ -68,30 +70,30 @@ class RetryInterceptor extends Interceptor {
err.type == DioExceptionType.connectionError) {
return true;
}
// 서버 오류 (5xx)
final statusCode = err.response?.statusCode;
if (statusCode != null && statusCode >= 500 && statusCode < 600) {
return true;
}
// 429 Too Many Requests는 재시도하지 않음
// 재시도하면 더 많은 요청이 발생하여 문제가 악화됨
return false;
}
/// 지수 백오프 지연 시간 계산
int _calculateBackoffDelay(int retryCount) {
final baseDelay = NetworkConfig.retryDelayMillis;
final multiplier = NetworkConfig.retryDelayMultiplier;
// 지수 백오프: delay = baseDelay * (multiplier ^ retryCount)
final exponentialDelay = baseDelay * pow(multiplier, retryCount);
// 지터(jitter) 추가로 동시 재시도 방지
final jitter = Random().nextInt(1000);
return exponentialDelay.toInt() + jitter;
}
}
}