본문 바로가기
Flutter

Flutter란?

by 안될개발 2023. 8. 11.

Flutter에 대해서 알아봅시다.

Flutter 란?

구글에서 2017년 5월 출시된 모바일/웹/데스크톱 크로스 플랫폼 GUI SDK이다. 하나의 코드 베이스로 안드로이드리눅스WindowsmacOSiOS 및 웹 브라우저에서 모두 동작되는 앱을 위해 출시되었다. 사용되는 언어는 역시 구글에 의해 제창된 Dart를 사용한다. (feat. 나무위키)

 

최근에는 많은 스타트업 회사들이 Flutter 플랫폼을 이용한 개발 및 구인도 하고 있는 듯 합니다.

그러면, Flutter는 플랫폼이 어떤 구조를 가지고 동작하며, 기존 iOS, AOS 개발 방식과는 어떤 차이가 있는지 알아 보도록 하겠습니다.

 

Flutter 구조와 동작 원리 알아보기

 플러터 프레임워크는 세 계층으로 나눠져 있습니다. 가장 하드웨어와 가까운 로우 레벨에는 임베더 계층이 있습니다. 임베더는 플러터가 현재 지원하는 6개 플랫폼의 네이티브 플랫폼과 직접 통신하고 운영체제의 자체 기능을 모듈화해둔 계층입니다. 이 모듈들은 각 플랫폼의 네이티브 언어로 작성되어 있습니다.

 

 중간 계층에는 엔진 계층이 있습니다. 이 계층은 대부분 C++로 작성되어 있으며, 플러터 코어 API와 스키아 그래픽 엔진, 파일 시스템, 네트워크 기능 등이 정의되어 있습니다. 마지막 계층에는 플러터 개발자들이 대부분의 시간을 보내는 프레임워크 계층이 있습니다.

프레임워크 계층에는 플러터 프레임워크를 사용하는 데 필수적인 위젯, 애니메이션, 머티리얼 패키지, 쿠퍼티노 패키지 등이 있습니다. 이렇게 세 계층으로 나뉘고, 잘 모듈화된 아키텍처 덕분에 플러터는 쉽게 여러 플랫폼을 지원하고 일관된 API 및 개발 경험을 제공합니다.

 

 

 스키아 엔진을 사용하는 플러터는 어떤 플랫폼이든 스키아 엔진을 지원한다면 컴파일되고 실행될 수 있습니다. 그래서 플러터 팀은 플러터로 iOS와 안드로이드 앱만 개발하는 데 만족하지 않고, 윈도우, 리눅스, MacOS 애플리케이션, 심지어 웹사이트까지 같은 플러터 코드로 배포하도록 구현했습니다. 따라서 플러터 프로그래밍을 할 줄 안다면 한 번에 6가지 플랫폼을 대상으로 배포할 수 있습니다.

 

 플러터가 스키아 엔진을 사용하는 데는 크로스 플랫폼 앱 개발 프레임워크들과는 다른 장점이 있습니다. 대부분의 크로스 플랫폼 앱 개발 프레임워크들은 웹뷰를 사용하거나 각 플랫폼의 UI 라이브러리를 사용합니다. 그러나 플러터는 직접 스키아 엔진을 사용해 화면에 UI를 그려냅니다. 이때 새로 렌더링이 필요한 위젯들만 렌더링하기 때문에, 다른 크로스 플랫폼 앱 개발 프레임워크보다 높은 퍼포먼스를 선보입니다.

 

 예를 들어, 플러터의 대표적인 경쟁 프레임워크인 리액트 네이티브는 자바스크립트 브릿지를 통해 플랫폼과 통신하며, 각 플랫폼의 UI(OEM 위젯)를 그대로 사용합니다. 따라서 플랫폼과 리액트 네이티브 간 통신을 할 때 필요한 리소스 비용이 큽니다. 그러나 플러터는 위젯을 스키아 엔진에 직접 그려내고, 필요한 제스처 및 이벤트를 브릿지를 통하지 않고 실행하기 때문에 리액트 네이티브보다 빠른 퍼포먼스를 제공합니다. (아래 그림 참고)

 

Flutter 특징

1.  Hot Reload 기능

 플러터는 Hot Reload라는 기능을 제공합니다. 개발자가 코드를 수정하면 앱 상에 바로 반영되어 빠른 피드백을 제공합니다.

 

2. 쉬운 UI 개발

 플러터는 위젯을 이용하여 UI 개발할 있습니다. 모든 위젯은 다른 위젯과 결합하여 복잡한 UI 쉽게 작성할 있습니다. 또한, 다양한 디자인 요소들을 프레임워크에서 미리 제공하고 있어 별도의 디자인 경험이 없는 개발자도 쉽게 UI 구현할 있습니다.

 

3. 높은 성능

 플러터는 네이티브 코드로 컴파일되기 때문에 높은 성능을 보장합니다. 또한, 응용 프로그램을 실행하는 필요한 메모리와 프로세서 효율성이 뛰어나므로 앱이 빠르게 작동됩니다.

 

4. 플랫폼 호환성

플러터는 iOS 안드로이드와 같은 여러 플랫폼에서 작동할 있습니다. 코드 작성을 플랫폼별로 분리하지 않아도 되므로 개발 시간을 대폭 단축할 있습니다.

 

