본문 바로가기
별밤 일지/개발

[Flutter] Retrofit 적용

by 별밤 에디터 2 2024. 4. 16.

별 헤는 밤 앱의 IOS 진출을 위해 Flutter 변환 작업을 하고 있습니다.

그 중 오늘은 앱에서 서버 데이터를 호출하는데 필요한 Retrofit에 대해 얘기 할 계획입니다.


RESTful API

Retrofit에 대해 설명하려면 RESTful API에 대해 먼저 설명할 필요가 있습니다.

REST(Representational State Transfer)는 클라이언트와 서버 간의 통신을 위한 소프트웨어 아키텍처 스타일을 의미합니다.

RESTful API은 이런 REST 아키텍처 스타일을 따르는 API로 주요 요소는 다음과 같습니다.

  • 자원: HTTP URI를 통해 웹 서비스의 글, 유저, 이미지 등을 가져옵니다.
  • 행위: HTTP Method를 사용하여 자원에 대한 행위를 나타냅니다. (ex) POST, GET, PUT, DELETE
  • 표현: 서버와 클라이언트 간에 자원을 주고 받을 때 사용되는 데이터 형식을 의미(주로 JSON, XML 등이 있음)

 

 

Retrofit

Retrofit은 안드로이드 앱이 RESTful API 통신을 할 때 사용하는 라이브러리를 의미합니다.

안드로이드에서 HTTP URI 형태로 메세지를 전송하면 서버에서 수신하여 JSON, XML 형태의 데이터로 답변을 주고 받을 수 있게 됩니다.

Flutter에서는 Retrofit 연결을 어떻게 하는지 지금부터 천천히 설명하도록 하겠습니다.

 

 

Dependencies 설정

Retrofit 관련 패키지들을 설정하기 위해 2가지를 추가해야 합니다.

dependencies:
	retrofit: ">=3.0.0 <4.0.0"
	build_runner: ^2.1.10
  json_annotation: ^4.8.1
	json_serializable: ^6.7.1
	
	dio: ^4.0.6
	
dev_dependencies:
	retrofit_generator: ">=4.0.0 <5.0.0"  // required dart >=2.19
  build_runner: '>=2.3.0 <4.0.0'
  json_serializable: ^6.6.2

pubspec.yaml

 

pub.dev 에서 retrofit 패키지 최신 버전을 설정하면 됩니다.

(확인해보니 24년 3월에는 “≥4.0.0 < 4.0.0”이 최신 버전이었습니다.)

 

$ flutter pub add retrofit

 

혹은 Android Studio로 개발 시 하단에 있는 Terminal 창에 해당 명령어를 입력하면 최신 버전으로 설치하게 됩니다.

Retrofit 패키지 이외에 4가지 패키지를 더 추가한 것을 확인할 수 있는데요.

각각의 패키지를 살펴보면 다음과 같습니다.

  1. dio: Flutter 전용 HTTP 통신 패키지
  2. build_runner: dart 파일 생성 패키지
  3. json_annotation: Dart 클래스에서 JSON 직렬화와 관련된 주석을 정의하기 위한 패키지
  4. json_serializable: json_annotation에서 제공한 주석을 기반으로 Dart 클래스의 직렬화 및 역 직렬화 코드를 생성하는 패키지

 

 

모델 코드 구현

패키지 설정을 다 했다면 2번째 단계로 모델 코드를 구현해야 합니다.

가장 기본적인 예시로 유저에 대한 서버 코드를 다음과 같이 준비되어 있다고 가정하겠습니다.

public class User{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;

    @Column
    private String name;
  
    @Column
    private String age;
}

서버의 User.java

 

 

그러면 Flutter 에서 User.dart 파일을 생성하고, 내부 코드는 다음과 같이 작성합니다.

import 'package:json_annotation/json_annotation.dart';

part 'User.g.dart'; // 자동 생성 시킬 g.dart 파일 이름 -- 현재 파일 동일한 이름+g.dart(!중요)

@JsonSerializable()
class User {
  final String? name;
  final String? age;

  User({required this.Name,
    required this.age }); //생성자

  factory User.fromJson(Map<String, dynamic> json) =>_$UserFromJson(json);

  Map<String, dynamic> toJson() => _$UserToJson(this);
}

 

 

직렬화를 위해서 Class User 상단의 @JsonSerializable() 어노테이션을 추가합니다

(part ‘User.g.dart’는 생성된 직렬화 코드의 파일 이름입니다.)

