Flutter vs Unity? 게임이 아닌 앱을 Cross platform 으로 개발시 Flutter 와 Unity 고려해야 할 점들은?

많은 개발자, 또는 개발자가 아니더라도 앱 개발과 크로스플랫폼 개발 환경에 대해서 관심이 있는 사람이라면 Flutter 와 Unity3D(이하 유니티) 쯤은 쉽게 접하는 키워드일 것이다.

필자는 최근 유니티 엔진을 이용해서 안드로이드 단말에서 구동하는 키오스크 앱 프로젝트를 진행한 경험이 있고 이 과정에서 크로스 플랫폼(Cross platform) 개발에 대해서 많은 고민을 했던 바 있다.

일단 Flutter유니티 에 잘 모르신다면 한번 둘러보시길 ...

Why Unity?

왜 안드로이드 스튜디오에서 자바나 코틀린을 이용하는 일반적인 앱 개발 방법을 사용하지 않고 유니티(정확하게는 Unity as a Library) 엔진을 사용했는가?
이유는 별거 없다. 이전에 게임 개발을 하면서 유니티와 C# 언어가 제공하는 강력한 기능에 매료되어 그 후로 좀 Complex 한 UI 개발이 필요한 경우에는 일단 1순위로 유니티를 사용하는 걸 고려하게 되어 버렸다. 그 뿐이다.

Why Flutter?

일단은 Google 형님이 강려크하게 Support 하고 계시는 중이다. 하루가 멀다 하고 새로운 위젯이나 라이브러리들이 나온다. 이러한 뒷받침에 왕성한 커뮤니티를 기반으로 한 다양한 플러그인, 라이브러리들이 개발을 더 쉽고 빠르게 해준다.  

  

Flutter, 유니티 모두 크로스 플랫폼이라 One code base 로 Android, iOS 동시에 개발이 가능하다

이번 글에서는 게임이 아닌 일반적인 앱을 개발할 때 Flutter 나 유니티중 어느 한가지를 선택해서 개발을 할때 고려해야 할 점들은 어떤 것들이 있을지 필자가 그동안 경험한 부분과 Search 한 것들을 토대로 각각의 장단점을 나열해 본다. 여기서 나열한 부분은 지극히 개인의 경험상 느낀 생각을 표현한 것일 뿐이며 어느 한쪽의 단점은 상대쪽에겐 장점이 될 수 있다.  

  

유니티로 앱 개발시 단점

  1. 배터리 광탈 - 하지만 어느정도 Optimizing 가능함
  2. 일반 앱 개발시 발생하는 문제와 관련하여 StackOverflow 에서 찾을 수 있는 자료가 많지 않다.
  3. 일반 앱 개발 관련하여 생산성을 높여주는 라이브러리들이 많지 않다.
  4. UI 개발시 Material 이나 Cupertino style 로 표현하고자 하는 경우 유니티가 제공하는 UI 시스템을 최대한 이용하여 생산성을 끌어 올리기 쉽지 않다.
  5. 유니티로 일반 앱을 개발하는 개발자가 많지가 않아서 서비스가 성장할수록 관련 개발자를 채용하기가 용이하지 않을 수 있다.
  6. Navigation stack (페이지 이동 관련) 구현을 직접 해야 한다.
  7. SVG 사용 쉽지 않음.
  8. APK 사이즈가 크다. 네이티브, Flutter 통틀어서 가장 크다.
  9. 네이티브 기능(카메라, 센서 등)에 특화된 앱 개발시 고단해짐.
  10. Android, iOS 각 SDK 변화에 대한 빠른 대응이 미흡하다.

유니티로 앱 개발시 장점

  1. 네이티브스러운(Material, Cupertino) UI 를 고집하지 않는다면 UI 개발이 즐거워진다.
  2. UI 개발시 유연성이 커져서 디자이너/artist 들의 요구사항을 왠만하면 무리 없이 반영할 수 있다. 이를테면, Flutter 나 네이티브로 개발할 때처럼 디자이너가 요청한 부분에 대하여 가능한지, 가능하지 않은지, 어떤 부분은 수정해야 하는지, 감안해야 하는지 등등... 많은 부분에서 서로 고민하는 부분이 덜해진다. 이 부분이 내가 유니티로 일반 앱 개발을 즐기는 가장 큰 이유가 아닌가 생각한다.

Flutter 로 앱 개발시 단점

  1. 기존 앱 개발자가 진입하기에 그래도 Learning curve 가 있다. (새로운 언어 Dart 를 익혀야 한다)
  2. Flutter 자체가 아직 성숙하지 않은 상태라 계속해서 개발이 되고 변경되는 부분이 많다 (Dart 도 마찬가지).
  3. Java, Kotlin, C# 대비 Dart 에 대한 신뢰도가 크지 않다.

