참고: Do it! 리액트 프로그래밍
요즘 리액트 네이티브에 빠져 있어서 리액트 네이티브로 앱을 만들어보고 있습니다. 모바일에서 보여지는 UI를 보면서 신기하기도 하고, 내가 표현할 수 있는 범위가 확장함에 따라서 재미가 많이 컸던 것 같습니다. 기본적으로 셋팅하면서 스트레스를 많이 받았지만 공기계도 꺼내보고 이것저것 코드를 치면서 핸드폰에서 나오는 결과물이 신기하여서 너무 좋았습니다.
그러나 앱을 만들고 싶어서 시작한거라 리액트 네이티브가 어떻게 동작하는지 알아보지 않고 사용하였습니다. 그러다보니 이게 어떻게 동작하는지 알아보고자 글을 정리하게 되었습니다.
React Native
모바일 앱 개발에서 ‘네이티브’라는 단어는 ‘운영체제를 만들 때 사용하는 프로그래밍 언어와 똑같은 언어로 만든’이라는 의미입니다. 네이티브 앱은 빠른 실행 속도가 장점이지만 습득해야 할 지식이 많고 똑같은 기능의 앱을 안드로이드용과 iOS용으로 따로 만들어야 해서 많은 시간과 비용이 든다는 단점이 있습니다.
이러한 단점을 극복하고자 크로스플랫폼이라는 개념이 등장했고, 하나의 소스 코드로 여러 운영체제에서 동작하는 앱을 개발이 가능해졌습니다. 네이티브 앱보다 실행 속도는 조금 느리지만 개발 시간과 비용을 크게 절역할 수 있다는 장점이 있습니다.
페이스북은 2015년 리액트 개발 방식으로 모바일 앱을 만들 수 있는 리액트 네이티브 프레임워크를 발표하였고, 웹 개발자가 네이티브 언어를 배우지 않고서도 크로스 플랫폼 모바일 앱을 만들 수 있습니다.
리액트 네이티브 프레임워크는 리액트 프레임워크에 기반을 둔 기술입니다. 그렇기 리액트 네이티브의 동작 원리를 이해하려면 먼저 리액트의 동작 원리를 알아야 합니다.
DOM과 렌더링
DHTML 방식은 Javascript가 객체 지향 언어의 상속 관계로 설계한 DOM(Document Object Model) 타입 자바스크립트 객체를 생성하는 방식으로 동작합니다. 웹 브라우저는 HTML 문서를 만나면 Parsing 과정을 거쳐 자바스크립트 객체 구조를 만듭니다.
웹 브라우저는 HTML 형태로 보여주지면 JS 코드 관점에서는 <div>
는 HTMLDivElement
클래스의 인스턴스이고 DOM 클래스의 인스턴스를 DOM 객체라 하며, DOM 객체는 트리 구조를 가지고 있습니다. 웹 브라우저가 HTML을 파싱하여 자바스크립트 DOM 구조로 만드는 것을 렌더링이라 합니다.
물리 DOM과 가상 DOM
- 물리 DOM 구조: 웹 브라우저에서 JS 코드가 생성하는 실제 DOM 구조
- 가상 DOM 구조: 리액트 코드가 생성한 자바스크립트 객체 구조
- 리액트는 특정 시점에서 가상 DOM 구조를 물리 DOM 구조로 만드는데 이를 ‘리액트가 렌더링한다.’라고 부른다. 이 기능을 수행하는 패키지를 렌더러(renderer)라 합니다.
리액트 프레임워크와 리액트 네이티브 프레임워크의 차이
- 리액트는 가상 DOM 구조를
react-dom
이란 렌더러 패키지를 사용하여 물리 DOM 구조로 렌더링하는 방식으로 동작하는 프레임워크입니다.react-dom
렌더러를 DOM 렌더러라 한다.
- 리액트 네이티브는
react-native
라는 렌더러 패키지를 사용하여 렌더링하는 방식으로 동작하는 프레임워크입니다.react-native
렌더러를 네이티브 렌더러라 한다.
react 패키지의 역할
리액트와 리액트 네이티브 프레임워크는 모두 react
라는 패키지를 사용합니다.
react
패키지는 앞서 본App.tsx
파일을 가상 DOM 구조로 만드는 역할을 하는 패키지입니다.- 네이티브 렌더러는 리액트 요소를 안드로이드 프레임워크나 iOS용 UIKit 프레임워크의 화면 UI 객체로 바꿔주는 역할을 합니다.
App 컴포넌트 ← react
패키지
네이티브 렌더러 ← react-native
패키지
안드로이드 프레임워크(안드로이드)/UIKit 프레임워크(아이폰)
브리지 방식 렌더링
모든 것이 JS로 동작하는 리액트에서는 React.render
라는 DOM 렌더러의 동작을 코드로 확인할 수 있지만 리액트 네이티브에서는 네이티브 렌더러의 모습을 확인할 수 없다. 그 이유는 리액트 네이티브 프로젝트의 Android와 iOS 디렉터리에 있는 Java나 Object-C로 구현한 네이티브 모듈 쪽에서 렌더링이 진행되기 때문이다.
Native 모듈 쪽에는 JavaScriptCore
라는 이름의 자바스크립트 엔진이 동작한다. 이 C++ 언어로 구현된 JavaScriptCore
엔진은 안드로이드에서는 JNI(Java Native Interface) 방식으로, iOS에서는 Object-C의 FFI(Foreign Function Interface) 방식으로 연결되어 동작합니다.
리액트 네이티브 앱을 Mobile에 설치하고 실행하면
- 리액트 네이티브의 네이티브 모듈이 실행되면서 네이티브를 담당하는 UI 스레드
App.tsx
와 같은 자바스크립트 코드를 실행하는 JS 엔진 스레드
이렇게 2개가 동시에 동작합니다.
이 두 스레드는 메시지 큐 방식으로 서로 렌더링과 관련된 데이터를 주고 받습니다. 사용자가 화면에 이벤트를 주면 UI 스레드는 자바스크립트 쪽 스레드에 이벤트 형태로 넘깁니다. 이런 방식으로 동작하는 프레임워크를 브리지(bridge) 방식 프레임워크라 합니다.
리액트 네이티브 앱처럼 2개 이상의 스레드에서 동작하는 앱을 다중 스레드(multi-thread) 앱이라 합니다.
리액트 네이티브 전용 패키지에는 항상 JS 스레드에서 동작하는 쪽과 UI 스레드에서 동작하는 쪽이 따로 존재합니다. 이 때문에 자바스크립트 쪽만 설치하고 네이티브, 즉 UI 스레드에서 동작하는 쪽을 설치하지 않으면 패키지가 정상 동작하지 않기 때문에 네이티브에서 동작하는 부분을 설치하는 (pod install
과 같은) 명령어를 반드시 실행시켜야 합니다.
React.createElement API가 하는 일
리액트와 리액트 네이티브에서 React.createElement API
는 가장 저수준 기능으로서 가상 DOM 객체를 생성합니다. 리액트 네이티브 렌더러는 네이티브에서 동작하므로 가상 DOM 객체를 네이티브로 넘겨주는 방식으로 동작합니다.
export default function App() {
const textElement = React.createElement(Text, null, 'Hello world');
return textElement;
}
react-native-cli
는 네이티브 모듈에서 동작하는 JS 엔진이 가상 DOM 객체를 넘겨주는 App의 존재를 알 수 있도록 다음 내용의 index.js
파일을 만듭니다.
import {AppRegistry} from 'react-native'
import App from './App'
import {name as appName} from './app.json'
AppRegistry.registerComponent(appName, () => App)
네이티브 모듈에서 동작하는 자바스크립트 엔진이 이 파일의 존재를 알 수 있도록 다음과 같은 코드를 가진 Java 파일도 만듭니다.
// android/app/arc/main/java/com/win/MainApplication.java
// ...생략...
@Override
protected String getJSMainModuleName() {
return "index"
};
// ...생략...
이제 리액트 네이티브 앱을 폰에서 실행하면 MainApplication.java
가 실행되고 JS 스레드에서 실행되는 자바스크립트 엔진이 getJSMainModuleName
을 통해 index.js
파일의 존재를 알게 됩니다. 이 index.js
파일을 통해 App
의 존재를 알고 App
을 호출하여 얻은 가상 DOM 객체를 브리지를 통해 네이티브로 넘겨 마침내 ‘Hello world!’를 출력합니다.
리액트 네이티브는 Text
와 같은 코어 컴퍼넌트(core component)와 Platform, Alert
와 같은 API가 있습니다. Text
와 같은 코어 컴포넌트는 화면에 어떤 내용을 렌더링해야 할 때 사용하고, API는 폰의 하드웨어나 운영체제가 제공하는 기능이 필요할 때 사용합니다.
요가(Yoga) 엔진
요가 엔진은 페이스북이 컴포넌트의 배치와 스타일링을 위해 C++ 언어로 구현한 라이브러리입니다.
리액트 네이티브는 웹 브라우저에서 단순히 자바스크립트 엔진만 떼어낸 것으로 HTML과 CSS 엔진이 존재하지 않습니다. 리액트 네이티브는 이 때문에 요가(Yoga)란 이름의 CSS 엔진을 직접 만들었습니다.
요가 엔진은 웹 브라우저의 CSS 엔진과 비슷하게 동작하지만 완전히 똑같지는 않습니다. 특히나 flexbox layout 부분에 차이가 있습니다. HTML과 비슷하게 모든 리액트 네이티브 코어 컴포넌트에는 style이란 이름의 속성이 있습니다. 컴포넌트의 style 속성에 적절한 값을 설정하면 네이티브 모듈 쪽에서 동작하는 요가 엔진이 이를 해석하여 안드로이드 프레임워크나 iOS의 UIKit 프레임워크가 요구하는 스타일링을 수행합니다.
리액트 네이티브의 동작을 간략하게 정리해보았습니다. 특별하진 않지만 리액트 네이티브에 대해서 조금은 알게 되어 동작하는 방식을 어느정도 이해할 수 있었습니다. 앞으로도 리액트 네이티브에 대해서 더 배우고 서비스도 배포할 예정인데 더 공부하고 공식문서도 조금 찾아보면서 다듬어 나가야겠다는 생각이 들었습니다.
'Front-End' 카테고리의 다른 글
React-Native - The Basics (1) | 2024.03.17 |
---|---|
React State 관리: 10년간의 교훈 (0) | 2024.03.03 |
Next.js Project Structure (1) | 2024.02.03 |
2024년 상태 관리의 종결 : zustand (0) | 2024.01.19 |
Virtualize List with react-window (2) | 2023.12.17 |