react-native cli로 앱을 생성하면 아래와 같은 index.js 파일이 생성됩니다.
import {AppRegistry} from 'react-native';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
AppRegistry.registerComponent는 입력받은 컴포넌트를 모듈 내부의 Runnables 객체에 등록합니다.
Runnables 객체에는 여러 개의 Runnable을 등록할 수 있지만, react-native cli로 만들어진 앱은 무조건 (app.json에 정의된 앱 이름)mainComponentName 앱만 로드하도록 되어 있습니다.
public void onCreate(Bundle savedInstanceState) {
mReactDelegate = new ReactDelegate(/* ... */);
if (mainComponentName != null) {
loadApp(mainComponentName); // app.js에 등록한 앱 이름
}
}
protected void loadApp(String appKey) {
mReactDelegate.loadApp(appKey); // ReactRootView 객체 생성, ReactContext 생성
getPlainActivity().setContentView(mReactDelegate.getReactRootView());
}
그리고 단 하나의 ReactRootView 객체만 관리(mReasctRootView)하며, 해당 객체를 MainActivity의 컨텐츠로 설정하고, JS에선 해당 ReactRootView의 태그를 얻어와서 여기에 Runnables에 등록한 단 하나의 컴포넌트를 그립니다.
react-native cli로 만든 프로젝트에서 MainActivity와 MainApplication이 ReactActivity와 ReactApplication을 상속하는한 선택권은 없습니다.
wix/react-native-navigation는 이러한 제한을 풀었습니다.
설치 과정을 보면, MainActivity, MainApplication에서 라이브러리에서 제공하는 NavigationActivity와 NavigationApplication를 상속하도록 변경하여 내부 구조를 완전히 바꿔버렸습니다.
https://wix.github.io/react-native-navigation/docs/installing#2-update-mainactivityjava
Runnables에 다수의 컴포넌트를 등록해 놓고,
// 스크린 등록
Navigation.registerComponent('Home', () => Home);
Navigation.registerComponent('Settings', () => Settings);
Navigation.registerComponent는 내부적으로 AppRegistry.registerComponent를 사용합니다.
스크린 이동시 라이브러리에서 제공하는 ReactView(ReactRootView 상속)를 생성하고, 해당 뷰에 Runnables 객체에서 컴포넌트를 추출하여 그립니다.
// 스크린 이동
Navigation.push(props.componentId, {
component: {
name: 'Settings',
},
});
즉, 하나의 스크린이 하나의 앱이 되는 것입니다.
이와 같은 방법으로 기존 네이티브 프로젝트에 리액트 네이티브를 통합하는 것도 가능합니다.
리액트 컴포넌트를 Runnables에 등록해 놓고,
https://reactnative.dev/docs/0.75/integration-with-existing-apps#2-add-your-react-native-code (마지막 라인)
CatalystInatnace를 통해 자바스크립트에 접근(참고: 리액트 네이티브에서 JS와 Java가 통신하는 방법)해서 Runnables에서 컴포넌트를 얻어와 View에 그리고, 해당 View를 화면으로 쓰면 되는 것입니다.
https://reactnative.dev/docs/0.75/integration-with-existing-apps#the-magic-reactrootview (mReactRootView.startReactApplication 부분)
위의 방법은 제가 코드를 분석할 때 최신 버전인 0.75.0이고, 0.76.0에선 더 간단하게 앱을 불러 올 수 있습니다.
https://reactnative.dev/docs/integration-with-existing-apps
리액트JS로 표현하면 다음과 비슷합니다.
<!-- index.html -->
<body>
<div id="home"></div>
<div id="settings"></div>
<script src="index.js"></script>
</body>
// index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
const Home = () => {
return <h1>첫 번째 React 앱</h1>;
};
const Settings = () => {
return <h1>두 번째 React 앱</h1>;
};
const root1 = ReactDOM.createRoot(document.getElementById("home"));
const root2 = ReactDOM.createRoot(document.getElementById("settings"));
root1.render(<Home />);
root2.render(<Settings />);
각 스크린은 하나의 리액트 루트 노드가 할당된 별개의 리액트 앱이기 때문에, 몇 가지 귀찮은 설정이 필요할 수 있습니다.
- Context
https://wix.github.io/react-native-navigation/docs/third-party-react-context - Bottom Tab 사용시 Bottom Sheet을 탭바 아래서 나타나도록하려면 특정 라이브러리를 사용해야 합니다.
https://github.com/CursedWizard/react-native-navigation-bottom-sheet - Bottom Tab 커스텀: 액션 버튼등을 추가하려면 네이티브 코드를 수정해야 합니다.
등등..
제대로 사용해 본 적은 없지만, 정말 빠를 것으로 예상됩니다.
하나의 앱으로 개발하다보면 처음엔 빨랐는데, 트리 깊이가 점점 깊어지고, 앱의 규모가 커질 수록, 새로운 스크린을 만들 때 뭐 별로 추가하지도 않았는데, 이상하게 느린 것 같은 느낌이 듭니다.
원인을 찾기도 점점 힘들어집니다.
하지만 코드만 보면 wix/react-native-navigation는 스크린을 추가할 때마다, 처음 리액트 네이티브 앱을 만들고 실행시킬 때 느끼는 빠릿빠릿함을 느낄 수 있을 것으로 예상됩니다.
'리액트 네이티브' 카테고리의 다른 글
esbuild로 리액트 네이티브에서 HMR을 구현해 보자. (0) | 2025.01.13 |
---|---|
리액트 네이티브 Fabric 랜더러는 항상 빠를까? (0) | 2024.10.29 |
flash-list로 복잡한 레이아웃 구현하기 (0) | 2024.10.16 |
리액트 네이티브에서 IntersectionObserver를 대체하는 방법 (0) | 2024.10.16 |
리액트 네이티브 WebView 중첩 (0) | 2024.10.03 |