Files
superport/lib/data/datasources/interceptors/api_interceptor.dart
JiWoong Sul 731dcd816b
Some checks failed
Flutter Test & Quality Check / Test on macos-latest (push) Has been cancelled
Flutter Test & Quality Check / Test on ubuntu-latest (push) Has been cancelled
Flutter Test & Quality Check / Build APK (push) Has been cancelled
refactor: Repository 패턴 적용 및 Clean Architecture 완성
## 주요 변경사항

### 🏗️ Architecture
- Repository 패턴 전면 도입 (인터페이스/구현체 분리)
- Domain Layer에 Repository 인터페이스 정의
- Data Layer에 Repository 구현체 배치
- UseCase 의존성을 Service에서 Repository로 전환

### 📦 Dependency Injection
- GetIt 기반 DI Container 재구성 (lib/injection_container.dart)
- Repository 인터페이스와 구현체 등록
- Service와 Repository 공존 (마이그레이션 기간)

### 🔄 Migration Status
완료:
- License 모듈 (6개 UseCase)
- Warehouse Location 모듈 (5개 UseCase)

진행중:
- Auth 모듈 (2/5 UseCase)
- Company 모듈 (1/6 UseCase)

대기:
- User 모듈 (7개 UseCase)
- Equipment 모듈 (4개 UseCase)

### 🎯 Controller 통합
- 중복 Controller 제거 (with_usecase 버전)
- 단일 Controller로 통합
- UseCase 패턴 직접 적용

### 🧹 코드 정리
- 임시 파일 제거 (test_*.md, task.md)
- Node.js 아티팩트 제거 (package.json)
- 불필요한 테스트 파일 정리

###  테스트 개선
- Real API 중심 테스트 구조
- Mock 제거, 실제 API 엔드포인트 사용
- 통합 테스트 프레임워크 강화

## 기술적 영향
- 의존성 역전 원칙 적용
- 레이어 간 결합도 감소
- 테스트 용이성 향상
- 확장성 및 유지보수성 개선

## 다음 단계
1. User/Equipment 모듈 Repository 마이그레이션
2. Service Layer 점진적 제거
3. 캐싱 전략 구현
4. 성능 최적화
2025-08-11 20:14:10 +09:00

100 lines
2.8 KiB
Dart

import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import '../../../core/storage/secure_storage.dart';
/// API 요청/응답 인터셉터
class ApiInterceptor extends Interceptor {
final SecureStorage _storage;
ApiInterceptor(this._storage);
@override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) async {
// 토큰 추가
final token = await _storage.getAccessToken();
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
// Content-Type 헤더 추가
if (options.data != null && options.data is! FormData) {
options.headers['Content-Type'] = 'application/json';
}
// 디버그 모드에서 요청 로깅
if (kDebugMode) {
print('🚀 API Request: ${options.method} ${options.path}');
if (options.queryParameters.isNotEmpty) {
print(' Query: ${options.queryParameters}');
}
if (options.data != null) {
print(' Body: ${options.data}');
}
}
handler.next(options);
}
@override
void onResponse(
Response response,
ResponseInterceptorHandler handler,
) {
// 디버그 모드에서 응답 로깅
if (kDebugMode) {
print('✅ API Response: ${response.statusCode} ${response.requestOptions.path}');
if (response.data != null) {
print(' Data: ${response.data}');
}
}
handler.next(response);
}
@override
void onError(
DioException err,
ErrorInterceptorHandler handler,
) async {
// 디버그 모드에서 에러 로깅
if (kDebugMode) {
print('❌ API Error: ${err.requestOptions.path}');
print(' Error Type: ${err.type}');
print(' Message: ${err.message}');
if (err.response != null) {
print(' Status: ${err.response?.statusCode}');
print(' Data: ${err.response?.data}');
}
}
// 401 Unauthorized 처리
if (err.response?.statusCode == 401) {
// 토큰 갱신 시도
final refreshToken = await _storage.getRefreshToken();
if (refreshToken != null) {
try {
// 토큰 갱신 로직 (필요시 구현)
// final newToken = await _refreshToken(refreshToken);
// await _storage.saveTokens(newToken);
// 원래 요청 재시도
// final options = err.requestOptions;
// options.headers['Authorization'] = 'Bearer $newToken';
// final response = await dio.fetch(options);
// return handler.resolve(response);
} catch (e) {
// 토큰 갱신 실패 시 로그아웃 처리
await _storage.clearAll();
}
} else {
// 리프레시 토큰이 없으면 로그아웃 처리
await _storage.clearAll();
}
}
handler.next(err);
}
}