flutter Retrofit

Pyo·2023년 9월 30일
0

Retrofit

다양한 HTTP 메소드(GET, PUT, POST 등)와 관련된 어노테이션을 활용할 수 있으며, 요청 헤더와 같은 HTTP 기능을 다루는 어노테이션도 존재합니다. 무엇보다 Retrofit은 API 엔드포인트에 필요한 파라미터를 관리하는 데 큰 도움을 주고,이로 인해 요청 파라미터를 보다 간편하게 처리하여, 코드의 가독성과 유지 보수성을 향상시키는 데 도움이 된다.

pubspec.yaml 설정

dependencies:
  flutter:
    sdk: flutter
  dio:
  json_annotation:
  retrofit:



dev_dependencies:
  flutter_test:
    sdk: flutter
  retrofit_generator:
  build_runner:
  json_serializable:
	

Retrofit을 사용하기 위해서는 추가할 것이 dio,retrofit,retrofit_generator,build_runner가 필요하고 추가로 fromJson , toJson을 위하여 json_serialize,json_annotaion이 필요하다.

코드 비교

retrofit을 사용하지 않았을때와 사용했을때의 코드를 통해 비교해 보려고 한다.
api는 개발자들이 웹 및 모바일 애플리케이션을 테스트하고 개발할 때 사용할 수 있는 무료 온라인 RESTful API 서비스인 https://jsonplaceholder.typicode.com/라는곳에서 아래 데이터를 불러올것이다.

[
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  },
  {
    "userId": 1,
    "id": 2,
    "title": "qui est esse",
    "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla"
  },
  {
    "userId": 1,
    "id": 3,
    "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut",
    "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut"
  },
  {
    "userId": 1,
    "id": 4,
    "title": "eum et est occaecati",
    "body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit"
  },
  {
    "userId": 1,
    "id": 5,
    "title": "nesciunt quas odio",
    "body": "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque"
  },
  {
    "userId": 1,
    "id": 6,
    "title": "dolorem eum magni eos aperiam quia",
    "body": "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae"
  },
  {
    "userId": 1,
    "id": 7,
    "title": "magnam facilis autem",
    "body": "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas"
  },
  {
    "userId": 1,
    "id": 8,
    "title": "dolorem dolore est ipsam",
    "body": "dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae"
  },
  {
    "userId": 1,
    "id": 9,
    "title": "nesciunt iure omnis dolorem tempora et accusantium",
    "body": "consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas"
  },
  {
    "userId": 1,
    "id": 10,
    "title": "optio molestias id quia eum",
    "body": "quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error"
  }
]

우선 두방법에서 사용할 JsonSerializable을 이용한 데이터 모델이다.

retrofit_sample_model.dart

import 'package:freezed_annotation/freezed_annotation.dart';

part 'retrofit_sample_model.g.dart';

@JsonSerializable()
class RetrofitSampleModel {
  final int userId;
  final int id;
  final String title;
  final String body;

  RetrofitSampleModel({
    required this.userId,
    required this.id,
    required this.title,
    required this.body,
  });

  factory RetrofitSampleModel.fromJson(Map<String, dynamic> json) =>
      _$RetrofitSampleModelFromJson(json);
}

우선 retrofit을 사용하지 않고 dio만을 사용하여 api로 데이터를 받아온 코드이다.

Dio 사용

default_screen.dart

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_practice/src/default_layout_widget.dart';
import 'package:flutter_practice/src/retrofit/retrofit_sample_model.dart';

class DefaultScreen extends StatelessWidget {
  const DefaultScreen({super.key});

  Future<List> getData() async {
    final dio = Dio();

    final resp = await dio.get(
      'https://jsonplaceholder.typicode.com/posts',
      queryParameters: {
        '_page': 1,
        '_limit': 10,
      },
    );

    return resp.data;
  }

  @override
  Widget build(BuildContext context) {
    return DefaultLayoutWidget(
      child: FutureBuilder<List>(
          future: getData(),
          builder: (context, AsyncSnapshot<List> snapshot) {
            if (!snapshot.hasData) {
              return const Center(
                child: CircularProgressIndicator(),
              );
            }
            return ListView.separated(
              itemCount: snapshot.data!.length,
              itemBuilder: (BuildContext context, int index) {
                final item = snapshot.data![index];
                final pItem = RetrofitSampleModel.fromJson(item);
                return Row(
                  // mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text('${pItem.id}'),
                    const SizedBox(
                      width: 10,
                    ),
                    Expanded(child: Text(pItem.title)),
                  ],
                );
              },
              separatorBuilder: (BuildContext context, int index) {
                return const SizedBox(
                  height: 20,
                );
              },
            );
          }),
    );
  }
}

이번에는 retrofit을 사용한 코드이다. 우선 retrofit의 기능들을 사용하기 위한 추상 클래스가 필요하다.
이렇게 추상 클래스를 사용하면 메서드를 호출함으로써 각각의 API 요청을 설정할 수 있고, 더 나아가 Retrofit은 응답 데이터를 자동으로 모델 객체로 변환해주기 때문에 개발자들에게 상당한 편의를 제공한다.

Retrofit 사용

common_repository.dart

import 'package:dio/dio.dart';
import 'package:flutter_practice/src/retrofit/retrofit_sample_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:retrofit/http.dart';

part 'common_repository.g.dart';

@RestApi(baseUrl: 'https://jsonplaceholder.typicode.com')
abstract class CommonRepository {
  factory CommonRepository(Dio dio, {String baseUrl}) = _CommonRepository;

  @GET("/posts")
  Future<List<RetrofitSampleModel>> getData(
    @Query("_page") int page,
    @Query("_limit") int limit,
  );
}

retrofit_screen.dart

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_practice/src/default_layout_widget.dart';
import 'package:flutter_practice/src/retrofit/common_repository.dart';
import 'package:flutter_practice/src/retrofit/retrofit_sample_model.dart';

class RetrofitScreen extends StatelessWidget {
  const RetrofitScreen({super.key});

  Future<List<RetrofitSampleModel>> getRepositoryData() async {
    final dio = Dio();

    final repository = CommonRepository(dio);

    return repository.getData(1, 10);
  }

  @override
  Widget build(BuildContext context) {
    return DefaultLayoutWidget(
      child: FutureBuilder<List<RetrofitSampleModel>>(
        future: getRepositoryData(),
        builder: (BuildContext context,
            AsyncSnapshot<List<RetrofitSampleModel>> snapshot) {
          if (!snapshot.hasData) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          return ListView.separated(
              itemBuilder: (context, index) {
                final pItem = snapshot.data![index];

                return Row(
                  children: [
                    Text('${pItem.id}'),
                    const SizedBox(
                      width: 10,
                    ),
                    Expanded(child: Text(pItem.title)),
                  ],
                );
              },
              separatorBuilder: (BuildContext context, int index) {
                return const SizedBox(
                  height: 20,
                );
              },
              itemCount: snapshot.data!.length);
        },
      ),
    );
  }
}

실행화면

retrofit에 대해 공부 해보니 낯선부분도 많았지만,http,dio를 이용하여 api통신을 할때 보다 api통신적인 부분에서도 편하고 , 자동으로 모델객체로 변경해주는 부분에서도 상당히 편리함을 느꼈다. 앞으로도 자주 사용할것 같다.

참고

https://velog.io/@leeeeeoy/flutter-Retrofit-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-1

0개의 댓글