본문 바로가기
SW 개발/FrontEnd(Flutter)

Flutter 이미지 처리를 위한 Image, FadeInImage, CachedNetworkImage, ExtendedImage 사용법 및 성능 비교

by Kibua20 2022. 1. 1.

지난 포스팅에서는 Material UI와 Cupertino Icon을 사용하는 방법을 정리하였습니다. 이번 포스팅에서는 GUI 처리에서 필수적인 Image 처리 방법에 대해서 설명하도록 하겠습니다.  Image 처리 API는 1) App 리소스에 이미지 포함 여부, 2) Placeholder 지원 여부, 3) Cache 기능 지원 여부, 4) 이미지 Display 속도를 기준으로 대락 5가지 정도의 API를 사용할 수 있습니다. 

 

모든 이미지를 App에 assets 형태로 리소스에 포함시킨다면 Image로딩 속도는 빠르나,  App의 크기 자체도 커지고 Image 변경 시 App 자체를 변경 후 다시 배포해야 합니다.

 

Internet 이미지는 App Asset 이미지와 반대의 경우로 리소스 서버 또는 인터넷에서 이미지를 다운로드하는 경우입니다. Internet 이미지는  이미지 로딩 속도는 느리지만, App 크기를 줄일 수 있고  이미지 서버(e.g, AWS S3, Google Cloud Storage)에 있는 리소스만 변경함으로써 App을 변경하지 않고 GUI 업데이트가 가능합니다.

 

Cached Network Image는 Mobile App (Andorid, I-OS)에서 인터넷에 다운로드한 이미지를 일정 기간 동안 단말 storage 저장하는 것으로 Internet 이미지의 장점을 살리면서, 단점을 cache로 보완한 것입니다.

 

  이미지 위치 Placeholder  Cache 지원 속도 비고
Image.asset() App리소스  X X 빠름 추가 설치 없음
Image.network() Internet (URL) X X 느림 추가 설치 없음
FadeInImage.memoryNetwork() Internet (URL) O X 느림  
CachedNetworkImage() Internet (URL) O O 첫 다운로드 느리고,
Cache 저장이후 빠름 
Download state 관리 가능
ExtendedImage.network() Internet (URL) O O 첫 다운로드 느리고,
Cache 저장이후 빠름 
Zoom, Panning 등 

 

5개의 API를 테스트해본 결과 Mobile (Android, I-OS ) App에서 이미지 loading 속도는 Image.asset()과 Cache를 사용하는 CachedNetworkImage()와 ExtendedImage.network() 가 빠릅니다. 본 포스팅 마지막에 비교 동영상을 올렸으니 확인해주세요.

 

Web App인 경우 Web server에서 어차피 이미지를 다운로드하고, Broswer에서 cache 기능을 지원하기 때문에 Local, Network, CachedNetwork의 기능 차이가 의미 없습니다. 

 

개발하는 App의 요구 사항에 따라서 적절한 API를 사용하면 될 것 같습니다. App 자체 이미지를 포함시키는 경우에는 Image.assert()을 사용하고, Internet Image를 사용해야 한다면 Cache기능이 있는 CachedNetworkImage() 또는  ExtendedImage.network()를 사용하는 것을 추천됩니다.  만일 다양한 이미지 처리 기능을 요구하는 경우에는 Cached Netowrk Image() 보다는 ExtendedImage() 사용을 추천드립니다.  

 

 

Image.asset(): App 리소스에 이미지 포함시키기 

