React Native CLI 앱을 Expo 기반으로 리뉴얼한 후기

운영 중인 React Native CLI 앱의 코드와 빌드 환경 문제를 분석해 Expo 기반 전면 재구축을 제안했고, 재출시와 Android 확장까지 이어갔다. 팀을 설득한 과정과 회고를 담았다.

배경

  1. 25년 9월, 한창 개발에 재미를 느낄 시점에 지인 소개로 숙명여대 창업팀 포에이에 프론트엔드 앱 개발자로 합류했다. (나는 남자다.)
  2. forA는 ADHD인들을 위한 커뮤니티 앱 서비스이다. 내가 들어갔을 당시에는 병원 지도 및 영수증 리뷰, 커뮤니티, 약 정보 등 다양한 기능이 있었다.
  3. 실제로 사용하는 사람들도 있었다. 아직 커뮤니티가 활발하게 운영되는 단계는 아니었지만, 꾸준히 찾아오는 사용자들이 있었고 대표는 카카오톡방을 통해 사용자 반응을 확인하고 있었다.
  4. 사용자가 많지는 않았지만 나름 단골 유저가 있는 서비스였다.

문제 상황

  1. 문제는 더이상 서비스 성장이 멈추었다는 점이다.
  2. 인스타그램, 쓰레드 등 SNS으로 계속 홍보를 하고 있었지만, 사용자 유입이 적었다.
  3. 사용자 소통방을 통해 알아낸 점은, 병원 지도와 영수증 리뷰 기능은 거의 사용자가 없었다는 것이다.
  4. 기존 기능만 유지해서는 사용자가 다시 방문할 이유를 만들기 어렵다고 판단했다.
  5. 또한 회원가입 플로우가 7단계로 무척 길었다는 점에서 UX 불만이 자주 나왔다.
  6. 심각한 점은, 카메라 기능과 사진 업로드 등 주요 기능이 사실상 동작하지 않는 상태였다.
  7. 나는 이 문제가 갑자기 발생한 것이 아니라, 이전부터 숨기고 있거나 쌓여온 문제라고 생각했다.
  8. 왜냐하면 현재 프로젝트가 소위 말하는 스파게티 코드였기 때문이다. 인수인계 할 때도 사수는 이 점을 강조했다.

1️⃣ 스파게티 코드

  1. 기존 프로젝트를 살펴보면서 가장 먼저 느낀 점은 코드 작성 기준이 통일되어 있지 않다는 것이었다.
  2. 기능마다 구조와 구현 방식이 달라, 한 부분을 이해하더라도 다른 기능에서는 다시 구조를 파악해야 했다.
  3. 예를 들어 전역 상태 관리에 Zustand와 MobX가 함께 사용되고 있었다.
  4. 두 라이브러리를 같이 쓰는 것 자체가 문제는 아니다. 다만 내가 파악한 서비스의 상태 관리 요구사항에 비해 도구가 중복되어 있었고, 어떤 기준으로 나누어 사용했는지도 코드만으로는 알기 어려웠다. 유지보수하는 입장에서는 두 가지 방식을 모두 이해해야 하는 부담이 생겼다.
  5. API 요청을 관리하는 방식도 일관되지 않았다. apis/ 폴더에 모아둔 코드가 있는 반면, 그렇지 않은 코드도 있었다.
  6. 이런 차이가 기능별로 나타나는 것을 보며, 여러 개발자가 각자 익숙한 방식으로 기능을 구현한 것으로 보였다.
  7. 나는 요청 로직을 확인하거나 수정할 때도 먼저 해당 기능이 어떤 방식을 따르는지부터 찾아야 했다.
  8. 가장 심각한 점으로, 구글 API 키가 별도 설정으로 분리되지 않고 코드에 직접 들어가 있는 부분도 있었다.

