React Native 와 Flutter는 어떻게 다른가

React Native 개발자의 Flutter 경험기

Favorite Medium
FM Stories

--

Dooyoung Gi, Software Engineer

2년 전 처음 리액트 네이티브를 접했을 당시, 같은 코드베이스로 안드로이드와 iOS앱 개발을 동시에 할수 있다는 것이 매력적이었다. 특히 ‘hot-reload’ 와 같은 기능은 UI개발 사이클을 상당히 단축시켰다. Favorite Medium 에 근무하면서 최근 또다른 크로스 플랫폼 프레임워크를 배워볼 수 있는 기회가 생겼는데 바로 구글에서 개발한 Flutter 이다. 약 3개월정도 Flutter를 사용해 봤는데 그간 React Native를 사용해본 경험과 Flutter를 비교해보면 좋겠다는 생각이 들었다. 아직 Flutter 에서 사용해보지 않은 부분들이 많아 공평한 비교가 될지는 모르겠지만, 혹시나 차후 이러한 크로스플랫폼 개발에 관심이 있는 분들에게 도움이 되기를 바라며 적어본다.

데모 프로젝트

이전에 우리 팀이 만든 디자인을 이용해서 Flutter를 사용해보기로 했다. (이 프로토타입은 개발자와 디자이너간의 Flutter를 이용한 협업을 보여주기위해 제작된 간단한 리워드 모바일 앱이다. Flutter 환경에서 디자이너와 개발자간의 협업하는 방법에 대해 보여주는 포스팅 참조)

나는 이 기존 디자인에 실제로 기능을 구현해서 데모 어플리케이션을 제작해보기로 했다. 이 데모를 통해 아래의 기능들을 구현하고자 했다.

  • 이용가능한 딜을 보여주기 위한 가로형 스크롤 뷰
  • 캡처 와 드롭핑 애니메이션 효과
  • 즐겨찾기 목록을 보여주기 위한 하단 서랍 메뉴 뷰
  • 디테일 스크린으로 전환하기 위한 Hero 애니메이션 효과(이미지 공유)

언어의 차이

  • Flutter: Dart
  • React Native: JavaScript

일단 2가지 컴파일 방식의 개념에 대해 간단히 짚고 넘어가자.

JIT — “Just in Time”. 컴파일러가 코드를 번역하고, JIT이 런타임중에 코드를 선택적으로 최적화 한다. 빠른 스타트업 속도를 보여주지만 준비시간(warm-up)이 걸리고 첫번째 실행해서는 퍼포먼스가 다소 낮지만 실행이 반복될수록 점차 향상된다. 하지만 상황에 따라 예측불가한 퍼포먼스를 보여준다.

AOT — “Ahead of time”. 컴파일과 최적화가 실행 전에 이루어진다. 컴파일 시간은 오래걸리지만 최적화가 완전히 이루어졌기 때문에 전반적으로 예측가능한 성능을 보여주고 특히 애니메이션과 같이 무거운 작업에서 좋은 퍼포먼스를 보여준다.

그렇다면, React Native와 Flutter가 어떻게 다를까? 이 두 프레임워크의 언어를 기반으로 전반적인 특징들을 정리해보았다.

리액트 네이티브는 자바스크립트 언어를 사용한다. 원래 리액트(React)가 웹 어플리케이션 개발용으로 먼저 개발되었고 리액트 네이티브가 차후 네이티브 플랫폼을 타겟으로하여 만들어졌는데 기존의 비즈니스 로직과 많은 모듈들을 활용하기 위해서는 자바스크립트를 사용해야 했다.

그렇다면 React Native 가 자바스크립트 코드를 네이티브 플랫폼에서 실행되기 위한 전반적인 프로세스에 관하여 간단히 알아보자: React Native는 자바스크립코드를 번들화해서 그것을 디바이스로 전송한다. 그 다음 네이티브 플랫폼상에서 번들화된 명령어를 수행하여 브릿지를 통해 UI렌더링을 실행할것을 React Native 에게 명령한다. 여기서 브릿지는 자바스크립트가 네이티브 플랫폼에서 그에 맞는 형태로 실행 될 수 있도록 도와주는 인터페이스 역할을 담당한다. (iOS — Objective-C / Android — Java)

