새로 들어가게된 팀에서, 기존 Flutter로 서비스중이던 앱을 Native로 전환하여 재배포하는 작업을 맡았는데, 드디어 5개월동안 진행했던 Flutter -> Native 전환 작업이 완료되었다.
이번 글에는, 해당 작업을 진행하며 있었던 이슈들과 느꼈던 점을 회고해보려 한다.
각각에 해당하는 깊은 내용까지 적으면 글이 너무 길어질것 같아, 상세 내용들은 별도의 글로 작성할 예정이다.
RevenueCat을 사용해서 상품 구독을 관리하고 있었는데, ApplicationId값이 다를 경우 RevenueCat에 연결 설정이 제대로 되지 않는다는 사실을 뒤늦게 알았다. ApplicationId에 suffix로 .dev가 붙도록 설정해둔 dev 환경으로 개발하고 있어서 많은 삽질의 원인이 되었다.
추가정보 : RevenueCat의 샌드박스 모드는 테스터 모드와 동일하다. Play Store와 연결되어있어, 콘솔에서 등록한 내부테스터 계정인 경우, 자동으로 샌드박스 모드로 실행된다.
서드파티 간편로그인을 지원했는데, Apple 로그인도 함께 지원되어, 안드로이드 구현시에도 Apple로그인 구현이 필요했음
구글, 페북, 카카오, 네이버는 소셜 로그인을 위한 네이티브 API가 있지만 애플 별도로 제공하지 않았다.
따라서 애플 로그인은 위 소셜 로그인과는 별도로 구현이 필요하여 아래 두가지 방법을 찾았다.

