favicon

Jayden { do: smite }

(후기) 네이티브 앱 → 리액트 네이티브 앱 전환 그리고 1년 후

📚 네이티브 앱 → 리액트 네이티브 앱 전환 그리고 1년 후

이 글은 네이티브 앱 → 리액트 네이티브 앱 전환 그리고 1년 후 를 읽고 핵심적인 부분만 요약한 글입니다.

특이점

기존 iOS, 안드로이드 네이티브 => 리액트 네이티브로의 변경

  • 리액트 네이티브를 사용했을 때 테스트에서 효율적일 것 같아서
  • A/B 테스트를 진행한다고 가정했을 때, iOS/안드로이드로 나뉜 경우 테스트를 위한 코드를 두 번씩 작성해야됨
  • 반면 리액트 네이티브를 사용하는 경우, 한 번만 하면 된다.
추가적으로, 라프텔 사업부에서는 서비스에 앞으로 도입될 기능이 무수히 많을 것이라 예상하고 있습니다. 만약 라프텔이 현재 서비스 중인 앱이 완성이 됐다고 판단하고 단순히 유지 보수만을 계획하고 있다면 리액트 네이티브로의 전환은 불필요했을 것이고, 부적합했을 것입니다. 하지만, 앞으로 서비스를 더욱 고도화 시켜야 하고 새로 구현할 기능들이 많기 때문에 더 늦기 전에 빨리 리액트 네이티브로의 전환을 해야겠다고 생각했습니다.

저희는 리액트 네이티브를 사용하면 Android와 iOS 앱을 동시에 개발할 수 있기 때문에 더욱 높은 생산성을 보일 것이라 믿었습니다. 따라서 전환을 하는 작업은 리소스를 많이 소모하겠지만 중장기적으로 봤을 때 서비스 개선을 함에 있어서 훨씬 유리하기에 이는 시간 및 개발 인력을 투자하기에 충분한 가치가 있다고 판단하였습니다.

개발 조직 구조

안드로이드 1명, iOS 1명, 프론트엔드 1명, 백엔드 2명

업무 처리에 있어서도 한계가 있었습니다. 예를 들어서 특정 기능을 개발한다고 하면, 동시에 다른 기능 개발을 진행하기 힘들고 그저 할 일이 Queue처럼 쌓일 뿐이었습니다.

모바일 앱 개발자 채용이 어려웠던 것이 리액트 네이티브로의 전환에 어느 정도 영향이 있었습니다. 리액트 네이티브로 전환하면서 업무 방식도 변경하고자 했다. 기존 개발 시에는 새로운 기능 개발 시, 모든 개발자들이 회의에 소집되었다.

이후 2019 ~ 2020년 프론트엔드 5명, 백엔드 4명

새로운 기능을 개발하게 될 때, '디자이너 1명, 프론트엔드 1명, 백엔드 1명'로 스쿼드 구성 및 스쿼드 단위의 기능 개발과 회의 개발자들은 본인과 유관한 회의에만 참여하고 업무에 집중할 수 있는 환경 구성

사전 조사

기술을 도입해보기 전에 모든 기능을 리액트 네이티브로 구현이 가능한지 알아볼 필요가 있었다.

  • UI는 걱정이 없었지만, 우려했던 부분은 비디오 스트리밍 플레이어
  • 단순히 영상을 재생만 하는 게 아니라, DRM(저작권 보호 장치)도 적용해야했기에 리액트 네이티브 환경에서도 가능한지 알아볼 필요가 있었다.

프로젝트 새로 만들기

전환 방법

  1. 처음부터 새로 만들기
  2. 기존 네이티브 프로젝트에 리액트 네이티브 화면을 추가
  3. 새로 만든 리액트 네이티브 프로젝트에 기존의 네이티브 뷰 호환

1번의 방법을 선택 => 올...?

사실 기존 앱의 기능을 유지한 상태로 리액트 네이티브로 새로운 기능들을 구현한다면 2번의 방식이 적합했을 수도 있다. 하지만 안드로이드에 비해 iOS의 버전이 뒤쳐지고 있었음 즉, 안드로이드에는 있지만 iOS에는 없던 기능들이 있었고 이 둘의 디자인도 달랐다. 그렇기에 처음부터 공통된 리액트 네이티브 코드를 작성하는 게 좋겠다고 판다. 또한, 반드시 네이티브로만 구현되어야 하는 화면은 따로 없었다.

프로젝트 초기 환경