아마 처음 코드를 작성할 때에는 part 부분이 붉은 줄이 나오는데, 아직 생성이 안된 파일을 언급한 것에서 발생한 자연스러운 현상이므로 당황할 필요 없습니다.

그리고 하단에 생성자와 FromJson, ToJson 코드를 추가하고 Android Studio 하단의 Terminal에 ‘flutter pub run build_runner build’ 명령어를 실행시켜주면 됩니다.

 

정상적으로 작동한 했다면 상단의 Succeeded outputs 로그가 나오면서 하단의 User.g.dart 파일이 생성될 것입니다.

 

part of 'User.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

User _$UserFromJson(Map<String, dynamic> json) => User(
      name: json['name'] as String?,
      age: json['age'] as String?,
    );

Map<String, dynamic> _$UserToJson(User instance) => <String, dynamic>{
      'name': instance.name,
      'age': instance.age,
    };

User.g.dart

 

만약 정상적으로 g.dart 파일이 생성되지 않았다면 다음과 같은 문제들이 원인일 것입니다.

  • g.dart 파일 이름이 클래스 이름과 동일하지 않음.
  • 코드를 저장하지 않고 build 코드 실행

이래도 오류가 난다면 flutter clean 후에 build 명령어를 실행시키면 해결이 됩니다.

flutter clean
flutter pub run build_runner clean
flutter pub get && flutter pub run build_runner build

 

 

RestClient

안드로이드 앱에서는 RESTful API를 호출하는 클라이언트 이름을 RetrofitClient로 선택했는데, 이유는 retrofit2.gson 라이브러리를 사용했기 때문입니다.

하지만 Flutter에서는 RetrofitClient란 용어를 사용하지 않고 일반적으로 RestClient.dart로 명하는데 본인이 헷갈리지 않는 명칭을 선택하면 될 것 같습니다.

 

안드로이드와 Flutter의 구조 차이를 정리하면

프레임워크 안드로이드 Flutter
구조 RetrofitClient + Interface RestClient.dart + RestClient.g.dart

 

안드로이드에서는 Client와 인터페이스 파일을 별도로 제작해야 했는데, Flutter는 RestClient.dart 하나의 파일에 Client와 API 인터페이스가 들어갑니다.

 

import 'package:retrofit/retrofit.dart';
import 'package:dio/dio.dart';
import 'User.dart';

part 'UserRestClient.g.dart';

@RestApi(baseUrl:'서버 IP 주소')
abstract class UserRestClient{
  factory UserRestClient(Dio dio, {String? baseUrl}) =
  _UserRestClient; //Client 호출 코드

	//안드로이드 interface 코드와 동일
  @GET("user/{userId}")
  Future<User> getUser(@Path('userId')int userId);

  @DELETE('user/{userId}')
  Future<void> deleteUser(@Path('userId')int userId);

}

RestClient.dart

 

여기서 중요한 차이 점은 안드로이드에서는 Call<type> 형태로 서버에 쿼리문을 전송했지만, Flutter는 비동기 명령어인 Future<type> 형태로 쿼리문을 전송합니다.

그리고 Flutter는 충격적이게도 short, Long, Float 타입을 지원하지 않고, int와 Double 타입만 지원합니다. 따라서 Flutter에서 서버로 전송할 땐 Long으로 전달해도 Flutter에서 수신할 때 int로 받아야합니다.

동일하게 터미널에서 ‘flutter pub run build_runner build’ 명령어를 실행하면 UserRestClient.g.dart

생성될 것입니다.

RestClient.g.dart 코드와 Future ,async, await 비동기 방식을 설명하려면 후술해야할 것들이 많기 때문에 다음 글에서 설명하도록 하겠습니다.

 

 

정리

이번 글에서는 Retrofit에 대해서 알아보고, Flutter에 적용 방법과 구현 방식을 알아보았습니다.

다음 글에서 실질적으로 서버에서 데이터를 불러오는 예시를 통해 RestClient.g.dart와 UI에서 구현한 RESTful api 호출 방식을 설명하도록 하겠습니다.

 

참고

 

retrofit | Dart package

retrofit.dart is an dio client generator using source_gen and inspired by Chopper and Retrofit.

pub.dev

 

 

별 헤는 밤 놀러가기!

 

별 헤는 밤: 밤하늘, 별자리, 여행정보와 날씨예보까지 - Google Play 앱

오늘부터 별잘알! 오늘밤 별자리 정보, 날씨·광공해·월령까지 고려한 '관측적합도', 주변 관측지 검색으로 누구나 쉽게 밤하늘을 즐겨보세요.

play.google.com