LCOV - code coverage report
Current view: top level - lib/services - api_service.dart Coverage Total Hit
Test: lcov.info Lines: 13.0 % 46 6
Test Date: 2025-08-12 03:21:51 Functions: - 0 0

            Line data    Source code
       1              : import 'package:dio/dio.dart';
       2              : import 'package:cookie_jar/cookie_jar.dart';
       3              : import 'package:dio_cookie_manager/dio_cookie_manager.dart';
       4              : import 'package:flutter/material.dart';
       5              : import 'package:flutter/foundation.dart';
       6              : import '../config/api_config.dart';
       7              : import '../services/auth_service.dart';
       8              : import '../screens/auth/login_screen.dart';
       9              : import '../main.dart'; // navigatorKey를 사용하기 위한 import
      10              : 
      11              : class ApiService {
      12            9 :   static final ApiService _instance = ApiService._internal();
      13            6 :   factory ApiService() => _instance;
      14              :   
      15              :   late Dio _dio;
      16              :   final CookieJar _cookieJar = CookieJar();
      17              :   String? _token;
      18              :   
      19            3 :   ApiService._internal() {
      20            9 :     _dio = Dio(BaseOptions(
      21              :       baseUrl: ApiConfig.baseUrl,
      22              :       connectTimeout: const Duration(seconds: 10),
      23              :       receiveTimeout: const Duration(seconds: 10),
      24              :       contentType: 'application/json',
      25              :     ));
      26              :     
      27              :     // 쿠키 관리자 설정 (웹 환경에서는 사용하지 않음)
      28              :     if (!kIsWeb) {
      29           15 :       _dio.interceptors.add(CookieManager(_cookieJar));
      30              :     }
      31              :     
      32              :     // 로그 인터셉터 (디버깅용)
      33           12 :     _dio.interceptors.add(LogInterceptor(
      34              :       requestBody: true,
      35              :       responseBody: true,
      36              :     ));
      37              :   }
      38              :   
      39              :   // 토큰 설정
      40            0 :   void setToken(String token) {
      41            0 :     _token = token;
      42            0 :     _dio.options.headers['Authorization'] = 'Bearer $token';
      43              :   }
      44              :   
      45              :   // GET 요청
      46            0 :   Future<dynamic> get(String path, {Map<String, dynamic>? queryParameters}) async {
      47              :     try {
      48            0 :       final response = await _dio.get(
      49              :         path,
      50              :         queryParameters: queryParameters,
      51              :       );
      52            0 :       return response.data;
      53            0 :     } on DioException catch (e) {
      54            0 :       _handleError(e);
      55              :       rethrow;
      56              :     }
      57              :   }
      58              :   
      59              :   // POST 요청
      60            0 :   Future<dynamic> post(String path, {dynamic data}) async {
      61              :     try {
      62            0 :       final response = await _dio.post(
      63              :         path,
      64            0 :         data: data ?? {},
      65              :       );
      66            0 :       return response.data;
      67            0 :     } on DioException catch (e) {
      68            0 :       _handleError(e);
      69              :       rethrow;
      70              :     }
      71              :   }
      72              :   
      73              :   // PUT 요청
      74            0 :   Future<dynamic> put(String path, {dynamic data}) async {
      75              :     try {
      76            0 :       final response = await _dio.put(
      77              :         path,
      78              :         data: data,
      79              :       );
      80            0 :       return response.data;
      81            0 :     } on DioException catch (e) {
      82            0 :       _handleError(e);
      83              :       rethrow;
      84              :     }
      85              :   }
      86              :   
      87              :   // DELETE 요청
      88            0 :   Future<dynamic> delete(String path) async {
      89              :     try {
      90            0 :       final response = await _dio.delete(path);
      91            0 :       return response.data;
      92            0 :     } on DioException catch (e) {
      93            0 :       _handleError(e);
      94              :       rethrow;
      95              :     }
      96              :   }
      97              :   
      98              :   // 에러 처리
      99            0 :   void _handleError(DioException e) {
     100            0 :     if (e.response != null) {
     101            0 :       print('API 에러: ${e.response?.statusCode} - ${e.response?.data}');
     102              :       
     103              :       // 401 오류 (인증 실패) 처리
     104            0 :       if (e.response?.statusCode == 401) {
     105            0 :         print('인증 오류: 토큰이 유효하지 않습니다. 로그아웃 처리합니다.');
     106            0 :         _handleUnauthorized();
     107              :       }
     108              :     } else {
     109            0 :       print('API 요청 에러: ${e.message}');
     110              :     }
     111              :   }
     112              :   
     113              :   // 인증 오류 처리 (401)
     114            0 :   void _handleUnauthorized() {
     115              :     // 다음 프레임에서 실행하여 현재 API 호출 스택이 완료되도록 함
     116            0 :     Future.microtask(() async {
     117              :       try {
     118              :         // AuthService 인스턴스 가져오기
     119            0 :         final authService = AuthService();
     120              :         
     121              :         // 로그아웃 처리
     122            0 :         await authService.logout();
     123              :         
     124              :         // 전역 네비게이터 키를 사용하여 로그인 화면으로 이동
     125            0 :         if (navigatorKey.currentContext != null) {
     126            0 :           Navigator.of(navigatorKey.currentContext!).pushAndRemoveUntil(
     127            0 :             MaterialPageRoute(builder: (context) => const LoginScreen()),
     128            0 :             (route) => false,
     129              :           );
     130              :         }
     131              :       } catch (e) {
     132            0 :         print('로그아웃 처리 중 오류 발생: $e');
     133              :       }
     134              :     });
     135              :   }
     136              : }
        

Generated by: LCOV version 2.3.1-1