2️⃣ React Native Cli 환경 문제

  1. 코드 구조와 별개로, 네이티브 빌드 환경을 관리해야 한다는 부담도 있었다.
  2. React Native CLI 기반 프로젝트에서는 JavaScript 코드뿐 아니라 iOS와 Android 프로젝트의 설정과 의존성도 함께 관리해야 한다.
  3. 특히 네이티브 기능을 사용하는 라이브러리는 패키지를 설치하는 것으로 작업이 끝나지 않을 수 있다.
  4. iOS에서는 CocoaPods와 Xcode 설정을, Android에서는 Gradle과 JDK, Android SDK 등의 호환성을 함께 살펴야 한다.
  5. JavaScript 패키지 설치가 성공했다고 해서 양쪽 앱의 빌드까지 보장되는 것은 아니다.
  6. 흔히 말하는 ‘의존성이 꼬이는 문제’도 여기서 생긴다.
  7. 추가하려는 라이브러리가 기존 프로젝트보다 높은 네이티브 SDK 버전을 요구하거나, 여러 라이브러리가 공유하는 네이티브 의존성의 버전 조건이 맞지 않을 수 있다.
  8. 기능 하나를 넣으려다 관련 패키지와 빌드 설정까지 함께 조정해야 하는 상황이 생긴다.
  9. React Native 자체를 업데이트할 때도 package.json의 버전만 바꾸면 끝나는 것은 아니다.
  10. 버전에 따라 iOS와 Android 프로젝트 파일의 변경 사항을 직접 반영해야 한다.
  11. 네이티브 의존성과 직접 관리하는 설정이 늘어날수록 호환성을 확인하고 오류의 원인을 좁히는 일이 복잡해졌다.
  12. 이 프로젝트는 여러 사람이 네이티브 코드를 수정해온 상태였기 때문에, 기존 설정을 이해하는 일부터 필요했다.

3️⃣ 깃 커밋 기록 삭제

  1. 가장 큰 문제점은 변경 이력이 거의 남아 있지 않다는 점이었다.
  2. 인수인계 과정에서 들은 바로는, 내가 합류하기 약 한 달 전에 대표와 불화가 있던 팀원이 래포를 전부 삭제하는 일이 있었다.
  3. 이후 복구는 했지만, 남아있는 커밋은 3개뿐이었다. 🫢
  4. 왜 이런 구조를 선택했는지, 특정 설정이 어떤 오류를 해결하기 위해 추가되었는지는 현재 코드만으로 알아내기 어려웠다.
  5. 특히 앞서 본 라이브러리 중복이나 네이티브 설정을 정리하려 할 때 이 문제가 컸다.
  6. 어떤 의존성을 위한 코드인지, 무슨 의도가 있는 코드인지 파악하기 어려웠다.
  7. 오류가 발생했을 때 이전 버전과 비교하거나, 어떤 변경 이후 문제가 생겼는지 추적하는 방법도 활용하기 어려웠다.
  8. 현재 상태에서 코드를 읽고 직접 실행해보며 원인을 좁혀야 했고, 수정이 다른 기능에 영향을 주지 않는지도 확인해야 했다.
  9. 일관되지 않은 코드 구조와 누적된 네이티브 설정에 변경 이력의 부재까지 겹치면서, 기존 프로젝트를 이해하고 안전하게 수정하는 데 드는 시간을 가늠하기 어려웠다.

이런 상황은 신규 기능 개발과 서비스 개선을 어렵게 만들고 있었다.

해결 방안 찾기

  1. 기능을 추가하는 과정에서 빌드 오류가 반복해서 발생했다.
  2. 당시 나는 기존 코드와 네이티브 설정을 이해하는 것부터 어려웠고, 변경 이력도 거의 없어 원인을 추적하는 데 한계가 있었다.
  3. 앞으로 알림과 사용자 행동 분석(GA)처럼 외부 라이브러리나 네이티브 설정이 필요한 기능도 추가해야 했다.
  4. 프론트엔드를 혼자 맡은 상황에서, 기능을 개발할 때마다 빌드 문제에 얼마나 시간을 쓰게 될지 알 수 없다는 점이 부담스러웠다.
  5. 지금 필요한 것은 네이티브 설정을 직접 관리하는 범위를 줄이고, 필요한 설정과 변경 사항을 일정한 방식으로 남길 수 있는 환경이었다.
  6. 앞으로의 개발 환경을 어떻게 관리할지도 고민하기 시작했다.
  7. 그 과정에서 React Native 기반 프레임워크인 Expo를 알게 되었다. 특히 관심이 갔던 것은 네이티브 프로젝트를 관리하는 방식인 CNG였다.