React Native CLI 통해 생성

  • 어차피 네이티브 코드 연동때문에 Expo 사용은 고려하지도 않음 리액트 네이티브 프로젝트에 기본적으로 정적 타입 시스템 'Flow'가 적용되어있지만, 커뮤니티가 너무 작아서 TS를 도입 화면 관리 라이브러리로는 React Navigation 사용
  • React Native Navigation도 후보였으나 해당 라이브러리는 기존 네이티브 앱에 리액트 네이티브를 적용하는 경우 더 적합해보였다.
  • 이를 사용할 경우 화면 하나 하나가 완전히 다른 별개의 리액트 앱으로 취급되는 부분이 마음에 들지 않아서 사용하지 않았다.

전역 상태 관리는 리덕스와 Context API를 사용

릴리즈까지

iOS를 릴리즈를 먼저 진행 iOS를 릴리즈했다고 안드로이드를 금방 릴리즈할 수 있는 것은 아니었다. 안드로이드에만 존재하는 기능, 추가적으로 개발해야하는 것들 그리고 스타일이 의도한대로 나오지 않는 등의 문제가 있었음

릴리즈 후

리액트 네이티브 릴리즈 후, QA 과정에서 잡아내지 못했던 버그들이 간혹 발견됨 리액트 네이티브에서는 CodePush라는 기술을 사용하여 앱스토어 검수 없이 바로 배포가 가능, 따라서 빠르게 서비스를 안정화시킬 수 있었다.

버저닝 규칙

리네(리액트 네이티브) 앱 업데이트 방법

  • CodePush
    • 네이티브 의존성이 변경되지 않았을 때에만 업데이트 가능하다.(만약 네이티브 코드가 업데이트된다면 반드시 스토어를 거쳐야함)
  • 스토어, 마켓을 통해서 업데이트

따라서 major.minor.patch 규칙을 조금 변경

  • major: 프로젝트 전체 리뉴얼 => 유지
  • minor: 새로운 피쳐 도입 => 네이티브 코드가 업데이트될 때
  • patch: 버그나 간단한 기능 수정 => JS 코드만 업데이트될 때

리네의 장점

  • JS를 사용하여 안드로이드, iOS 네이티브 UI를 작성할 수 있다.(플랫폼 통합)
  • 회사 내 다른 서비스의 JS 코드 공유(한정적이긴 했지만, 충분히 유용하고 효율적)
    • 웹과 리네 플젝 모두 리덕스를 사용하기에 리덕스 관련 코드를 쉽게 공유할 수 있을 것이라 생각했지만 개발 환경의 차이로 쉽지 않았다.
    • 웹과 모바일에서 다뤄야하는 상태가 달랐기 때문이다.
    • 또한, 웹과 모바일에서 사용하는 리덕스 미들웨어도 달랐다.
      • 웹: redux-saga, 모바일: redux-observable
      • saga의 경우, saga를 작성하는 과정에서 타입 유추가 안되기에 직접 타입을 설정해줘야하는 반면, observable은 타입 유추가 잘 되기에 모바일에서는 후자를 적용
      • 웹에서도 observable로 넘어갈까 했지만 추후 SSR을 구현할 때 어려움이 존재할 것 같아 전환할 수 없었다.

정말 간단한 로직부터 코드를 공유 시도(유틸 함수 위주)

  • 에피소드를 대여한 후 남은 대여 시간을 표기하는 함수
    • @laftel/common 프로젝트에 따로 배포하여 프로젝트에서 공유해서 사용
  • UI의 경우, 리네 컴포넌트와 HTML Element는 다르기 때문에 공유하여 재사용할 수 없었다.
    • 단, 여기서 사용되는 로직은 모두 공유할 수 있다!

서로 다른 플랫폼이지만 완전히 똑같은 기능이다 보니, 위 로직들은 하나의 Hook으로 만들어서 재사용을 할 수 있었습니다. 다만, 결제 시작의 경우 모바일과 웹은 방식이 조금 다릅니다. 모바일의 경우 새로운 화면을 띄워서 결제를 하고, 웹의 경우 모달을 띄워서 결제를 합니다. 결제를 시작하는 시점에 호출되어야 하는 함수는 서로 다르기에, **결제에 관련된 함수는 Hook의 파라미터로 콜백 형태로 설정하여 호출하도록 구현**했습니다.

callback 이미지

CodePush를 통한 배포
  • 원하는 기능의 배포를 아무 때나, 스토어를 거치지 않고 할 수 있다는 장점
  • 단, 네이티브 코드 변동이 있을 땐 사용 불가(매우 뜸하게 업데이트되기 때문에 큰 문제는 없었다.)