Flutter 로 앱 개발시 장점

  1. 네이티브 코드와의 Communication 이 비교적 간단함.
  2. UI 개발시 Material, Cupertino 등 일반 앱 스타일스러운 개발이 빠르다.
  3. Hot reload 로 개발이 정말 빨라진다. 이 부분은 특히 장점이라 할 만하다.
  4. 비교적 화려한 UI (에니메이션 등) 구현이 가능하다.  

  

 

** FYI **  

유니티에서 Flutter 위젯을 사용하는 것과 같은 개발 경험이 가능하게 해주는 라이브러리 UIWidgets 도 존재하니 참고할 만하다.  

  

결론 (지극히 개인적인 의견)

  • 화려하고 복잡한 UI 개발이 필요하면 유니티, 네이티브 스러운 앱을 빠르게 개발하고자 한다면 Flutter ??
  • C# 과 유니티를 사랑하는 입장에서 크로스 플랫폼 개발시 아직까지는 유니티를 1순위로 사용하고 싶다. 하지만 네이티브 스러운 앱 개발시 Flutter 도 쓸만한 거 같고 이 부분에 대한 고려시 그 비중이(Flutter에 대한) 점점 커진다.

이벤트 관련 기능을 구현할 때 C# 에서 제공하는 이벤트 (Delegate, Action 등) 대신 UnityEvent 를 사용하는 유일한 이유는 UnityEvent 가 에디터에서 사용 가능하기 때문이다. 에디터 상에서 이벤트에 드래그 앤 드랍으로 등록 할 수 있고 어떤 이벤트가 등록된 상태인지 확인 할 수도 있다.

이에 더해서 이벤트를 등록하고 사용할때 해지도 해줘야 하는데 UnityEvent 를 사용하면 이러한 등록/해지 과정에서의 실수로 인한 문제를 방지해주는 이점이 있다. 그 이유는 UnityEvent 가 weak reference 기반으로 동작하기 때문.

하지만 이러한 이점에도 불구하고 개발자가 Editor plugin 을 만드는게 목적이 아니라면 항상 native C# event 를 사용하기를 권장한다. 그 이유는 더 빠르고 메모리를 덜 사용하기 때문이다. (fast performance, small memory usage)

  

 

더 읽을거리
UnityEvent vs Delegate Event benchmark
Event Performance: C# vs. UnityEvent

 

출처 : StackOverflow

  

  

The only advantage and reason to use UnityEvent is that it allows you to use events in the Editor. That's for drag and drop people or those making Editor plugins.

Another advantage of UnityEvent is that it prevents the problem of Unity Object not being freed due to the misuse of delegates or using anonymous delegates with Unity Objects. Although they get freed when the main script that's holding them is destroyed. The reason for this is because UnityEvent is implemented with weak references therefore removing/minimizing this problem. These two things are still not worth it to use UnityEvent over native C# events.

You should always use native event and delegate over UnityEvent if you are not making an Editor plugin because of its fast performance and small memory usage. See this and this post post for more information.

WorkManager 란?

여러 개의 백그라운드 작업을 조건에 따라 스케쥴링 하고 상태 체크, 취소 등의 작업을 쉽게 할 수 있도록 도와주는 라이브러리

백그라운드 작업

  • 유저 입장에서 보면 눈에 보이지 않게 이뤄지는 작업
  • 쓰레드 개념으로 보면 메인쓰레드(UI 쓰레드) 가 아닌 쓰레드에서 동작하는 작업
  • 알람매니저, 서비스 등의 안드로이드 컴포넌트를 통해 실행되는 작업
  • API 호출, DB 쿼리, 데이터 동기화, 로깅, 미디어 업로드, 폴링, 백그라운드 음악 재생, 이미지 프로세싱

백그라운드 작업을 특성에 따라 구분

그럼 WorkManager 를 어디에 써야 하나?

미룰수 있는 일 | 실행이 보장되어야 하는 일


사용하기




Constraint

WorkRequest 생성(worker 와 constraints 결합)

생성한 workrequest 를 WorkManager에 enqueue

WorkRequest 의 상태 체크하기

WorkRequest 반복되는 작업 만들기

병렬실행

순차실행

콤보

그룹핑

'Android > Android Develop' 카테고리의 다른 글

Android Navigation  (0) 2019.07.07
Android Slice  (0) 2019.07.07
Instant App  (0) 2019.07.07
Android Notification 개발시 주의사항  (0) 2019.07.07