5. 쉬운 애니메이션 개발

 플러터는 애니메이션을 쉽게 개발할 있는 API 제공합니다. 모든 위젯이 애니메이션이 가능하며, 매우 세밀한 제어도 가능합니다. 또한, 애니메이션을 사용하여 앱의 사용자 경험을 향상시키는 것도 가능합니다.

 

6. 개방성

플러터는 오픈소스 프로젝트이며, 많은 개발자가 활발히 참여하고 있습니다. 또한, 다양한 플러그인이 있어서 개발자들이 개발을 보다 쉽게 있습니다.

 

위 특징 사항 중 Hot Reload 기능과 위젯을 이용한 개발이 개발 시간 단축에 큰 효과를 발휘합니다.

iOS와 AOS에도 SwiftUI, Jetpack Compose라는 개발툴킷이 있어서 UI 개발이 좀 덜 수고스러워 지긴 했습니다만, Native UI 개발에 익숙해져 있는 분들에게는 거의 신세계나 다름 없습니다.

 

그러면, UI 개발 방식에 있어서 플러터와 같은 선언형 UI 개발 방식이 있고, 기존 iOS/AOS 와 같은 명령형 UI 개발 방식이 있는데... 이 차이점에 대해서 알아보도록 하겠습니다.

 

명령형 UI vs 선언형 UI 개발 차이점

1. 명령형 UI

 Win32부터 안드로이드, iOS까지 기존 개발자들에게 가장 친숙한 형태로, 메뉴얼하게 화면에 UI View를 추가하거나 삭제할 수 있는 형태의 프레임워크를 말합니다. 예를 들어, 위 그림에서 ViewB에 View c1, c2가 있고, 배경 화면이 빨간색이며, c3을 갖는 화면으로 변경한다면, 명령형 UI에서는 다음과 같은 코드로 구현하게 됩니다.

b.setColor(red)
b.clearChildren()
ViewC c3 = new ViewC(...)
b.add(c3)

 

2. 선언형 UI

 선언형 UI는 새로운 개념입니다. 코드 상에 추가한 화면 View는 더 이상 추가하거나 삭제할 수 없으며, 화면을 변경하기 위해선 새롭게 만들어야 합니다. 여기서 State라는 개념이 도입되는데, 선언형 UI에서는 뷰마다 State가 있으며, 이 State값이 변경하게 되면 새로운 화면을 생성해서 다시 화면을 업데이트하게 됩니다. 위 예시의 왼쪽 그림을 오른쪽 그림처럼 바꾼다면, 선언형 UI에서는 아래 코드처럼 c1, c2를 제거하는 것이 아니라, 새로운 화면을 생성합니다. ViewB가 c3만 자식으로 갖고, 배경 화면이 빨간색인 새로운 상태를 가지게 됩니다.

return ViewB(
color: red,
child: ViewC(...),
)

 

새로운 화면을 생성하면서 발생할 수 있는 오버헤드는 프레임워크에서 담당합니다. 이미 프레임워크에서 최적화를 많이 해뒀기 때문에 성능 이슈는 크게 걱정하지 않아도 좋습니다.

 

 선언형 UI에서는 UI의 변경 사항은 프레임워크에서 담당하도록 했기 때문에, 개발자는 화면이 변경할 때 발생할 수 있는 오버헤드를 고려할 필요가 없으며, State 변화에만 집중할 수 있어 개발하기 편리해집니다. 명령형 UI를 사용한 개발자라면, State라는 새로운 개념이 익숙하지 않을 수 있지만, 사용하다보면 잔업무가 줄어드는 것을 느낄 수 있습니다.

 선언형 UI는 리액트를 사용하는 웹에서 주로 사용되다가, 현재는 플러터, Swift UI, Android Jetcompose과 같은 새로운 UI 프레임워크에서도 적용되는 추세입니다. 앞으로도 다양한 프레임워크에서 선언형 UI를 채택하게 될 것으로 예상됩니다.

 

마지막으로 개발 코드로도 얼마나 차이가 있는지 보겠습니다.

 

Native Code VS Flutter Code

간단한 리스트를 보여주는 코드를 보겠습니다.

 

안드로이드의 경우 파일 3개의 코드가 필요합니다.

/*
MainActivity.java
*/

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {
    String[] fruitList = {"Apple", "Banana", "Cherry", "Durian", "Elderberry", "Fig", "Grape"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ListView listView = findViewById(R.id.listView);
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, fruitList);
        listView.setAdapter(arrayAdapter);
    }
}
/*
activity_main.xml
*/

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
/*
simple_list_item_1.xml
*/

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAppearance="?android:attr/textAppearanceListItemSmall"
    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
    android:minHeight="?android:attr/listPreferredItemHeightSmall"
    android:singleLine="true" />

 

플러터에서는 파일 하나로 끝이죠!

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  final List<String> fruitList = ["Apple", "Banana", "Cherry", "Durian", "Elderberry", "Fig", "Grape"];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Demo Home Page'),
        ),
        body: ListView.builder(
          itemCount: fruitList.length,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(
              title: Text(fruitList[index]),
            );
          },
        ),
      ),
    );
  }
}

 

마치며...

Flutter는 확실히 장점이 많은 플랫폼이긴 합니다. 그러나 단점이 없는 건 아닙니다.

Native 대비 성능 이슈, Dart 언어 사용, UI 코드의 계층 구조로서의 가독성 문제 등이 있습니다만...

그럼에도 불구하고 iOS, AOS 앱을 동시에 개발 및 출시 할 수 있다는 장점이 명확한 플랫폼 인 것 같습니다.