React Navite -> Flutter 개발 플랫폼 이전 후기 [1. React-Native]

React Navite -> Flutter 개발 플랫폼 이전 후기 [1. React-Native]

 2018년, 새로운 핀테크 서비스를 개발하고자 앱 개발 플랫폼 비교를 시작했다. 웹앱은 퍼포먼스 문제때문에 고려사항에 없었고, 자마린과 리액트 네이티브 중 어떤 것을 사용할 것 인가를 놓고 고민하다가 결국 리액트 네이티브를 선택했다. 지금 엄청난 인기를 누리는 플러터는 당시 신생이었고, 깃허브 스타수도 굉장히 적었기 때문에 고려하지 않았다.

 한국에서 개발자를 하며, 느낀 React-Native(이하 RN)의 장단점을 간략하게 정리하면 다음과 같다.

장점

  1. CodePush로 앱 심사를 거치지 않고 업데이트
  2. 방대한 JS디버깅 플랫폼 이용 가능 (ex: bugsnag)
  3. React와의 문법 유사성

단점

  1. RN의 버전을 올리는 것이 너무 힘듦
  2. Navigation을 3rd-party에 의존해야 함
  3. ios와 android의 css항목 불일치 때문에 코드가 매우 더러워짐
  4. 결국 js이기 때문에 앱의 스케일이 커질 시 속도저하
  5. React와의 컴포넌트 공유 X

 단점들이 장점보다 많다. 아무래도 ‘Write once, use everywhere’ 라는 장점 하나로 많은것을 희생하는 방식이고, 네이티브보다 작은 생태계를 형성하는 하이브리드앱 플랫폼 특성상 어쩔 수 없다. 장점의 볼드체로 작성한 부분은 어떠한 플랫폼들도 가지지 못한 아주 강력한 장점이고, 단점의 경우엔 RN을 포기한 결정적인 이유다. 아래는 장점과 단점에 대한 내 간략한 생각들을 정리해 두었다.

장점

  1. CodePush로 앱 심사를 거치지 않고 업데이트
// Active update, which lets the end user know
// about each update, and displays it to them
// immediately after downloading it
class MyApp extends Component {}
MyApp = codePush({
  updateDialog: true,
  installMode: codePush.InstallMode.IMMEDIATE,
})(MyApp);

 위 예시처럼 사용하는 컴포넌트 클래스를 넘겨주면 라이브러리가 알아서 버전 업데이트를 해준다. 원리를 간략하게 설명하자면, RN이 사용하는 jsbundle과 image asset들을 마이크로소프트 서버에 업로드 하고, 이를 유저가 앱 사용시 번들의 최신버전을 확인해 업데이트 하는 기능이다. 이 기능은 웹앱, RN을 제외하고선 어떠한 하이브리드, 네이티브 플랫폼도 불가능하다.
 또한 웹처럼 서버에서 바로 소스코드를 가져오는 경우가 아니기 때문에 기존 앱들은 알파, 베타 서비스로 오류를 확실하게 잡고 넘어가야 했다. 내부 개발자의 실수로 버그가 발생했을 때, 긴급하게 수정해 최대한 유저의 이탈을 막을 수 있는 매우 강력한 기능이다. 나는 이 기능이 사실상 리액트 네이티브의 전부라고 생각한다.

 개발자도 사람인 이상 실수를 할 수 밖에 없고, 미처 확인을 다 하지 못한다. 특히나 스타트업은 테스터가 아예 없거나 소수라 모든 버그를 잡아내지 못할 가능성이 높은데, 이는 ios 에겐 굉장히 치명적이다. 애플측 테스터가 미처 검수를 하지 못했을 경우, 유저들은 최소 3일동안이나 치명적인 버그를 가진 앱을 사용해야만 한다. 이때문에 많은 회사들은 단지 이 기능 때문에 플러터대신 리액트 네이티브를 선택한다.

  1. 방대한 JS디버깅 플랫폼 이용 가능 (ex: bugsnag)

 이건 사실 소스맵 업로드가 가능하다는 장점이다. js의 경우, 여러 파일을 작성하고 release모드로 묶으면 jsbundle과 sourcemap이 같이 생성된다.

Repeat.prototype.toString = function () {
  if (this.size === 0) {
    return "Repeat []";
  }

  return "Repeat [ " + this._value + " " + this.size + " times ]";
};

Repeat.prototype.get = function (index, notSetValue) {
  return this.has(index) ? this._value : notSetValue;
};

Repeat.prototype.includes = function (searchValue) {
  return is(this._value, searchValue);
};