App 자체 리소스에 이미지를 포함시키고 App에서 Asset을 로딩하는 방식입니다. Image.assert()은 기본 dart lib에 포함되는 class 라 별도의  lib추가는 필요 없고, Project 폴더 이미지를 추가하고 pubspec.yaml 파일에 path를 추가하면 사용할 수 있습니다.  

  1. Project 폴더에서 /asserts에 이미지 추가 
  2. pubspec.yaml 파일  - flutter:  assets:  - asset/  에 추가
  3. 사용할 때는 Image.asset(‘assets/sample.jpeg')

Image.asset() 기능은 별도의 asset_cacheprecachedImage Library를 통해서 이미지를 한 번에 메모리에 로딩하는 방법을 사용할 수 있습니다.  App에 수많은 이미지를 한 번에 하나씩 메모리에 읽는 것보다는 asset에 포함된 모든 이미지를 한 번에 메모리에 읽고 이를 canvas에 그리는 것이 속도가 유리합니다. 

Image.asset(): App에 있는 이미지 로딩하기 

Dart code에서는 아래와 같이 Image.assert() 함수에 assert 위치를 포함시킬 수 있습니다.  Path 설정시 Web에서는 /asset을 생략해도(/image/sample.jpeg) 이미지 로딩이 가능하지만, Android와 I-OS 빌드 시에는 이미지 path를 못 찾기 때문에 /asserts/ 을 포함한 full path(/asserts/images/sample.jpeg)로 작성해주세요. 참고로, asset 변경 시 Hot reload시 에러가 발생합니다.  이미지를 추가하는 경우 Debugger를 끊고  다시 attach해서 재 시작해야 합니다.

Image.asset(
  'assets/images/sample.jpeg',
  width: 100,
),

 

Image를 assert에 포함시키는 경우 apk내부의 /assets/assets 폴더에 그대로 포함됩니다.  

Image.assert()  사용 시 apk 내부에 이미지가 포함됨

 

 Web인 경우에도 assets/assets 폴더에 포함되고, AssetManager.json 파일에 Assert에 포함된 이미지 정보를 확인할 수 있으며 Web서버에서 Broswer로 이미지가 다운로드되는 것을 확인할 수 있습니다.  

Image.assert()  사용 시 apk 내부에 이미지가 포함됨

 

 

 

Image.network(): Network Image  가져오기

Image class에서 network() method를 지원합니다.  사용법은 URL 만 지정하면 사용 가능합니다. 

Image.network('https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif'),

 

Network Image 로딩이 실패하는 경우

Network 이미지를 로딩하는 경우 Android와 I-OS에서는 이미지 로딩 자체는 가능하고 exception warning을 발생합니다. 또한 Web App인 경우에는 아래와 같이 이미지 로딩 시 'Failed to load network image' 에러가 발생합니다.  Web server 에서는 서로 다른 domain으로 리소스(CORS)를 접근하는 경우 막혀 있는 아래와 같은 이미지 로딩 에러가 발생합니다.  

 

Network Image 로딩이 실패하는 경우

 

에러 메시지:  Failed to load network image.

Image URL: https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif
Trying to load an image from another domain? Find answers at:
https://flutter.dev/docs/development/platform-integration/web-images

 

수정 방법:  CORS 허용 또는 Web Html render 사용

방법 1. Image 서버에 CORS (Cross-Origin Resource Sharing) 허용 : Web site에서 다수의 domain으로부터  리소스 구성을 허용  

# GCP 인 경우:  참고 링크
gsutil cors set cors.json gs://your-bucket

cors.json 내용
[ {
       "origin": ["http://origin1.example.com"],
        "responseHeader": ["Content-Type"],
         "method": ["GET"],
         "maxAgeSeconds": 3600
  } ]

 

방법 2. Web Rendering 방법을 Html로 변경

Flutter web build 시 html render를 사용합니다.  Android Studio인 경우에는 Addition run arg에  --web-render html을 추가하여 html render를 사용 가능합니다. 

flutter run -d chrome --web-renderer html // to run the app
flutter build web --web-renderer html --release // to generate a production build

* HTML renderer: when the app is running in a mobile browser.

* CanvasKit renderer: when the app is running in a desktop browser.

* auto (default) - automatically chooses which renderer to use.

Network Image 로딩이 실패하는 경우 html render 사용

 

 

 

FadeInImage.memoryNetwork() :  placeholder 사용

인터넷에서 이미지를 다운로드하는 동안 초기 화면이 임시로 placeholder를 표시하는 기능을 사용하기 위해서는 FadeInImage를 사용할 수 있습니다. 사용법은 링크를 참조하세요.

 

transparent_image 설치

dependencies:
  transparent_image: ^2.0.0

 

Dart파일 수정

import 'package:transparent_image/transparent_image.dart';
FadeInImage.memoryNetwork(
  placeholder: kTransparentImage,
  image: 'https://picsum.photos/250?image=9',
),

 

cached_network_image: Cache 지원 network Image 

cached_network_image은 일정 기간 (기본 값 30일, 변경 가능) 동안 인터넷에서 받은 이미지를 Cache 저장소에 저장하, 이미지를 다시 로딩하는 경우 인터넷 대신 Cache 저장소에서 이미지를 로딩합니다.  Pub site에는 Android,  I-OS, MacOS는 지원으로 명시돼 있고,  Web은 미 지원합니다. Web인 경우 이미지는 정상적으로 표시하고, cache 기능만 미 지원하기 때문에 실질적으로 web이라도 Image display용으로 사용 가능합니다. 

 

cached_network_image 설치 

dependencies:
  cached_network_image: ^3.2.0

 

Dart 파일 수정

import 'package:cached_network_image/cached_network_image.dart';
CachedNetworkImage(
  imageUrl: "https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif",
  placeholder: (context, url) => CircularProgressIndicator(),
  errorWidget: (context, url, error) => Icon(Icons.error),
  width: 150,
),

또는 

Image(image: CachedNetworkImageProvider(url))

 

extended_image:  Cached Network Image와 다양한 기능 지원

extended_image는 Network Image에 대한 cache 기능과 함께 Zoom, Panning과 같은 다양한 이미지 프로세싱 기능을 지원하며, Download, Loading, Complete에 대한  state별 widget을 설정할 수 있습니다. 

 

extended_image 설치 

dependencies:
  extended_image: ^6.0.1

Dart 파일 수정

ExtendedImage.network(
  "https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif",
  width: 150,
  cache: true,
)

 

5개 Image 처리 API에 대한 로딩 성능 비교

앞에서 설명한 Image 처리 class의 성능을 비교하면 다음과 같습니다.  처리 속도는 Image.assert() 함수가 가장 빠르고,  네트워크 이미지인 경우에는 Cache 기능을 지원한  API가 빠른 처리 속도를 보여줍니다. FadeImage는 가장 느린 반응 속도를 보였습니다. 아래 동영상에서 최상단 Image.asset(), Image.network(), FaceInImage.memoryNetwork(), CachedImage(), ExtendImage.network() 순서로 구현한 Widged이고, 이미지 로딩 순서는   Image.asset Image < CachedNetworkImage = ExtenedImage <  Image.network < FadeImage 순입니다. 

 

  • Debug 모드 측정, 9MB 크기의 GIF, I-OS Emulator 사용
  • 첫 이미지 로딩은 속도가 유사함
  •  첫 로딩 이후 속도 순서:  Image.asset Image < CachedNetworkImage = ExtenedImage <  Image.network < FadeImage

 

로딩속도비교.mov
4.16MB

 

5개 Image 처리 class의 로딩 성능 비교 (동영상) : Image.asset(), Image.network(), FaceInImage.memoryNetwork(), CachedImage(), ExtendImage.network() 

 

Sample Source

앞에서 설명한 소스를 GitHub의 a01Basic/lib/main02_images.dart에 올렸습니다.  

import 'package:flutter/material.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:extended_image/extended_image.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            Image.asset(
              'assets/images/sample.gif',
              width: 150,
            ),
            Image.network(
              'https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif',
              width: 150,
            ),
            FadeInImage.memoryNetwork(
              placeholder: kTransparentImage,
              image:
                  'https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif',
              width: 150,
            ),
            CachedNetworkImage(
              imageUrl:
                  "https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif",
              placeholder: (context, url) => CircularProgressIndicator(),
              errorWidget: (context, url, error) => Icon(Icons.error),
              width: 150,
            ),
            ExtendedImage.network(
              "https://i.pinimg.com/originals/e9/73/0a/e9730ab2e35d7ec8ac7e432099b5e6d9.gif",
              width: 150,
              cache: true,
            )
          ],
        ),
      ),
    );
  }
}

 