쉽고 빠른 UI 작성
  • JS와 리액트만 잘 알고 있다면 비교적 빠르게 작성 가능

단점

때로 일관성 없는 UI

iOS에서는 괜찮은데 안드로이드에서만 UI가 이상한 경우, 코드를 추가해야했다. 애니메이션 또한 양쪽에서 다 된다는 보장이 없음 이런 부분에서는 Flutter가 훨씬 더 안정적이라고 한다.

성능

일반적인 리액트보다 최적화에 대해서 신경을 많이 써야 렌더링 병목같은 게 사라지는 편이다. 네이티브로 구현할 때보다 더 많은 메모리를 잡아먹기도... 구형 기기에서 메모리 관리를 위해 GC가 너무 빠른 주기로 작동해서 CPU 사용률도 함께 올라가는 현상이 발생하기도 했다. 이런 부분에 있어서 더욱이 최적화를 통해서 따로 안정화시키는 작업을 해야했음

이슈에 대한 디버깅이 쉽지가 않음

아이폰 X에서만 발열이 심한 문제 발생 컴포넌트를 제거해봐도 동일한 문제가 발생했다. 리네에서 화면을 관리하는 react-navigation 라이브러리의 이슈였음 사이드바를 관리하는 DrawerNavigator에서 width가 정수가 아닌 소수라면 CPU 사용률이 높아지는 이슈 영상 플레이 부분에서만 화면이 가로로 전환되는데, 다른 화면 스택들도 가로에 맞게 width가 소수로 맞춰지면서 발생하는 이슈

너무 자주 발생하는 앱 구동 그리고 너무 오래 걸리는 빌드 타임
리액트의 철학과 맞지 않는 것 같은 써드파티 라이브러리들

리네의 써드파티 라이브러리들은 하위호환을 지키지 않는 패치가 잦다. 따라서 기존 작성한 코드를 라이브러리 업데이트 때문에 수정해야하는 일이 자주 발생 (아예 API 이름 자체가 바뀌어 버리는 경우가 비일비재)

또한 대부분의 써드파티 라이브러리들의 완성도가 떨어진다는 단점이 있다.

리네 사용 유의점

리액트만 할 줄 알아서는 서비스를 완성하기 쉽지 않다. 아주 간단한 소규모 앱은 가능하지만..! 써드파티 라이브러리에 의존하다보면 분명한 한계가 존재한다. 네이티브 언어(Kotlin, Swift)도 다를 줄 알아야 한다는 점

후기(리네 변경의 좋았던 점)

가장 좋았던 것은 프런트엔드 개발자들의 협업과 소통의 발전이였습니다. 이전에 Android, iOS, Web 개발이 따로 따로 이뤄질 때에는 서로 소통하는 것이 꽤나 적은 편이였습니다. 왜냐하면 서로의 개발 환경도 달랐고 만약 소통을 한다면 특정 로직 구현에 관한 노하우 공유 정도가 전부였는데, 이제는 프런트엔드 개발팀의 그룹원들이 모두 웹 개발자이자, 앱 개발자이기 때문에 더욱 많은 것을 함께 설계해나갈 수 있게 됐습니다. 같은 고민을 함께 하는 사람들이 늘어나니까 확실히 이전보다 더 좋은 솔루션에 도달하는 것이 느껴집니다. 앞으로 팀이 더 커질 것을 생각하면 너무 기대가 됩니다.

회고

최근 리액트 네이티브에 대한 도입에 대한 고민과 학습의지가 생겨서 정말 재미있게 읽었다. 무엇보다 보통 서비스의 규모가 커질수록 리액트 네이티브 => 네이티브로 마이그레이션하는 경우는 많이 봤는데, 이것과 거꾸로라서 더 흥미가 갔던 것 같다. 도대체 리네가 네이티브보다 좋은 점이 뭐가 있을까?! 하면서 재미있게 읽었다. 이 아티클은 2020년도 글이라, 지금은 또 얼마나 많은 변화가 있을지 직접 사용해보면서 느껴봐야할 것 같다. 과연 지금 리액트 네이티브를 도입해보는 게 좋은지 고민이 되긴 하지만, 확실히 그냥 웹에서 코드를 짜는 것보다 다른 관점의 경험들을 해볼 수 있겠다는 확신이 들어서 아마 도입해보지 않을까 싶다.

Copyright 2023. all rights reserved by Jayden