Repeat.prototype.slice = function (begin, end) {
  var size = this.size;
  return wholeSlice(begin, end, size)
    ? this
    : new Repeat(
        this._value,
        resolveEnd(end, size) - resolveBegin(begin, size)
      );
};

 bundle로 묶으면 저런 코드들이 튀어나온다. 이 코드들이 내가 작성한 코드들 중 어떤 부분에 해당하는지는 전혀 알 수 없다. 하지만 sourcemap은 저 코드들이 원래 작성한 코드의 어느 부분에 해당하는지를 알려주어 개발자가 편리하게 디버깅을 할 수 있게 도와준다. 웹이나 서버에서는 굳이 node.js가 아니어도 이러한 기능들이 많이 있지만, 플러터에는 없다.
 예를들어, 내가 현재 사용하는 플러터의 경우, 릴리즈모드에서는 에러가 어느 위치에서 발생했는지 정확하게 알려주지 않는다. 그래서 리포트를 보낼 때 대략적인 위치를 담아보내는데, 여간 귀찮은 작업이 아니다. 이럴땐 정말 RN이 선녀같다…

  1. React와의 문법 유사성
     별 것 아닌 장점이라 느껴질 수 있지만, 한개의 문법만으로 개발하는것과 여러개의 문법으로 개발하는것은 생산성에 있어 큰 차이가 난다. 스타트업에서 개발을 하면서 웹, 앱을 넘나들며 개발을 할 일이 많은데 ReactJs문법으로만 개발하니 정말 편했다. 물론 후술할 단점 부분에서 이 장점은 상쇄되고만다.

단점

  1. RN의 버전을 올리는 것이 너무 힘듦
     버전이 0.60을 넘어가면서 이 문제는 조금이나마 개선되었지만, 이는 언제까지나 네이티브코드를 작성하지 않았을 경우에만 해당된다. 기존에는 아래 명령어로 버전을 업그레이드 해왔다.

    react-native-git-upgrade

 명령어에서부터 어떤 방식으로 업그레이드 되는지 짐작이 가지 않는가? git으로 버전관리를 하다보면 push/pull을 하는 과정에서 충돌 발생 시 수동으로 merge하는 과정을 많은 개발자들이 경험했을것이다. 0.55부터 개발을 시작해 현재의 0.61까지 단 한 번도 충돌이 나지 않은적이 없다. 그래서 버전 업그레이드를 할 때가 되면 항상 압축파일로 프로젝트를 백업해 두었다.
문제되던 업그레이드 방식

https://www.npmjs.com/package/react-native-git-upgrade

 위 패키지로 업그레이드를 실패해 아예 앱이 안돌아가는 경우에는, rn-diff-purge라는 페이지로 들어가 직접 다른점을 확인하고 손으로 다른점을 찾아 바꿔야 한다. 유저들이 많이 업그레이드를 하던 0.59 -> 0.61버전페이지를 들어가 보자.

https://github.com/react-native-community/rn-diff-purge/compare/release/0.59.10..release/0.61.5

저걸 하나하나 손으로 바꿔서 넣다보면 자주 틀리는데, 그러면 어디가 잘못됐는지를 찾기 힘들어 보통은 처음부터 다시 시작한다 (…)

 서드파티의 기능 개선때문에 어쩔 수 없이 버전을 올려야 할 때면 그날은 밤새 작업을 했다. 1년넘게 이 짓을 하고나니, 이게 뭐 하는 짓인가 싶어 자주 허탈감에 빠지곤 했다.

  1. Navigation을 3rd-party에 의존해야 함  RN 을 처음 접했을 때 정말 충격이었다. 엄청난 사용자수를 가졌고, MS가 주도적으로 사용하는 플랫폼이 기본적인 네비게이션 조차 없다니! 다행히 JS진영의 React-navigation과 네이티브 진영의 React-native-navigation이 있었지만, 서드파티여서 지원이 늦다는 단점을 안고있었다.
     나는 처음엔 React-navigation을 사용했으나, 이내 React-Native-Navigation으로 넘어갔다. 그 이유는,
  2. 앱 스케일이 커질경우 매우매우매우 느리다.
  3. redux를 사용시, 큰 데이터가 담겨있을 경우 네비게이팅만 2초가랑 소요
    그래도 최근 @react-navigation/native라는 패키지가 나와 조금은 해결되었다만, 결국 React-Native-Navigation보다는 느리다.
     20,000자 이상의 스트링을 redux에 넣고 navigate에 걸리는 시간을 간략히 정리해보자면 다음과 같다.
RN RN/native RNN
2s 0.5s <=0.5s

 ‘아니 뭘 하길래 20,000글자나 포함되어있는 데이터를 가지고 있냐’ 라고 할 수 있는데, 핀테크 앱을 개발하다보면 20,000자는 우습고 100,000자까지 가기도 한다.

  1. ios와 android의 css항목 불일치 때문에 코드가 매우 더러워짐  아주 간단한 예를 들어보겠다.
// RN
<Button
  title="버튼"
  color="blue"
  onPress={() => Alert.alert("Button with adjusted color pressed")}
/>

좌측이 안드로이드, 우측이 ios다. 사진의 빨간선을 기준으로 미세한 차이가 생긴다. ios는 가운데 정렬이 제대로 되는 반면, android는 가운데 정렬이 제대로 되지 않는다.

지금까지 React-Native에 대한 생각을 기술했다. 개발자들끼리 전부 리액트 네이티브에 대해 회의적으로 느낄 무렵, Flutter를 접해봄으로써 느낀점들을 다음편에 기술하겠다.