Navigation 의 의미

기존 문제점

  • exception 에 잘 정돈된 fragment transactions 을 어떻게 할 것인가
  • 앱의 여러 위치에 어떻게 deep link 를 만들고 최신화할 것인가
  • argument bundle 을 어떻게 안전하게 전달할 것인가
  • up and back 이 어떻게 사용자가 원하는 방식으로 동작할 것인가
    • 특히 deep link 로 앱에 들어올 경우 back stack 을 어떻게 조절할 것인가
  • 기존의 errorprone boilerplate code 를 어떻게 없앨 것인가
  • 잘 동작하는지 어떻게 Testing 할 것인가

Navigation 이 할 수 있는 일

  • Handling fragment transactions
  • Deep linking is a first class operation
  • Type safety when passing information while navigating
  • Handling up and back correctly by default
  • Provides defaults for animations and transitions
  • Navigation UI patterns like navigationn drawers and bottom nav with little additional work
  • Android Studio offers tooling for visualizing and editing the navigation flow of an app

Navigation 사용





NavHostFragment implements NavHost

a widget that is meant to swap in and out different fragment destinations as you navigate through the navigation graph


NavigationUI(navigation-ui-ktx)

collection of static methods that associate menu items with navigation destinations
destination id를 id로 가진 menu item 을 누를 경우 해당 destination 으로 이동 시켜줌



NavController


NavOptions

Safe Args




Deep Linking



'Android > Android Develop' 카테고리의 다른 글

WorkManager  (0) 2019.07.07
Android Slice  (0) 2019.07.07
Instant App  (0) 2019.07.07
Android Notification 개발시 주의사항  (0) 2019.07.07

Slices 란?

Android's new approach for remote content

  • Tempalted
  • Interactive
  • Updateable
    Backwards-compatible through KitKat/API 19+ (95% of devices)

The Goal?

Reusable API to generalize for how we present remote app content in Android
Slice를 하나의 재사용 가능한 API 로 만들어서 안드로이드 내에서 원격 컨텐츠를 일반적으로 사용할 수 있게 하기 위함

Slice 사용

Define your SliceProvider in AndroidManifest

    ...
    <!-- Slices definition -->
    <provider anroid:name=".MySliceProvider"
        android:authority="com.example.slicesample"
        android:exported="true"
    </provider>
    ...

Implement your SliceProvider

    class MySliceProvider : SliceProvider() {
        fun onBindSlice(uri: Uri) : Slice ? {
            if(uri.path == "/wifi") {
                rturn buildWifiSlice(uri)
            } else if(uri.path == "/alarms") {
                rturn buildAlarmsSlice(uri)
            } else if(uri.path == "/nightlight") {
                rturn buildNightLightSlice(uri)
            }
            ...
            return null
        }

        ...
        fun buildWifiSlice(sliceUri: uri) : slice {
            val listBuilder =
                ListBuilder(getContext(), sliceUri, INFINITY)

            val row = RowBuilder(listBuilder).setTitle("Wi-fi");

            if(!isWifiStateLoaded) {
                loadWifiState()
                row.setSubtitle(null, true /* isLoading */)
                    .addEndItem(null, true /* isLoading */)
            } else {
                ...
            }

            listBuilder.addRow(row)
            return listBuilder.build()
        }
        ...
    }

'Android > Android Develop' 카테고리의 다른 글

WorkManager  (0) 2019.07.07
Android Navigation  (0) 2019.07.07
Instant App  (0) 2019.07.07
Android Notification 개발시 주의사항  (0) 2019.07.07

인스턴트앱 이란?

  • 설치 없이 실행 가능한 앱 - 설치화면으로 보여주는 대신, 앱의 일부 기능을 경험할 수 있다
  • Google Play 나 모바일 웹에서 인스턴트앱을 실행할 수 있다
  • 필요시 설치화면으로 보여줄 수 있다