자바스크립트는 interpreted 또는 JIT 컴파일 언어이다. 동적(dynamic) 타입의 언어이기 때문에 AOT 컴파일에는 적합하지 않다. iOS의 경우 자바스크립트 코어(JavaScriptCore)라는 자바스크립트 엔진을 제공하는데 이는 Safari 환경에서 JIT을 지원하고 안드로이드는 Chrome V8이 있다. 이러한 엔진은 자바스크립트 코드와 네이티브 플랫폼간의 좋은 성능을 보여주는데 그럼에도 불구하고 React Native는 네이티브 플랫폼 상에서 제한적인 면들이 있다. 예를들어, iOS는 보안상의 이유로 릴리즈 환경에서 JavascriptCore의 JIT을 사용할 수 없다. 또한 Javascript가 AOT에 부적합하다는 것은 무거운 작업에서 예측가능한 퍼포먼스를 보장해주지 못한다.

플러터는 Dart언어를 사용한다. Dart는 자바스크립트에 비해 유연하다. 이 언어가 자바스크립트와 달리 갖는 특별한 점은 정적 타입 또는 다이나믹 타입 성격을 유동적으로 지닐 수 있다는 것이다. 그러므로 다트는 JIT뿐만 아니라 AOT 또한 지원할 수 있다. 게다가, Dart는 플러터를 개발한 구글에서 만든 언어이기 때문에 구글은 필요와 상황에 맞게 언어를 조정할 수 있다. 그리고 실제로 AOT가 Dart 2부터 도입되었다. 다트의 이러한 예외적인 성격은 iOS와 안드로이드 뿐만 아니라 다른 플랫폼까지도 확장될 수 있다는 가능성을 보여준다.

빌트인 컴포넌트

리액트 네이티브와 플러터 둘다 많은 빌트인 컴포넌트를 제공한다. 이는 플러터에서는 위젯이라고 부른다. React Native 에는 기본적으로 제공하는 컴포넌트 외에 특정 플랫폼에서만 동작하는 것들도 있다. 사용자가 하나의 코드로 다수의 플랫폼에서 동일하게 동작하기를 기대하지만 각각의 플랫폼에 적합한 컴포넌트를 구분해서 사용해야 하는 번거로움이 있다. 그 중 React Native 에서 내가 가장 다루기 어려웠던 부분이 네비게이션이다. 네비게이션이 모바일 어플리케이션에서 중요한 부분임에도 불구하고 React Native는 네비게이션을 위한 충분한 빌트인 컴포넌트를 지원하지 않고, 사용자는 외부 모듈을 가져와서 사용해야한다. 이는 시간이 걸리고 관리하기가 쉽지 않다.

반면 Flutter 에서는 굉장히 많은 빌트인 위젯들을 제공한다. 그 범위가 상당히 넓은데, 예를 들어 플러터는 iOS를 위한 Cupertino 테마, 안드로이드를 위한 Material테마를 각각 제공하여 개발자들에게 플랫폼 별 색이나 텍스트, 크기 스펙을 찾을 필요가 없도록 도와준다. 또한 Hero나 Slivers 위젯의 경우 이 자체가 지닌 애니메이션 효과로도 충분히 UI구현이 가능하기 때문에 애니메이션 구현 시간을 덜어준다. Hero 위젯의 경우 위의 데모 어플리케이션에서 실제로 사용했다. Flutter 에서 발견한 유용하다고 생각한 또다른 위젯중에 RichText가 있다. React Native 에서는 한 문장 안에서 글자나 단어 단위로 스타일을 변경하고 싶을 때 HTML 태그를 텍스트에 적용해야 각각에 다른 스타일을 적용할 수 있었다. 하지만 Flutter의 RichText 위젯을 이용하면 그 안에 다수의 TextSpan child 위젯을 넣을 수 있고 각각의 TextSpan에 스타일을 적용할 수 있다.