Expo라는 프레임워크

  1. Expo는 React Native 앱 개발에 필요한 라이브러리와 도구를 제공하는 프레임워크다.
  2. 내가 주목한 부분은 iOS와 Android 프로젝트의 설정을 관리하는 작업을 자동화할 수 있다는 점이었다.
  3. 기존 프로젝트(CLI 방식)에서는 ios/, android/ 안의 파일을 직접 수정하고, 그 결과를 계속 유지해왔다.
  4. 이 방식에서는 현재 설정뿐 아니라 그동안 어떤 수정이 쌓였는지도 이해해야 했다. 커밋 기록이 거의 없었던 우리 프로젝트에서 특히 어려웠던 부분이다.
  5. Expo의 CNG(Continuous Native Generation)는 앱 설정과 필요한 변경 사항을 바탕으로 네이티브 프로젝트를 생성하는 방식이다.
  6. 네이티브 파일에 수정을 계속 쌓아가는 대신, 프로젝트를 어떻게 생성할지를 관리한다. (나는 이 부분이 Dockerfile 비슷하다고 생각했다.)
  7. Expo에서는 app.json이나 app.config.ts에 앱 설정을 작성하고, 추가적인 네이티브 설정 변경은 Config Plugin으로 정의할 수 있다.
  8. Prebuild는 이를 바탕으로 ios/, android/ 프로젝트를 생성한다.
  9. Config Plugin은 네이티브 파일에 필요한 변경을 적용하는 코드다. 직접 파일을 열어 수정했던 내용을 플러그인으로 옮기면, 프로젝트를 새로 생성할 때도 해당 변경을 적용할 수 있다.
  10. 이 방식을 유지하려면 생성된 파일에만 수정 사항을 남기지 않고, 필요한 변경을 앱 설정이나 플러그인에 반영해야 한다.
  11. 이 점이 당시의 문제를 해결할 수 있었다. 앞으로 추가하는 네이티브 설정을 앱 설정과 플러그인으로 모아 관리하면, 무엇을 바꿨는지 확인할 기준이 생긴
  12. 여기에 변경 이유를 커밋으로 남긴다면, 나중에 같은 문제를 마주해도 지금보다는 원인을 좁히기 쉬울 것이라고 생각했다.
  13. 물론 Expo를 사용한다고 의존성 충돌이나 빌드 오류가 사라지는 것은 아니다. 외부 라이브러리의 호환성을 확인하고 필요한 네이티브 설정을 이해하는 일은 여전히 남는다. (실제로 Firebase를 연동하는 과정에서 깊게 들어가야 하는 부분이 있었다.)
  14. 다만 직접 관리해야 하는 범위를 줄일 수 있다는 점에서, 혼자 개발을 이어가야 하는 상황에 적합한 선택지라고 보았다.
  15. 이렇게 Expo를 이후 개발 환경의 후보로 두게 되었다. 다음으로 판단해야 할 것은 기존 프로젝트에 점진적으로 도입할지, 서비스 개편에 맞춰 새로 구축할지였다.

서비스의 방향도 다시 고민했다

  1. 개발 환경과 함께 서비스에서 무엇을 바꿔야 할지도 함께 고민했다.
  2. 기존 사용자들이 어떤 불편을 겪는지, 어떤 기능을 이용하는지부터 돌아봤다.
  3. 사용자 오픈 채팅 소통방의 반응으로는 병원 지도와 영수증 리뷰를 이용하는 사람이 많지 않았다.
  4. 모든 기능을 계속 유지하기보다, 이용이 적은 기능을 정리하고 사용자가 찾아올 만한 기능에 집중할 필요가 있다고 생각했다.
  5. 또한 앞으로도 소통방에서 들은 의견만으로 전체 사용자의 이용 방식을 판단하기는 어렵다고 생각했다.
  6. 이후에는 어떤 화면과 기능을 이용하고 어디에서 이탈하는지 확인할 수 있도록, 사용자 행동을 분석하는 환경도 필요했다. 개선한 기능이 실제로 사용되는지 판단할 근거를 만들고 싶었다.