앱 링크 제공

  • 모바일 웹에서 링크를 클릭했을 때 진입할 액티비티에 인텐트 필터를 설정

  • 앱 링크는 별도의 인증절차를 거쳐야 한다

      <intent-filter>
          <action android:name="anroid.intent.action.ViEW" />
          <category android:name="anroid.intent.category.DEFAULT" />
          <category android:name="anroid.intent.category.BROWSABLE" />
          <data android:scheme="http" android:host="www.example.com" />
          <data android:scheme="https"
      </intent-filter>

    모듈 분리 (4MB size 제한)

    • 인스턴트 앱이 실행될 때 Google Play 에서 일부 기능을 위한 모듈을 다운받는다
    • 필요한 기능만 다운받을 수 있도록 프로젝트를 기능별 모듈로 분리해야 한다
    • Base Feature, Feature 로 분리 (각각의 Feature 들은 BaseFeature 에 의존성을 가진다)
    • 어떤 부분을 Base 로 잡을지 고민을 많이 해야됨
    • 4MB 크기 제한은 크리티컬함

    인스턴트 앱에서 지원되는 권한 문제

    • 지원되는 권한들의 제한적임 (예: VOD 앱에서 READ_PHONE_STATE, READ_EXTERNAL_STORAGE 등 요청할 경우 거절됨)
    • 인스턴트 앱 내의 통신은 모두 https 만 가능

    모듈간의 의존성 때문에 gradle 관리가 힘들다 - gradle sync 문제가 빈번함

    데이터바인딩 지원이 안된다

    백그라운드 작업 제한 - 로컬 웹서버 실행할 수 없음

'Android > Android Develop' 카테고리의 다른 글

WorkManager  (0) 2019.07.07
Android Navigation  (0) 2019.07.07
Android Slice  (0) 2019.07.07
Android Notification 개발시 주의사항  (0) 2019.07.07

Android Api 버전별로 다른 동작의 notification 을 만들려면 Action 을 이용함

NotificationCompat.Action(int icon, CharactorSequence title, PendingIntent intent)

Large icon 을 설정하면 버전별로 각각 다른 위치에 아이콘이 보이게 된다

단순구현

    public Notification getNotification(Context context) {
        return new NotificationCompat.Builder(context, getName())
            .setSmallIcon(R.drawable.noti_status_icon)
            .setColor(Color.GREEN)
            .setContentTitle("content title area")
            .setProgress(100, 0, true)
            .setLargeIcon(getBitmapFromResource(context, R.drawable.badge_gift))
            .setShowWhen(false)
            .addAction(new NotificationCompat.Action(R.drawable.star, "star", null))
            .addAction(new NotificationCompat.Action(R.drawable.check, "check", null))
            .build();
    }

OS 버전별로 다른 Notification 배경 색상

    <TextView
        style="@style/TextAppearance.Compat.Notification.Title"
        ... />

    // 버전별로 분기해서 style 을 만들어 놓음
    TextAppearance.Compat.Notification.Title(.../values/values.xml)

기본 icon 활용

    android.R.drawable.stat_sys_download
    android.R.drawable.stat_sys_download_done

Create a fully custom notification layout - Full custom layout 을 만드는 경우

  • setStyle() 을 호출하면 안됨
  • OS >= 7.0 인 경우 DecoratedCustomViewStyle() 적용
  • OS >= 7.0 인 경우 Padding 이 자동으로 들어가는 점 고려

Grouping

    .setGroupSummary(true)

Channel : 채널

  • Android O 이상부터는 채널 적용해야 Notification 정상 노출

  • 개발자는 Notification 에 채널을 할당하고

  • 사용자는 채널에 있는 Notification 들에 대해 개별 설정을 할 수 있음

      if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
          notificationManager.createNotificationChannel(new NotificationChannel(
              "channelId",
              "channelName"
              NotificationManager.IMPORTANCE_DEFAULT));
      }
      notificationManager.notify(ordinal(), notification);
    
      public Notification getNotification(Context context) {
          return new NotificationCompat.Builder(context, getName())
              .setSmallIcon(R.drawable.noti_status_icon)
              .setColor(Color.GREEN)
              .setGroup(GROUP_NAME)
              .setChannelId("channelId")
              .setContentTitle("channel test")
              .build();
      }

'Android > Android Develop' 카테고리의 다른 글

WorkManager  (0) 2019.07.07
Android Navigation  (0) 2019.07.07
Android Slice  (0) 2019.07.07
Instant App  (0) 2019.07.07

JetPack 과 AndroidX 에 관하여 간략하게 정리한 글입니다.

 

JetPack

  • 안드로이드 앱 개발을 보다 손쉽게 할 수 있게 도와주는 라이브러리, 도구, 가이드 모음
  • 이미 널리 쓰이는 아래와 같은 라이브러리들을 모아서 JepPack이라 부르고 새로운기능도 추가
    • Android Architecture Component
    • Support-v4
    • AppCompat-v7
    • ConstraintLayout
    • Design Support Library (중 일부)
  • 안드로이드 개발에 로켓을 달아줌??
  • Cute logo 있음

 

 