관련 글:

[SW 개발/FrontEnd(Flutter)] - Flutter 특징과 개발환경 설정 방법

[SW 개발/FrontEnd(Flutter)] - Android Studio와 Visual Code로 Flutter Project 프로젝트 생성

[SW 개발/FrontEnd(Flutter)] - Flutter Stateless와 Stateful Widget 개념과Life Cycle, 대표 Widget(MaterialApp, Scaffold)

[SW 개발/FrontEnd(Flutter)] - Flutter Parent-Child Widget간 State 전달과 업데이트 방법

[SW 개발/FrontEnd(Flutter)] - Flutter에서 Widget Tree와 layout 디자인 방법

[SW 개발/FrontEnd(Flutter)] - Flutter에서 Material UI Icon과 Cupertino Icon 검색하고 사용하기

[SW 개발/Python] - Python 자동 테스트를 위한 Pytest 사용법과 예제

[개발환경/우분투] - Docker 개념과 명령어 사용 방법 및 예제

[개발환경/Web Server] - Web Server 성능 및 Load 측정 Tool: Apache AB (Apache Http Server Benchmarking Tool)

[SW 개발/Android] - 안드로이드 adb 설치 및 설정 방법

[SW 개발/Data 분석 (RDB, NoSQL, Dataframe)] - Python Dataframe Visualization: matplotlib로 chart 그리기 (sample code)




댓글