매거진 기능 인앱화

  1. 커뮤니티에 글을 쓰거나 댓글을 남기지 않는 사용자도 앱에서 이용할 수 있는 콘텐츠가 있으면 좋겠다고 생각했다. 그때 눈에 들어온 것이 포에이에서 이미 발행하고 있던 매거진이었다.
  2. 포에이는 매주 여러 작가와 협업해 ADHD 관련 매거진을 만들고 있었다. 다만 매거진은 앱이 아닌 노션으로 배포하고 있었다.
  3. 이미 꾸준히 만들어지는 콘텐츠가 있었지만, 앱 안에서는 이를 볼 수 없었다.
  4. 매거진을 앱에서도 제공하면 커뮤니티 활동 외에 앱을 방문할 이유가 하나 더 생길 것이라고 생각했다.
  5. 자연스럽게 앱 체류 시간도 늘릴 수 있다고 생각했다.
  6. 그래서 매거진 인앱화를 제안했다. 실제 방문과 재방문으로 이어지는지는 출시 이후 확인해야 했지만, 이미 운영 중인 콘텐츠를 활용한다는 점에서 시도해볼 만하다고 판단했다.

커뮤니티 UI 개선

  1. 커뮤니티는 앱의 중심 기능인 만큼, 들어왔을 때 사용자들의 글이 먼저 눈에 들어오면 좋겠다고 생각했다.
  2. 기존 화면은 상단 배너가 차지하는 공간이 커서 게시글을 한눈에 살펴보기 어려웠다.
  3. 그래서 배너의 비중을 줄이고, 게시글 내용을 목록에서 미리 볼 수 있는 피드 형태를 제안했다. (인스타처럼)
  4. 카테고리도 함께 변경했다. 기존의 나이대 중심 구분으로는 사용자가 어떤 주제로 이야기하고 싶은지 충분히 담기 어렵다고 느꼈다.
  5. 연령뿐 아니라 이야기의 주제와 성격에 따라 글을 찾고 올릴 수 있도록 카테고리를 개편하는 방향을 제안했다.
  6. 커뮤니티를 처음 이용하는 사람도 어디에 어떤 글을 쓰면 좋을지 알기 쉬운 구조를 만들고 싶었다.

리뉴얼이라는 결론

  1. 필요한 작업을 모아보니 변경 범위가 작지 않았다.
  2. 동작하지 않는 기존 기능을 고치는 것에 더해, 이용이 적은 기능을 정리하고 매거진을 추가하며 커뮤니티 화면도 개편해야 했다.
  3. 이 작업을 기존 프로젝트에서 이어갈지, Expo 기반으로 새로 구축할지 고민했다.
  4. 기존 코드를 유지하면 동작하는 기능을 활용할 수 있고, 변경을 나누어 적용할 수 있다는 장점이 있었다.
  5. 하지만 내가 마주한 프로젝트에서는 작은 기능을 추가하는 과정에서도 빌드 오류가 반복되고 있었다.
  6. 코드와 네이티브 설정을 이해하는 데 시간이 필요했고, 변경 이력도 부족해 수정에 얼마나 걸릴지 예상하기 어려웠다.
  7. 새로 만드는 쪽도 부담은 있었다. 기존에 동작하던 기능까지 다시 구현하고 검증해야 했고, 익숙하지 않은 Expo를 배우는 시간도 필요했다.
  8. 대표가 제시한 목표 시점은 2026년 3월이었다. 합류 시점에서 약 6개월이 남아 있었고, 프론트엔드는 나 혼자 담당하고 있었다. 이 기간 안에 무엇을 완성할 수 있을지 판단해야 했다.
  9. 나는 기존 구조를 파악하고 수정하는 데 드는 불확실성보다, 필요한 기능의 범위를 정해 내가 이해할 수 있는 구조로 다시 구현하는 편이 당시에는 일정을 계획하기 수월하다고 판단했다.
  10. 서비스 개편으로 기존 화면과 기능을 상당 부분 변경해야 한다는 점도 이 판단에 영향을 주었다.
  11. 그렇게 Expo를 기반으로 앱을 전면 재구축하는 방향을 제안하기로 했다. 다음 과제는 이 선택에 필요한 작업과 부담을 팀에 설명하고, 함께 진행할 수 있을지 논의하는 것이었다.