So Why JetPack ?

  • 하위 호환성 보장
    • 플랫폼에 새로운 기능이 추가 된다 하더라도(예를 들어 JobScheduler) 사용자가 그 버전의 OS 를 쓰지 않으면 결국 그 기능을 쓸 수 없다. 그리하여 코드내에서 분기를 해서 플랫폼 별로 코드를 작성한다던지 하는 불편함을 감수 해야 했다.하지만 JetPack 에 새로 도입된 WorkManager 를 사용하면 깔끔하게 코드를 작성할 수 있다. 따라서 버전별 고민 없이 그냥 라이브러리에서 가져다 쓰면 된다는 편리함 있음.
    • 코틀린의 경우 ViewTreeObserver 에서 onPreDraw 에서 뭔가를 해주어야 되는 경우, doOnPreDraw 로 깔끔하게 작성할 수 있음
    • IDE 와 통합되어 ROOM 에서 SQL Annotation 사용시 annotation 작성시 오류가 있을 경우 오류를 보여줌

 

 

JetPack 에 새로 포함된 기능들

  • WorkManager
  • Navigation
  • Paging
  • Slice
  • Recyclerview Selection
  • Recyclerview ListAdaptor
  • androidx.webkit
  • Browser action
  • HeifWriter
  • Material Components - Design 의 독자적인 라이브러리로 계속 발전 예정
    • https://material.io/develop/android/

 

 

 

AndroidX

  • android.support.v4, v7, v9 .... 등 하위 호환성을 위해 더럽게 만들어야 했던 지원라이브러리 버전들을 깔끔하게 정리(사실 현재는 minSDK 가 v9 보다 훨씬 윗 버전으로 올라가 버렸기 때문에 더이상 지원라이브러리명이 무의미 하게 되어 버렸다)
  • 앱에서 다른 라이브러리를 쓰는 경우 그 라이브러리가 사용하는 지원라이브러리와 내가 쓰고 있는 라이브러리 디펜던시가 다를 경우 충돌이 발생하게 되는데 그걸 해결하려고 gradle dependency 선언할때 지원라이브러리 버전을 exclude 해버리거나 해야 했음. 그래서 이걸 깔끔하게 정리해서 지원라이브러리명을 androidx.* 로 통일함
  • 기존의 덩치가 컷던 라이브러리들을 위젯, 유스케이스 별로 분리
    • support-core-ui
      • asynclayoutinflater
      • drawerlayout
      • interpolater
      • cursoradapter
      • customview
      • slidingpanellayout
      • swiperefreshlayout
      • viewpager
    • support-compat
      • core
      • collections (java library)
    • support-core-utils
      • documentfile
      • loader
      • localbroadcastmanager
      • print
  • 라이브러리 네이밍시 기능단위로 그룹/아티팩트 ID, 패키지명 지정 (v4, v7 따위는 모두 제거)
    • Maven
      • androidx.<feature>:<feature>-<sub-feature>
      • androidx.lifecycle.lifecycle-viewmodel:2.0.0-beta01
    • Java 패키지
      • androidx.<feature>.ClassName
      • androidx.lifecycle.ViewModel
    • So, 내가 원하는 라이브러리, 패키지를 찾기 쉬워짐
  • AndroidX 1.0.0 으로 버전 리셋
  • 엄격한 Semantic versioning 도입으로 의존성 관계를 깔끔하게 정리하고자 함
    • 1.0.0 : <MAJOR>.<MINOR>.<PATCH> 
    • binary 호환성이 깨질때 major 버전이 올라가고, 기능이 추가될때 마이너 버전이 올라감, PATCH:버그픽스
    • 예 : viewmodel 에 새로운 기능이 들어오면 viewmodel:1.0 -> viewmodel:1.1 이런식으로
  • Design Support Library 의 경우는 전부 androidx 로 전환되지 않고 정말 필수적 기능만 androidx 로
    • androidx.coordinatorlayout.CoordinatorLayout
  • 나머지는 Material Components 로
    • com.google.android.material
  • Support Library 28.0.x 버전은 마지막이고 AndroidX 1.0.x 가 거의 동일한 기능 제공
  • Support Library 28 버전을 끝으로 AndroidX 로 마이그레이션이 필요함
    • Android Studio 에서 Source code 마이그레이션 기능을 지원함 (Refactor -> Migration to)
    • Jetifier - binary 단에서 마이그레이션 제공(Jar 파일 등)
      • gradle.properties 를 통해 활성화
        • android.useAndroidX=true
        • android.enableJetifier=true
        • VCS 에 커밋하는게 좋음
      • 빌드시 쓰이는 (kapt, annotationProcessor 등) 라이브러리가 레거시 패키지를 참조한다면 여전히 문제 발생 가능
  • Cute logo 없음

+ Recent posts