하지만 이렇듯 기능이 이미 많이 내장된 빌트인 위젯들은 사용하기가 굉장히 편리하더라도 많이 의존하게 되면 차후 UI가 변경되거나 다른 기능들이 추가로 필요할 경우 커스터마이징이 어려울 수 있다.

스타일링

리액트 네이티브는 HTML과 비슷한 React 컴포넌트 JSX 문법을 사용한다. 그리고 컴포넌트의 스타일 속성은 css와 유사한 StyleSheet을 통해 부여한다. 아래의 코드는 리액트 네이티브에서 간단한 텍스트 컴포넌트를 렌더링 할때 쓰이는 스타일 적용 방식을 보여준다.

Rendering part — React Native
StyleSheet — React Native

Flutter 에서 스타일링 방식은 위젯을 통해 이루어진다. 그 차이를 보여주기 위해 코드의 일부를 첨부했다. 보이다시피 padding, margin, alignment나 다른 스타일 속성값들이 위젯으로 정의되어 있다. 스타일을 적용하고싶은 위젯을 스타일 위젯으로 감싸는 방식으로 스타일을 부여한다.

Widget tree — Flutter

‘Padding’ 은 속성값과 함께 그 값을 부여할 child 위젯을 갖고있다. ‘Column’ 은 수직형 레이아웃과 기본적으로 Flex 위젯을 상속함으로써 flex 속성을 지니고, 다수의 child 위젯을 가질 수 있다.

위의 두 예시 모두 비슷한 형태의 텍스트 뷰를 렌더링한다. Flutter의 코드가 React Native 방식보다 좀더 장황해 보인다. 하지만 이부분은 지극히 개발자의 취향차이인데, 만약 웹 개발에 익숙하다면 React Native 방식이 좀 더 친근하게 보이겠다. 나의 경우, 안드로이드 개발을 했었고 스타일과 렌더링 파트는 분리하는것이 편했다. 하지만 Flutter 방식에 익숙해지는데 그리 오래 걸리지는 않았다. 특히, 이 위젯 트리라는 것이 유용하다고 느꼈을 때가 hot-reload 기능을 사용할때 였는데, UI를 변경하면 그것이 즉각적으로 나타나고 이 때 위젯들과 스타일이 어떻게 연계되었는지 뷰의 구조를 한눈에 볼 수 있는 점이 꽤 직관적이라고 생각했다.

모듈 의존성

빌트인 기능만으로는 어플리케이션의 모든 기능을 구현하는것은 불가능하다. 그래서 기능을 확장하려면 외부 모듈이 필요한데 (Flutter에서는 플러그인이라고 부른다) 이 때 모듈 의존성 이슈는 크로스 플랫폼 엔지니어링에서 흔히 발생 할 수 있는 문제이다.

React Native 를 사용할 당시 많은 문제점을 겪었다: 만약 내가 필요한 모듈들이 각각 다른 버전의 React 혹은 React Native 버전에 의존하고 있다면 강제적으로 해당 모듈의 하위 버전을 사용해야하는 경우가 발생했다. (React 혹은 React Native의 버전을 바꾸는것은 다른 모듈들에 영향을 미칠 수 있기 때문에 왠만하면 지양했다.) 적합한 모듈을 발견한 후에는 그것이 다른 플랫폼(ios 혹은 안드로이드)에서도 정상적으로 동작하는지, 충돌은 없는지 확인해야 한다. 다음으로 이렇게 찾은 모듈은 보통 네이티브에 의존성이 연결되어야 하는데 React Native 에서는 이를 위해 ‘react-native link’라는 명령어를 제공한다. 이 커맨드 라인 한번으로 해결되어야 간단히 끝나는 일이지만 보통은 각각의 플랫폼 환경에서 사용자가 직접 모듈 경로를 지정해줘야 하는 경우가 빈번하다. 이 부분에서 네이티브 플랫폼에 대한 이해가 예상했던 것보다 더 요구 될 수 있음을 사용자는 인지하고 있어야 한다. (만약 기존에 안드로이드 혹은 iOS 만 개발했던 사용자라면 더 어려운 상황에 놓일 수 있다.) 어플리케이션의 규모가 커질수록 많은 모듈 들에 의존해야 할 것이고 이러한 시나리오를 더 자주 직면하게 되기 때문이다.

