(+) Data Layer - Network

κ°•μ˜μ—μ„œλŠ” retrofit을 μ‚¬μš©ν•˜κ³  μžˆλŠ”λ°, dio κ°€ 더 인기 있고 보편적인 λΌμ΄λΈŒλŸ¬λ¦¬μ΄λ‹€. λ”°λΌμ„œ 이 라이브러리λ₯Ό μ‚¬μš©ν•˜κΈ°λ‘œ ν–ˆκ³  이 라이브러리 κΈ°μ€€μœΌλ‘œ μ„€μ •ν•œ 것을 μ •λ¦¬ν•œλ‹€.

νŒ¨ν‚€μ§€ ꡬ성 κ΄€μ μ—μ„œ 바라본 Network

ν•˜λ‚˜μ˜ μ•±μ—μ„œ 각기 λ‹€λ₯Έ μ„œλ²„μ™€ 톡신할 일이 ν”ν•˜κ³ , 이 λ•Œ 각 μ„œλ²„μ™€μ˜ 톡신을 μ±…μž„μ§€λŠ” ν΄λΌμ΄μ–ΈνŠΈλ„ ꡬ뢄을 ν•΄λ‘λŠ” 것이 μ½”λ“œ ν™•μž₯μ„±μ΄λ‚˜ μœ μ§€λ³΄μˆ˜ μΈ‘λ©΄μ—μ„œ μœ λ¦¬ν•˜λ‹€.

κ·Έλ ‡μ§€λ§Œ ν•œνŽΈμœΌλ‘œλŠ” 각기 λ‹€λ₯Έ μ„œλ²„μ™€ ν†΅μ‹ ν•œλ‹€κ³  해도 κ³΅ν†΅μ μœΌλ‘œ κ°€μ Έμ•Όν•  μ—­ν•  및 ν˜•νƒœκ°€ μžˆμ„ 수 밖에 μ—†λ‹€. κ·Έλž˜μ„œ μ΄λŸ¬ν•œ 뢀뢄을 κ³ λ €ν•΄μ„œ base_http_client 을 λΆ„λ¦¬ν•΄λ‘μ—ˆκ³  각 ν΄λΌμ΄μ–ΈνŠΈκ°€ 이λ₯Ό μƒμ†ν•΄μ„œ κ΅¬ν˜„ν•˜λ„λ‘ μ²˜λ¦¬ν–ˆλ‹€.

interceptor 도 μš©λ„λ³„λ‘œ μ΅œλŒ€ν•œ λΆ„λ¦¬ν•΄μ„œ λ”°λ‘œ κ΅¬ν˜„μ„ ν•˜μ—¬ ν•„μš”ν•œ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ•Œμ•„μ„œ κ°€μ Έκ°€μ„œ μ‚¬μš©ν•˜λ„λ‘ μ²˜λ¦¬ν–ˆλ‹€.

Network κ΅¬ν˜„

import 'package:dio/dio.dart';

abstract class BaseHttpClient with DioMixin implements Dio {
  final String clientBaseUrl;
  final List<Interceptor> customInterceptors;
  BaseOptions? customOptions;

  BaseHttpClient({
    required this.clientBaseUrl,
    required this.customInterceptors,
    this.customOptions,
  }) {
    if (customOptions != null) {
      options = customOptions!;
    }
    options.baseUrl = clientBaseUrl;
    // options.connectTimeout = Duration(seconds: 5);
    // options.receiveTimeout = Duration(seconds: 3);
    interceptors.add(LogInterceptor(requestBody: true, responseBody: true));
    interceptors.addAll(customInterceptors);
  }
}
import 'package:app_frame_sample/data/network/client/base_http_client.dart';

class TodoApiClient extends BaseHttpClient {
  TodoApiClient({
    required super.clientBaseUrl,
    required super.customInterceptors,
    super.customOptions,
  });
}
import 'dart:io';

import 'package:app_frame_sample/application/application.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:package_info_plus/package_info_plus.dart';

class BaseHeaderInterceptor extends InterceptorsWrapper {
  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    await _setHeaderWithAccessToken(options.headers);
    await _setHeaderWithPackageInfo(options.headers);
    await _setHeaderWithDeviceInfo(options.headers);

    super.onRequest(options, handler);
  }

  Future<void> _setHeaderWithAccessToken(Map<String, dynamic> headers) async {
    headers[HttpHeaders.authorizationHeader] =
        await AccessManager.getAccessToken();
  }

  Future<void> _setHeaderWithPackageInfo(Map<String, dynamic> headers) async {
    final PackageInfo packageInfo = await PackageInfo.fromPlatform();
    headers['appName'] = packageInfo.appName;
    headers['packageName'] = packageInfo.packageName;
    headers['version'] = packageInfo.version;
    headers['buildNumber'] = packageInfo.buildNumber;
  }

  Future<void> _setHeaderWithDeviceInfo(Map<String, dynamic> headers) async {
    final DeviceInfoPlugin deviceInfo = Injector.deviceInfoPlugin;
    if (Platform.isAndroid) {
      final AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
      headers['uuid'] = androidInfo.id;
      headers['operatingSystem'] = 'Android';
      headers['operatingSystemVersion'] = androidInfo.version.sdkInt.toString();
      return;
    }

    if (Platform.isIOS) {
      final IosDeviceInfo iOSInfo = await deviceInfo.iosInfo;
      headers['uuid'] = iOSInfo.identifierForVendor;
      headers['operatingSystem'] = 'iOS';
      headers['operatingSystemVersion'] = iOSInfo.systemVersion;
    }
  }
}

μ—¬κΈ°μ„œ device 정보와 package 정보λ₯Ό μ½λŠ”λ° μ‚¬μš©λœ νŒ¨ν‚€μ§€λ“€μ€ μ•„λž˜μ™€ κ°™λ‹€.

μœ„ μ½”λ“œμƒμ— κ΅¬ν˜„λœ AccessManager λŠ” data λ ˆμ΄μ–΄λ³΄λ‹€λŠ” application λ ˆμ΄μ–΄μ— μ†ν•˜λŠ”κ²Œ λ§žλŠ” 것 κ°™μ•„μ„œ 일단은 application layer 에 κ΅¬ν˜„ν•΄λ‘μ—ˆκ³  μ½”λ“œλŠ” μ•„λž˜μ™€ κ°™λ‹€. shared_preferences νŒ¨ν‚€μ§€λ₯Ό μ‚¬μš©ν–ˆλ‹€.

import 'package:shared_preferences/shared_preferences.dart';

class AccessManager {
  static const String accessTokenKey = 'accessToken';

  static Future<void> saveAccessToken(String accessToken) async {
    final SharedPreferences preferences = await SharedPreferences.getInstance();
    await preferences.setString(accessTokenKey, accessToken);
  }

  static Future<String> getAccessToken() async {
    final SharedPreferences preferences = await SharedPreferences.getInstance();
    final String? accessToken = preferences.getString(accessTokenKey);
    if (accessToken == null) {
      return '';
    }
    
    return accessToken;
  }
}

Last updated