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