팀원에게 제안하는 태도가 중요하다.

  1. 재구축이 필요하다고 판단한 뒤에는, 이 생각을 팀원들에게 어떻게 전달할지 고민했다.
  2. 내가 납득한 선택이라고 해서 팀 전체에도 당연히 좋은 선택인 것은 아니었다.
  3. 특히 이번 제안에는 개발 환경 변경뿐 아니라 기능 정리와 새로운 콘텐츠, UI 개편까지 포함되어 있었다.
  4. 프론트엔드 개발자인 내가 제안했지만, 실행하려면 기획과 디자인, 백엔드 등 다른 영역에서도 함께 작업해야 했다.
  5. 내가 개발하기 편해진다는 이유만으로 팀원들에게 큰 변경을 요청할 수는 없었다.
  6. 왜 지금 리뉴얼이 필요한지, 서비스에 어떤 도움이 될지, 그 과정에서 어떤 부담이 생길지를 함께 설명해야 한다고 생각했다.
  7. 다른 분야의 의견을 듣는 것도 중요했다. 개발하면서 발견한 문제와 개선 아이디어는 제안할 수 있지만, 각 영역에서 무엇이 필요하고 가능한지는 담당자들과 논의해야 했다. 내 판단만으로 전체 작업의 난이도나 우선순위를 정할 수는 없었다.
  8. 기존 프로젝트의 문제를 이야기하는 방식도 조심스러웠다. 최대한 지금 겪고 있는 어려움과 앞으로 바꿔야 할 부분을 중심으로 이야기하려 했다.
  9. 이런 점들을 고민하며 대표와 팀원들에게 현재 프로젝트에서 겪는 어려움, 리뉴얼이 필요하다고 판단한 이유, 그리고 바꾸고 싶은 서비스의 방향을 설명했다.
  10. 단순히 새로 만들자는 제안보다, 무엇을 위해 어떤 변화를 시도하려는지 전달하고 싶었다.
  11. 다행히 대표와 팀원들도 변화의 필요성에 공감해주었다. 그렇게 팀의 동의를 얻어 2025년 10월, 리뉴얼을 시작했다.

개발 중 겪은 문제

  1. 당시 나는 개발을 시작한 지 약 6개월이 지난 상태였다. React Native를 다룬 경험도 많지 않았고, Expo로 앱을 개발하고 배포하는 과정도 익숙하지 않았다.
  2. 인프런 강의와 공식 문서를 보며 공부했고, 이해하기 어려운 부분은 프론트엔드 오픈카톡방에 질문하며 도움을 구했다.
  3. 25년도 하반기는 포에이밖에 생각이 나지 않을 정도로 개발에 몰두했다. 수업 시간은 물론 학교가 끝난 후에도 카페에서 앱 개발만 했다.
  4. 이 기간 동안 배운 점이 무척 많다. 하지만 리뉴얼 과정에서 Expo를 익히고 여러 개발 문제를 해결했던 경험은 별도 글에서 다루려고 한다. 분량이 너무 많다.
  5. 여기서는 재구축 이후 서비스가 어떻게 달라졌는지 정리해보겠다.

결과 및 성과

  • 리뉴얼을 시작한 지 4개월 만에 앱을 재출시했다. 기존 기능을 정리하고 커뮤니티 화면을 개편했으며, 이후 안드로이드 출시와 추가 기능 구현도 이어갔다.