Flutter 에서는 아직 많은 모듈이 필요로하는 작업을 해보지 않아서 별다른 이슈를 발견할 수는 없었다. 사소한 버전 충돌 이슈가 있었는데 패키지 설정 파일에서 버전 수정을 통해 해결되었고 따로 경로를 지정하거나 네이티브 환경에 접근해야 하는 경우는 아직까지 없었다.

결론

React Native 를 모바일개발 도구로 사용한지 꽤 오래 되었고 이것이 주는 장점 뿐만이 아니라 많은 한계점도 경험할 수 있었다. 지금으로써는 단순히 공부의 목적으로 Flutter를 사용했지만 실제 프로젝트에 적용되었을때 그 규모가 커질 수록 이전에는 몰랐던 주요 이슈들이 발생하기 시작한다.

일단은 Flutter를 배우는것이 어렵지 않았고, 다수의 애니메이션효과를 간단하게 구현하고 데모제작을 몇일만에 완료할 수 있었다는 점에서 앞으로 계속해서 여러가지 기능들을 사용해 볼 생각이다.

Flutter는 풍부한 빌트인 위젯을 제공하여 UI작업을 굉장히 간소화 해준다. 하지만 장기적으로 봤을 때 프레임워크에 너무 의존하게 될 수도 있고, 그만큼 커스터마이징에 제한적일 수 있다. Javascript보다 비교적 덜 알려진 Dart 언어를 새롭게 배워야 하겠지만 Java에 익숙한 개발자라면 쉽게 다가올 수 있다. React Native는 Flutter보다 오래되어 보다 방대한 커뮤니티를 갖고 있고, 자바스크립트를 사용한다는 점에서 좀 더 보편 적일 수 있다.

이러한 크로스 플랫폼 프레임워크들은 모두 네이티브보다 부족한 기능들로 외부 모듈(라이브러리) 의존도가 높을 수밖에 없다. 그래서 만약 필요한 모듈이 부재할경우 각각의 네이티브 플랫폼에서 적합한 모듈을 직접 제작해야 할 경우도 발생한다. 이는 결코 단순한 작업은 아니다. 이렇듯 모든 도구들은 장단점이 있고 어떤것을 선택하느냐는 개인의 선호도에 따른 문제이기 때문에 직접 사용해보는 것을 적극 추천한다.

그래서 이러한 단점들에도 불구하고 네이티브가 아닌 크로스 플랫폼 프레임워크를 사용해야 하는것이 맞는가? 이점은 또한 상황에 따라 다르다. 이러한 React Native나 Flutter와 같은 프레임워크는 효율적으로 네이티브와 최대한 비슷한 사용자 경험을 주기 위할 뿐 근본적으로 네이티브는 아니기 때문에 메모리나 퍼포먼스 이슈에 관해서는 더 취약할 수 있다. 그래서 네이티브 만큼의 퍼포먼스를 보여주지 않을 수 있겠지만, ‘코드 재사용’(하나의 코드로 다수의 플랫폼을 동시 개발) 뿐만 아니라 개발자에게 멀티 플랫폼에 대해 상당한 유연성을 제공한다는 것은 무시할 수 없는 장점이다. 따라서, 만약 네이티브 대신 크로스 플랫폼 프레임워크를 채택할 것을 고려중이라면 단순히 코드재사용이나 비용절감의 이유 뿐만이 아니라 해당 제품의 목적과 주 기능들이 그 프레임워크를 통해 충분히 구현될 수 있는지 신중하게 알아보고 결정하는것이 바람직하다고 생각한다.

--

--

Favorite Medium
FM Stories

Favorite Medium builds digital products with tangible purpose for companies around the world.