Firebase Authentication을 통한 애플 로그인 구현이 좀 더 쉽고 간편하지만, 우리 서버에서 직접 토큰관리를 해야한다는 요구조건이 있어, 토큰을 직접적으로 관리하지 못하는 Firebase Authentication는 선택하지 않았다.
남은 방법인 OAuth 2.0 동작을 직접 구현하기 위해, 앱 내에서 커스텀탭을 사용해 애플의 웹용 API를 호출, Redirect를 통해 토큰을 리턴받는 방식으로 로그인을 구현했다.
다만 https로 등록된 도메인이 없다면 애플 로그인을 할 수 없다는 주의사항이 존재한다.
로컬에 저장된 사용자 리플레이를 읽어와 표시해주는 기능이 있었는데, 품질팀으로부터 Loading이 너무 길다는 이슈가 올라왔음
파일 읽어오는 방식 개선 진행
기존에는 파일 내부에 있는 json 내부의 key값을 사용해 파일들을 순회하며 해당하는 리플레이를 읽어오도록 구현했는데, 이 방식은 모든 파일을 순회하며 json 파일을 읽어오며 sorting하기 때문에 저장된 리플레이가 많아질수록 동작 속도가 정배수로 증가했다.
앱 정책상, 사용자는 최대 100개의 리플레이를 저장할 수 있기 때문에 100개의 리플레이를 모두 채우면 3초정도의 딜레이가 발생했다.
위 구조를 개선하기 위해, 처음 폴더 정보를 읽어올때, 해당 폴더의 dirName을 정보에 저장해두고, 이후 해당하는 리플레이를 읽어올때는 저장된 dirName을 사용해 해당하는 폴더만 읽어오도록 개선하여 리플레이 개수에 크게 영향을 받지 않도록 하였다.
기존 시나리오 관리가 전혀 되고있지 않아, 시중에 나와있는 앱과 코드를 크로스체크하며 개발을 했다는 점이 가장 힘들었다.
Flutter 코드를 약간이나마 공부하게되는 계기가 되긴 했지만, 기존 코드가 외주 + 추가 개발 + 담당자의 잦은 교체로 인해 파악하기 굉장히 힘든 구조였다.
때문에, 초기에는 예상했던것보다 훨씬 더 많은 시간이 걸렸다.
시나리오 및 버전 관리의 중요성 재확인하는 계기가 되었다.
새로운 서비스를 만드는것이 아닌 이미 시장에 나와있는, 사용자가 존재하는 서비스를 전환하는것이기 때문에, 사용자 데이터를 반드시 유지시키고 호환성을 보장해야한다는 전제 조건에 많은 제약과 어려움이 있었다.
플루터 Local Storage에 저장되어있던 각종 데이터 + 이미지 + metaData.json를 Native 버전 업데이트 시에도 그대로 유지시키고, 해당 데이터 포맷에 맞춰 기존 동작에 오류가 없도록 구현하기 위해 설계할때 많은 시간이 소모되었다.
최종적으로는 저장되어있던 데이터들은 업데이트 된 Native앱 실행 시, Splash 화면에서 최초 1회 옮겨오는 작업을 통해 옮겨주고, 사용중인 데이터 형태는 그대로 유지하고, 새로 추가되는 데이터들도 동일한 형식으로 유지되도록 수정하였다.
앱 공통 Loading과 Error Handling 방식을 좀 더 개선하고 싶다.
현재 적용한 방식은, Loading과 Error 모두 StateHandler와 StateListener 두가지를 만들어, DI를 활용해 필요한곳에 주입해 해당 화면에서 컨트롤하도록 하는 구조이다.
주입된곳에서 viewLifecyclerOwner와 context를 StateListener에 넘겨주면, 해당 화면에서 Loading이나 Error를 Dialog를 표시하는식으로 동작한다.
동작 자체는 문제가 없지만, Loading이나 Error를 사용하는 화면마다 주입해주고, 다시 해당 화면서 viewLifecyclerOwner와 context를 넘겨 재설정을 해줘야 하는데, 화면이 많이 늘어나면 휴먼 에러가 발생할 수 있는 확률이 높다고 생각했다.
그렇다고 BaseXXXX에 선언해두고 사용하면, 불필요한 화면들도 사용하게 되어서 해당 방법은 지양했다.
관련해서 좀 더 자료를 찾고 실험을 해서 좀 더 안정적이고 편리하게 사용할 수 있는 방법을 찾고싶다.
API 데이터에 대한 Paging은 서버 개발자와 소통을 통해, Page Number나 Cursor값을 넘겨서 처리했었는데, 이번 프로젝트에 사용된 로컬 파일 형태로 저장된 데이터를 Paging 형태로 불러올 경우, Page Number나 Cursor값이 함께 존재하지 않기 때문에 어떻게 조회해야 Paging 방식으로 효율적으로 자원을 불러올 수 있을지에 대한 고민이 필요하다.
DB를 조회하는 방식처럼 Primary key값이 있는것도 아니고, 앱 내부 저장소에 저장된 데이터라 항상 원하는 형태로 정렬되어 저장된 상태인것도 보장할 수 없다. 당장 생각나는 방식은 파일명에 숫자를 오름차순으로 배치하여, 해당 숫자를 기반으로 페이징을 적용하는 방법정도가 떠오른다.
좀 더 나은 방법이 없나 조금더 생각 및 서칭해보고, 개선 완료 시 추가로 글을 남기도록 하겠다.
현재는 앱 내부저장소에 위치한 폴더들을 직접 읽어 가져오고 있는데, 좀 더 개선하여 해당 데이터를 Room DB 같은 로컬데이터 저장소를 활용하여 좀 더 쉽게 읽어오고, 캐싱을 적용할 수 있는 방법에 대해 좀 더 고민할 필요가 있다고 생각한다.
만약 Room DB에 데이터를 저장하고 불러올 수 있다면, 위에서 고민했던 Paging도 훨씬 간단하게 적용할 수 있다. 적용하는 방식과 효율적인지 여부는 좀 더 생각해볼 필요가 있다.
개발 1년 + 업데이트 8개월의 시간을 통해 만들어진 앱을 5달(실 개발기간 3.5달)동안 전환하며 얻은게 굉장히 많았다.
전환 시작 전, 사수가 그만두게되면서 촉박한 일정과 책임감에 많은 스트레스와 고민을 동반했지만, 결국 해냈고 그 과정에서 애매하게 알고있던 부분이나 정리가 덜 됐던 부분들까지 갈무리할 수 있는 좋은 기회가 되었다.
혼자 프로젝트 설정부터 개발, QA팀과 의사소통, 심사 및 배포까지 진행할 수 있는 기회여서, 개발뿐만 아니라 다른 개발자분들, 다른팀과 소통 등, 개발 외적인 부분들도 많이 성장시킬 수 있었다.
또한 새로 만드는 서비스가 아닌, 이미 실제 사용자들이 있는 서비스를 전환하며 신경쓰고 고려해야하는 포인트가 달라지는것도 굉장히 좋은 경험이였다.
많은것을 배웠지만 여전히 개선해야하는 점도 많고, 추가하고 싶은것들도 많아 더 많은걸 배우고 개선해나갈 수 있으면 좋겠다.
이상 글을 마치겠다.