1️⃣ 커뮤니티 중심으로 UI 개편

  1. 커뮤니티에 들어왔을 때 사용자들의 글이 먼저 보이도록 화면을 개편했다.
  2. 상단 배너의 비중을 줄이고, 게시글을 중심으로 살펴볼 수 있는 구조로 바꾸었다.
  3. 게시글 목록에는 피드 형식을 적용했다. 사용자가 글을 하나씩 열어보지 않아도 내용을 미리 살펴보고 관심 있는 게시글을 선택할 수 있도록 했다.
  4. 나이대 중심이었던 카테고리도 이야기의 주제와 성격에 따라 구분하도록 개편했다. 사용자가 관심 있는 이야기를 찾고, 자신의 글을 올릴 곳을 선택하기 쉽게 만들고자 했다.

2️⃣ 안드로이드 출시와 기능 확장

  1. 기존 환경에서 진행하지 못했던 안드로이드 출시를 완료했다. Expo 기반으로 빌드 환경을 정리한 뒤, 2월 25일 안드로이드 앱을 출시할 수 있었다.

  2. 푸시 알림은 expo-notifications를 활용해 구현했다. 기존에 설정의 복잡함 때문에 구현하지 못했던 기능을 실제 서비스에 추가했다.

  3. 구글 AdMob 광고도 구현해 수익화를 시도할 수 있는 기반을 마련했다.

  4. 안드로이드 출시 이후에는 새로운 사용자 유입도 관찰할 수 있었다.

  5. 아직 큰 폭의 성장이나 수익과 성과는 없었다. 하지만 이번 리뉴얼의 의의는 막혀 있던 기능 개발을 다시 진행하고, 더 많은 사용자가 서비스를 이용할 수 있는 기반을 마련했다는 데 있다고 생각한다.

후기

다음 사람이 이해할 수 있는 코드

  1. 이번 리뉴얼을 통해 가장 크게 느낀 것은 유지보수할 수 있는 코드의 중요성이었다.
  2. 이전의 해커톤과 사이드 프로젝트에서는 주로 빈 프로젝트에서 개발을 시작했다.
  3. 이미 운영 중인 서비스를 넘겨받아 다른 사람이 작성한 코드를 이해하는 경험은 달랐다.
  4. 기능이 동작하는지뿐 아니라, 어디를 수정해야 하고 그 수정이 어디까지 영향을 미치는지 파악할 수 있어야 했다.
  5. 코드 작성 기준과 변경 이력이 부족하면 유지 보수하는 데 많은 시간이 드는지 직접 경험했다.
  6. 내가 작성한 코드도 언젠가는 다른 사람이 이어받을 것이다. 그때 같은 어려움을 겪지 않도록 구조와 작성 기준을 일관되게 유지하는 행위가 무척 중요하다는 사실을 깨달았다.

사용자가 다시 찾아올 이유

  1. 서비스를 운영하는 일이 생각보다 어렵다는 것도 느꼈다.
  2. 대표님은 본인이 ADHD를 겪으며 느꼈던 어려움을 바탕으로 포에이를 만들었다. 사용자에게 어떤 도움이 필요한지 잘 알고 있었고, 서비스에 대한 애정도 컸다.
  3. 그런데 필요한 기능을 만들고 홍보한다고 해서 사람들이 계속 앱을 사용하는 것은 아니었다. “사용자가 왜 이 앱에 들어와야 할까?”라는 근본적인 문제를 해결하는게 중요했다. 이번 경험으로 깨달았다.
  4. 매거진을 앱으로 가져오자고 제안한 것도 이 때문이었다. 글이나 댓글을 쓰지 않더라도 읽을거리가 있다면 앱에 들어올 이유가 생기지 않을까 생각했다.
  5. 예전에는 개발자로서 주어진 기능을 완성하면 내 역할은 끝이라고 생각했다.
  6. 하지만 실제 사용자가 있는 서비스를 개발해보니, 출시한 뒤 사람들이 어떻게 쓰는지 보고 불편한 부분을 고치는 행위가 개발에도 영향을 미친다는 사실을 깨달았다. 더 나은 개발을 위해서 필요한 작업이었다.

댓글