
💡 읽기 전 필요한 지식 💡
- React와 React Native(RN)가 서로 다른 것이라는 정도의 구분
- SSR과 CSR이라는 말을 들어본 경험
- 앱이 웹뷰(앱 안에 띄우는 브라우저 창)로 웹을 띄울 수 있다는 사실
📖 읽고 나면 얻을 수 있는 것 📖
- "순수 RN"과 "RN 껍데기&웹뷰의 조합인 하이브리드"의 차이와 각각의 장단점을 이해하게 된다
- 웹뷰만으로 만든 앱이 왜 스토어 심사에서 거절될 수 있는지, 어떻게 하면 하이브리드 앱으로 인정받는지 알게 된다
- 모노레포·pnpm·Next.js App Router·Expo 같은 선택을 "왜" 했는지 근거로 설명할 수 있게 된다
자 이제 기획이 정리가 되었다. (기획 관련글은 아래 글을 참고하면 좋을 것 같다.)
[tolli #1] 무거운 성경암송 앱이 싫다
💡 읽기 전 필요한 지식 💡- 모바일 앱이 "기획 -> 개발 -> 출시"의 단계를 거친다는 정도의 큰 그림- 듀오링고 같은 학습 앱을 한 번이라도 써본 경험- "타겟 사용자"나 "문제 정의"라는 말이 어떤
bbin-guuuu.tistory.com
그 후에 가장 먼저 결정되어야 했던 건 "앱을 어떤 방식으로 만들 것인가"였다.
크게 두 갈래길이 있었다.
- 화면을 전부 RN의 전용 문법으로 직접 그리는 순수 RN 방식
- 화면은 웹으로 만들고 RN은 그 웹을 띄우는 껍데기로만 쓰는 방식
고민 끝에 RN을 껍데기로 사용하고 리모트 웹뷰의 방식을 채택했다.
큰 그림은 이렇게 정리되었다.

위 구조는 앱과 앱의 역할을 나눈 것이다.
화면과 학습 플로우는 Next.js로 만들어 Vercel에 올리고,
RN은 그 주소를 WebView로 불러오는 껍데기 역할만 맡는 것이다.
당근마켓도 같은 이유로 로컬에서 리모트 웹뷰방식으로 전환한 가정을 확인할 수 있었다.
당근마켓에 웹 프로젝트 배포하기 #2 — 웹 서버로 돌아가기
당근마켓은 기존의 지속하기 어려운 로컬 웹뷰 방식을 탈피하고, 다시 웹 서버를 도입하고 있어요. 이 과정에서 있던 여러 새로운 고민들과 해결방법들을 공유해요
medium.com
그렇다면 왜 순수 RN이 아니었을까?
순수 RN을 선택하지 않은 이유는 현실적인 제약 때문이다.
순수 RN은 View, Text와 같은 RN "전용 컴포넌트"를 써야 하고,
이로 인해 Next.js나 React와 같은 웹 프론트엔드 스택과 개발 환경 자체가 달라지고, 그것을 필자는 이미 경험했었다.
RN과 웹을 동시에 깊게 다루는 것만큼 비효율적인 방법이 없었다고 생각한다. (제약조건을 따져보면 개발인원이 부족하다...)
그래서 핵심 역량을 웹 프론트엔드에 집중하고, RN은 껍데기로만 활용하는 방향을 선택하기로 했다.
또 하나 큰 이점이 있었다.
플로우를 수정하고 Vercel에 배포하면 사용자는 앱을 업데이트하지 않아도 바뀐 플로우를 바로 경험할 수 있다는 것!
즉, 스토어 심사를 기다릴 필요 없이 즉각적인 실험이 가능하다고 생각했다.
우리 톨리의 "1분 안에 가볍게"란 방향처럼 플로우를 빠르게 고쳐가며 개선할 수 있다는 점이 개인적으로 너무 좋았다!
그냥 웹뷰 앱은 스토어심사가 어렵다...
하지만 여기에는 함정이 있긴하다.
웹뷰만으로 구성된 앱은 구글 플레이스토어와 애플 앱스토어 모두에서 심사 거절 위험이 높다.
실제로 이로 인한 거절 사례가 정말 많은 것을 볼 수 있다.
뿐만 아니라, 두 스토어 모두 "단순히 URL만 연결하거나 앱의 콘텐츠가 없는 앱은 승인하지 않는다"는 정책을 명시하고 있다.
단순히 웹을 껴주기만 하는 앱은 앱으로서의 기능이 없다고 본다는 뜻이지 않을까?라는 생각을 해본다.
그래서 필자는 학습플로우 자체는 웹으로 구현하되,
다음 네이티브 기능은 RN에서 직접 구현해 심사 기준을 충족하기로 했다.
첫 번째 는 마이크 접근이다.
먼저 이전글에서도 명시했지만 이번 예시는 STT로 진행해보려고한다. (실제로는 이 기능이 빠졌다.)

녹음과 STT는 웹 API만으로는 안정적으로 처리하기 어렵다고 보았다.
두 번째는 푸시 알림이다.
습관 형성을 위해 필수라고 생각했다.
세 번째는 JS 브릿지다.
녹음 결과나 푸시 수신 여부 같은 데이터를 웹과 RN 사이에서 주고받기 위해 필수라고 보았다.
이 세 가지를 RN이 직접 다루면, 단순 웹뷰가 아닌 하이브리드 앱으로 분류될 것이라 예상해보았다.
근데 JS 브릿지가 뭐야?!!!
💡 JS 브릿지란?
WebView 안의 웹과 바깥의 React Native는 기본적으로 서로 모른다.
JS 브릿지는 이 둘을 연결하는 통로다.
아래 사진으로 보면 바로 이해가 될 것이다.


즉 플로우는 아래와 같이 실행될 수 있는 것이다.
- 웹뷰에서 "녹음 시작해줘"라고 RN에 요청을 한다.
- 여기서 JS Bridge가 이 요청을 RN에 전달
- RN에서 요청을 받고 마이크를 실행시키며 STT를 실행한다.
- 결과는 "텍스트"로 나올 것이고 이것을 전송한다.
- 여기서 JS Bridge가 이 결과를 웹뷰에 전달한다.
- 웹뷰는 이 결과를 받아 텍스트 화면에 표시한다.
즉 메세지 전달 통로 그 자체가 되는 것이다!
근데 왜 React안쓰고 Next.js써?
웹을 어떤 프레임워크로 만들지도 너무 고민이었다.
순수 React, 즉 CSR로 만들면 JS 번들을 다 받고 난 뒤에야 화면이 그려진다.
그러면 웹뷰로 진입할 때 흰 화면이 잠깐 뜨는 문제가 생기고, 네트워크 상황에 따라 로딩이 느려질 수도 있지 않을까? 라고 판단했다.
실제로 당근 테크밋업에서도 동일한 이유로 CSR에서 SSR로 전환한 사례가 있다고 했던게 기억이 났다.
Next.js의 SSR을 쓰면 서버에서 HTML을 미리 만들어 내려주기 때문에 첫 화면이 훨씬 빨리 뜨다고 생각했다.
"1분 안에 가볍게"라는 방향성을 가진 앱이니 몇 초 차이라도 화면이 빨리 렌더링되는 게 중요하다고 느꼈던 것 같다.
MVP만 보면 Next.js가 오버엔지니어링처럼 보일 수 있다.
하지만 나중에 성능개선을 위해 React에서 Next로의 마이그레이션을 하는 것보다 Next.js를 초기에 도입하는게 비용을 크게 줄일 수 있지 않을까?라는 생각을 했다.
다만 SSR 방식은 페이지를 이동할 때마다 새로 그리는 MPA의 문제가 있다는 것은 인지하고 있었다.
학습 플로우는 스텝이 자주 전환되는데, 매 스텝마다 페이지를 이동하면 그 느낌이 끊길 수도 있다.
그래서 스텝 전환을 "페이지 이동이 아닌" "컴포넌트의 상태 전환"으로 구현하면 문제가 해결된다고 보았다.
실제로 필자가 iOS 개발을 했을 때도 스텝 페이지를 구현할 때 페이지를 나누지 않고 중간 가운데에 길게 스텝 화면들을 배치해서 한 페이지 내에서 해결되게 구현한 적이 있었다.
예를 들어보겠다.
/onboarding -> SSG (온보딩, 안 바뀜)
/study -> use client (상태 기반 스텝 전환)
위 라우팅 전략은 화면별로 렌더링 방식을 다르게 가져간 것이라고 볼 수 있다.
안 바뀌는 온보딩은 미리 만들어두는 SSG로,
스텝이 계속 바뀌는 학습 화면은 클라이언트에서 상태로 전환하도록 use client 지시어로 둠으로서
온보딩의 경우, 한 화면 안에서 상태만 바꿔 스텝을 넘기면, SSR의 빠른 첫 화면과 SPA의 끊김 없는 전환을 동시에 가져갈 수 있다고 생각했다.
실제로 이 전략을 사용하여 톨리의 온보딩 로딩 속도를 빠르게 개선시켰다.
모노레포, pnpm, App Router, Expo 선정 이유
프로젝트 구조는 웹과 네이티브를 하나의 레포에서 관리하는 모노레포로 잡았다.
러프하게 구조를 보여주자면 아래와 같다.
tolli_FE/
├── apps/
│ ├── web/ # Next.js
│ └── native/ # Expo RN
├── package.json
├── pnpm-workspace.yaml
└── tsconfig.json
모노레포
위 구조는 web과 native를 하나의 레포 안 apps 폴더로 묶은 것이다.
둘을 별도 레포로 분리하면 JS 브리지 타입 같은 공유 코드를 관리하기 번거롭다고 생각했다.
같은 레포에서 관리하면 나중에 공유 패키지를 추가하기도 쉽고, 웹과 네이티브의 변경사항을 한 커밋에서 추적할 수 있다고 보았다!
패키지 매니저 (pnpm)
패키지 매니저로는 pnpm으로 골랐다.
npm은 모노레포 환경에서 web과 native 각각의 node_modules에 같은 패키지를 중복 설치한다는 "단점"이 있다.
반면 pnpm은 전역 저장소에 한 번만 저장하고 링크로 연결해 디스크 사용량과 설치 속도가 유리하다고 한다.
무엇보다도 filter옵션을 써서 web 또는 native중 하나에만 설치가 가능하다는 장점도 있다.
따라서 필자는 개발 경험(DX)를 위해 pnpm을 선택하게 됬다.
App Router
App Router는 폴더 기반 라우팅이라 프로젝트 구조가 직관적이고,
모든 컴포넌트가 기본적으로 서버 컴포넌트로 취급되서 서버 컴포넌트의 이점을 온전히 살릴 수 있다고 생각했다.
이전에 썼던 글을 참고하면 좋을 것 같다.
[Next.js] App Router vs Pages Router
Pages Router 개념Pages Router는 Next.js의 전통적인 라우팅 시스템이다./pages 디렉토리 내의 파일 구조를 기반으로 라우트가 자동으로 생성되는 방식으로 동작한다.(직관적이고 간단하게 라우팅을 구현
bbin-guuuu.tistory.com
Expo
솔직히 이 앱은 고도화된 네이티브 기능이 필요하지 않았다.
Expo는 RN을 한 번 더 래핑해서 마이크나 푸시 알림같은 네이티브 기능을 훨씬 쉽게 쓸 수 있게 해주고,
관련 라이브러리도 너무 많아서 좋게 보았다.
그럼으로 네이티브 설정 관련 비용을 줄이고, 개발 속도를 높일 수 있지 않을까?
이 부분도 이전 글을 참고하면 좋을 것 같다.
[React Native] Expo로 React Native 생성하기 + 이유
최근 진행하고 있는 공모전에서 앱서비스를 개발하게 되었다.스택을 선정하는 과정중 동료 개발자가 웹개발자였고 한정적인 시간 내에서 JavaScript와 React 지식을 그대로 활용할 수 있는,상대적
bbin-guuuu.tistory.com
아키텍처를 정하면서 느낀 것
아키텍처를 정하는 과정은 결국 "내 상황에서 무엇을 포기하고 무엇을 취할것인가"를 고르는 일이었던 것 같다.
마치 문제정의 프로세스에서의 제약조건은 안고가고 현실적으로 극복 가능한 장애물이 무엇인가를 고민하는 것처럼 말이다.
2인 개발이라는 제약 속에서 순수 RN의 완성도를 포기하는 대신 웹 역량을 살리는 하이브리드를 택했고,
심사 거절 리스크는 네이티브 기능을 직접 구현해 메꾸는 식으로 풀어냈다.
완벽한 정답이라기보다는, 현재의 제약과 앱의 방향성에 가장 맞는 선택을 내리는 과정이었다고 느꼈다.
다음 글에서는 이 아키텍처 위에서 핵심인 학습플로우를 어떻게 설계했는지, 그리고 그 구조를 switch 뼈대로 분리해 다른 개발자와 어떻게 협업했는지를 다뤄보려 한다.
[tolli #3] switch로 쪼갠 화면이 협업 단위가 됐다
💡 읽기 전 필요한 지식 💡- 컴포넌트라는 개념에 대한 가벼운 이해- switch문이 값에 따라 분기하는 문법이라는 정도의 지식- 여러 명이 하나의 레포에서 함께 개발한다는 상황에 대한 감📖 읽
bbin-guuuu.tistory.com
'프로젝트' 카테고리의 다른 글
| [tolli #3] switch로 쪼갠 화면이 협업 단위가 됐다 (0) | 2026.06.22 |
|---|---|
| [tolli #1] 무거운 성경암송 앱이 싫다 (0) | 2026.06.16 |