"React Native is an open source framework for building Android and iOS applications using React and the app platform's native capabilities."
간단하게 말하면,
Facebook의 고민:
"The biggest mistake we made as a company was betting too much on HTML5 as opposed to native." — Mark Zuckerberg, 2012
Facebook은 처음에 모바일 앱을 HTML5 기반 하이브리드로 만들었음. 그런데 느리고 불안정함. 그래서 네이티브 앱으로 전환함.
하지만 네이티브 개발에도 문제가 있었음:
React Native의 탄생:
"In the essence of Facebook's hacker culture, React Native started as a hackathon project in the summer of 2013."
2013년 여름, Facebook 내부 해커톤에서 React Native 프로젝트가 시작됨. Jordan Walke를 포함한 개발자들이 “JavaScript로 네이티브 UI를 만들 수 있을까?” 라는 아이디어를 실험함.
주요 개발 타임라인:
| 시점 | 이벤트 |
| 2013년 여름 | Facebook 내부 해커톤에서 프로젝트 시작 |
| 2015년 1월 | React.js Conf에서 첫 공개 |
| 2015년 3월 | iOS 버전 오픈소스 공개 (GitHub) |
| 2015년 9월 | Android 버전 오픈소스 공개 |
| 2024년 10월 | New Architecture가 기본값이 됨 (v0.76) |
React Native의 목표:
웹 기술(JavaScript/React)의 높은 생산성과 네이티브 앱의 성능/UX를 둘 다 가져가는 것
React Native를 이해하려면 먼저 네이티브 UI 가 뭔지 알아야 함.
"In Android and iOS development, a view is the basic building block of UI: a small rectangular element on the screen which can be used to display text, images, or respond to user input. Even the smallest visual elements of an app, like a line of text or a button, are kinds of views. Some kinds of views can contain other views. It’s views all the way down!"
즉, View(뷰)란?
예를 들어, 카카오톡 채팅 화면을 보면
이렇게 View들이 겹겹이 쌓여서 하나의 화면을 구성함.
네이티브 UI란 각 운영체제(iOS, Android)가 자체적으로 제공하는 UI요소를 말함.
| 플랫폼 | 네이티브 UI 예시 | 개발 언어 |
| Android | android.view.ViewGroup android.widget.TextView | Kotlin, Java |
| iOS | UIView UILabel UIImageView | Swift, Objective-C |
| Web | <div> <p> <img> (DOM 요소) | HTML, CSS, JS |
왜 “네이티브”라고 부를까?
각 플랫폼의 운영체제가 직접 렌더링하기 때문!
모바일 앱을 만드는 방식은 크게 두 가지:


React Native는 웹 뷰가 아닌 네이티브 UI를 사용함.
"Because React Native components are backed by the same views as Android and iOS, React Native apps look, feel, and perform like any other apps. We call these platform-backed components Native Components."
React Native로 만든 앱은:
JavaScript로 코드를 작성하면(JavaScript로 로직 및 UI 선언)↓React Native가 네이티브 UI로 변환해준다(JS 객체 설계도를 각 OS가 이해하는 명령으로 번역)↓결과: 진짜 네이티브 앱!(iOS/Android가 진짜 자기 UI 요소를 그림)
React Native의 해결책:
네이티브 앱 개발의 가장 기본 단위는 사각형 형태의 View임.
React Native는 각 OS(iOS/Android)의 네이티브 뷰들을 JavaScript에서 다룰 수 있도록 용도별로 매핑하여 Core Components를 제공함.
⇒ 기본은 다 같은 View이지만, 용도에 따라 이름이 다른 전용 View를 꺼내 쓰는 것
아까 위에서 간단히 언급했던 카카오톡 페이지를 더 자세히 예를 들어 설명해보자면
| 화면 요소 | 역할 | React Native 컴포넌트 |
| 전체 화면 | 레이아웃을 잡는 가장 큰 상자 | <View> |
| 메시지 말풍선 | 글자나 이미지를 묶어주는 그룹 상자 | <View> |
| 말풍선 안의 글자 | 텍스트 표시 전용 상자 | <Text> |
| 프로필 사진 | 이미지 표시 전용 상자 | <Image> |
웹과의 중요한 차이점:
이렇게 용도에 맞는 전용 상자(Core Components)를 사용하면, React Native가 실행 시점에 각 플랫폼의 진짜 네이티브 UI 요소로 변환함!
"To work with React Native, you will need to have an understanding of JavaScript fundamentals." — React Native 공식 문서: Introduction
React Native를 하려면:
// React (웹)function Counter() {const [count, setCount] = useState(0);return (<button onClick={() => setCount
두 코드를 비교해보면, 구조가 거의 같다는 걸 알 수 있음!
그대로 가져갈 수 있는 것들:
| 구분 | React (웹) | React Native |
| 렌더링 대상 | DOM (브라우저) | 네이티브 UI (iOS/Android) |
| 기본 요소 | <div>, <span>, <p> | <View>, <Text> |
| 스타일링 | CSS 파일 | JavaScript 객체 |
| 이벤트 | onClick | onPress |
가장 큰 차이는 어디에 그리느냐!
Native Components:
Core Components:
React Native가 기본 제공하는 주요 컴포넌트들:
공식 문서의 표를 참고함 — Core Components and Native Components
| React Native | Web에서 비슷한 것 | Android View | iOS View | 설명 |
| <View> | <div> | ViewGroup | UIView | 레이아웃 컨테이너 |
| <Text> | <p> | TextView | UITextView | 텍스트 표시 |
| <Image> | <img> | ImageView |
React Native의 <View>는:
<View>→ Android에서는 android.view.ViewGroup으로 변환→ iOS에서는 UIView로 변환
React Native의 <Text>는:
<Text>→ Android에서는 TextView로 변환→ iOS에서는 UITextView로 변환
그래서 React Native 앱이 진짜 네이티브 앱처럼 보이고 동작하는 것!
"With React Native, you style your application using JavaScript. All of the core components accept a prop named style. The style names and values usually match how CSS works on the web, except names are written using camel casing."
import { StyleSheet, View, Text } from 'react-native';function App() {return (<View style={styles.container}>
| CSS | React Native |
| background-color | backgroundColor (카멜케이스) |
| font-size: 16px | fontSize: 16 (단위 없음) |
| class="container" | style={styles.container} |
| Flexbox 기본: row | Flexbox 기본: column |
왜 CSS를 안 쓸까?
네이티브 플랫폼에는 브라우저 CSS 엔진이 없기 때문
JS 객체로 정의해야 네이티브 스타일 시스템으로 변환 가능
React에서는 CSS 외에도 emotion, styled-components, Tailwind CSS 등을 많이 씀.
React Native에서는 어떻게 되나?
"Style and render React Native components using emotion"
Emotion은 React Native용 패키지 @emotion/native를 공식 제공함
import styled from '@emotion/native';const Container = styled.View`display: flex;justify-content: center;align-items: center;
특징:
"NativeWind allows you to use Tailwind CSS to style your components in React Native."
Tailwind CSS를 React Native에서 쓰려면 NativeWind 같은 라이브러리를 사용함
import { View, Text } from 'react-native';function App() {return (<View className="flex-1 items-center justify-center bg-white"><Text className="text-xl font-bold text-blue-500"
특징:
| 구분 | React (웹) | React Native |
| 기본 | CSS/SCSS, CSS Modules | StyleSheet.create + style prop |
| CSS-in-JS | @emotion/react, styled-components | @emotion/native, styled-components/native |
| Tailwind | Tailwind CSS + PostCSS | NativeWind (Tailwind → RN 스타일 변환) |
이 부분이 React Native를 이해하는 데 가장 중요한 부분!
작성한 JavaScript 코드가 어떻게 네이티브 UI로 바뀌는가?를 단계별로 살펴보자!!
React Native 앱은 두 개의 세계가 함께 동작함

JavaScript:
Native:
핵심 문제:
이 두 세계는 서로 다른 언어로 되어 있음.
JavaScript와 네이티브(Swift/Kotlin)가 서로 대화해야 한다!
"The render pipeline can be broken into three general phases:
- Render: React executes product logic which creates a React Element Trees in JavaScript.
- Commit: After a React Shadow Tree is fully created, the renderer triggers a commit.
- Mount: The React Shadow Tree is transformed into a Host View Tree."

이런 코드가 있다고 하자:
function MyComponent() {return (<View><Text>Hello, World</Text></View>);}
위 코드가 실행되면
Step 1: React 컴포넌트 실행
MyComponent 함수가 호출되고, 이 함수는 JSX를 반환함
Step 2: React Element Tree 생성
JSX가 JavaScript 객체로 변환됨. 이걸 React Element라고 함.
// 실제로는 이런 구조의 JavaScript 객체가 만들어진다{type: 'View',props: {children: {type: 'Text',props: {children: 'Hello, World'}
React Element는:
Step 3: React Shadow Tree 생성
React Element Tree를 기반으로 React Shadow Tree가 생성됨.
React Element Tree (JS) → React Shadow Tree (C++)<View> ViewShadowNode└── <Text> └── TextShadowNode└── "Hello" └── (text content)
React Shadow Node는:
왜 Shadow Tree가 필요할까?
화면에 뭘 그릴지는 이전 Render 단계에서 결정됨. 이제 어디에, 얼마나 크게 그릴지 정할 차례
<View style={{ flex: 1, padding: 20 }}><Text style={{ fontSize: 24 }}>Hello</Text></
계산 과정:
계산 결과:
ViewShadowNode:x: 0, y: 0width: 375, height: 812 (예: iPhone 화면 크기)TextShadowNode:x: 20, y: 20 (패딩 때문에 20px 안쪽)width: 80, height: 29 (텍스트 크기에 맞춰 계산됨)
이 계산을 담당하는 게 Yoga
React Native에서 flex, alignItems, justifyContent 같은 스타일을 쓸 수 있는 이유가 Yoga 덕분
레이아웃 계산이 끝나면, 이 Shadow Tree를 “다음에 화면에 그릴 Tree”로 확정
앱을 처음 실행할 때는 모든 UI를 새로 만듦.
하지만 상태가 변경될 때는 다름:
// 상태 변경 전<View><View style={{ backgroundColor: 'red' }} /><View style={{ backgroundColor: 'blue' }} />
React Native는 이전 트리와 새 트리를 비교해서:
이렇게 하면 전체를 다시 그릴 필요 없이 바뀐 부분만 수정할 수 있다!
마지막으로, Shadow Node가 실제 네이티브 뷰로 변환됨
React Shadow Tree → Host View Tree(네이티브 UI)ViewShadowNode → Android: ViewGroupiOS: UIViewTextShadowNode → Android: TextViewiOS: UITextView
이렇게 실제 화면에 뷰가 표시됨!
React Native 0.76부터 새로운 아키텍처가 기본값이 됐음.
React Native는 2015년에 처음 나왔음.
그 때 만든 구조로 거의 10년간 사용해옴.
하지만 시간이 지나면서 한계를 느낌.
기존에는 JavaScript와 네이티브가 비동기 브리지(Bridge)를 통해 통신:

문제점:
사용자: 버튼 클릭!↓JS: "버튼 눌렸어!" (메시지 보냄)↓ (기다림...)Native: (메시지 받음) → UI 업데이트↓ (기다림...)
“The New Architecture removes the asynchronous bridge between JavaScript and native and replaces it with JavaScript Interface (JSI).”
기존 Bridge 방식
JS ←── Bridge (비동기, JSON 변환) ──→ Native
새로운 JSI 방식
JS ←── JSI (동기/비동기, 직접 참조) ──→ Native
JSI가 뭘 바꿨나?
1. JSI (JavaScript Interface)
2. Fabric (새로운 렌더러)
3. TurboModules (새로운 네이티브 모듈 시스템)
| 항목 | 기존 (Bridge) | 신규 (JSI) |
| 통신 방식 | 비동기만 가능 | 동기/비동기 모두 |
| 데이터 전달 | JSON 직렬화 필요 | 메모리 직접 참조 |
| UI 반응성 | 느릴 수 있음 | 즉각 반응 가능 |
| 앱 시작 시간 | 모든 모듈 미리 로딩 | 필요한 것만 로딩 |
체감 효과:
React Native 앱을 개발하는 방법은 크게 두 가지:
"Expo is a production-grade React Native Framework."
# Expo로 시작하기npx create-expo-app@latest my-appcd my-appnpx expo start
# React Native CLI로 시작하기 (더 복잡한 설정 필요)npx @react-native-community/cli init MyAppcd MyAppnpx react-native run-ios # 또는 run-android
| 항목 | Expo | React Native CLI |
| 시작 난이도 | 쉬움 | 어려움 |
| 환경 설정 | 거의 없음 | Xcode/Android Studio 필요 |
| 네이티브 코드 수정 | 제한적 | 자유로움 |
| 기본 제공 기능 | 많음 (Expo SDK) | 적음 |
| 빌드/배포 | Expo 서비스 제공 | 직접 설정 |
| 적합한 경우 | 대부분의 앱, 빠른 개발 | 복잡한 네이티브 요구사항 |
"We believe that the best way to experience React Native is through a Framework, a toolbox with all the necessary APIs to let you build production ready apps."
"You can also use React Native without a Framework, however we've found that most developers benefit from using a React Native Framework like Expo."
왜 Expo를 권장할까?
Expo로 진행한다!
React Native 코드가 실제로 네이티브 UI로 변환되는 과정을 직접 눈으로 확인해보기!
# 1. 프로젝트 생성npx create-expo-app@latest counter-appcd counter-app# 2. 개발 서버 시작npx expo start# 3. 먼저 기본 코드를 제거하는 스크립트# app 디렉토리와 다른 디렉토리(components, constants, hooks-기본 코드 포함)에 있던 기존 파일들은 스크립트에 의해 app-example 디렉토리로 이동npm run reset-project
app/index.tsx (또는 App.js) 파일을 다음 코드로 교체:
import { useState } from 'react';import { View, Text, Pressable, StyleSheet } from 'react-native';export default function App()
{"extends": "expo/tsconfig.base.json","compilerOptions": {
실행 방법
터미널에서 w 키를 눌러 웹 브라우저에서 앱을 실행
개발자 도구로 확인

확인 결과
<!-- React Native 코드 --><View style={styles.container}><Text style={styles.title}>카운터</Text></View><!-- 웹에서 변환된 결과 -->
1. 웹에서는 react-native-web이 대신 실행
Expo에서 w 키를 눌러 웹으로 실행하면, 내부적으로 react-native-web이라는 라이브러리가 동작!
// 우리가 작성한 코드import { View } from 'react-native';// 웹에서 실제로 실행되는 코드 (Expo가 자동으로 바꿔줌)import { View } from 'react-native-web';
즉, 우리는 react-native에서 View를 가져온다고 썼지만, 웹 환경에서는 Expo(정확히는 bundler 설정)가 자동으로 react-native-web의 View로 바꿔서 실행하는 것.
2. react-native-web의 View는 div를 반환
react-native-web의 View 컴포넌트는 내부적으로 이렇게 구현되어 있음:
// react-native-web 라이브러리 내부 (단순화)function View(props) {return <div style={변환된스타일} {...props} />;}
그래서 <View>를 쓰면 결국 <div>가 렌더링되는 것
3. 왜 하필 div일까?
브라우저는 HTML만 이해함. 브라우저가 아는 건 <div>, <span>, <p> 같은 HTML 태그뿐.
iOS의 UIView나 Android의 ViewGroup 같은 건 브라우저가 전혀 모르기에 브라우저에서 실행할 때는 가장 비슷한 HTML 태그인 <div>로 바꿔주는 것.
| React Native | 웹에서 변환 | 이유 |
| <View> | <div> | 레이아웃 컨테이너 역할이 같음 |
| <Text> | <div> 또는 <span> | 텍스트를 감싸는 역할 |
| <Image> | <img> | 이미지 표시 역할이 같음 |
4. 스타일도 CSS로 변환
// React Native 스타일const styles = StyleSheet.create({container: {flex: 1,backgroundColor: '#f0f0f0',}});
↓ 웹에서는 이렇게 변환됨
<div class="r-flex-1 r-backgroundColor-f0f0f0" />
.r-flex-1 { flex: 1; }.r-backgroundColor-f0f0f0 { background-color: #f0f0f0; }
react-native-web이 JavaScript 스타일 객체를 CSS 클래스로 자동 변환해줌.
실행 방법
npx expo start 로 실행하면 QR이 뜸.
핸드폰에 expo go 어플을 깔아두고, QR을 스캔하면 앱에서 확인 가능
React Native Devtools 열기
확인 결과
요소를 클릭하면 다음과 같은 정보가 표시

1. React DevTools의 동작 원리
DevTools(JS Debugger)는 JavaScript 영역에 연결됨.
그래서 우리가 작성한 컴포넌트 이름(`View`, `Text`)이 그대로 보이는 것.
2. 아직 네이티브로 변환되기 전 단계
DevTools에서 보이는 건 React가 관리하는 컴포넌트 트리.
이건 네이티브 뷰로 변환되기 전 단계의 정보.
<View> ← DevTools에서 보이는 것↓RCTViewComponentView ← 실제 iOS에서 렌더링되는 것
React Native 컴포넌트가 실제로 iOS 네이티브 뷰로 변환되었는지 확인
사전 준비: Development Build 생성
Expo Go가 아닌 Development Build를 사용해야 Xcode에서 디버깅이 가능
# 1. expo-dev-client 설치npx expo install expo-dev-client# 2. iOS 빌드 생성 (Xcode 필요)npx expo run:ios
Xcode에서 View Hierarchy 확인하기
open ios/counterapp.xcworkspace
확인 결과: 네이티브 뷰 이름
Xcode의 View Hierarchy에서 다음과 같은 실제 iOS 네이티브 뷰 이름을 확인할 수 있음

| React Native 컴포넌트 | iOS 네이티브 뷰 (Old Arch) | iOS 네이티브 뷰 (New Arch / Fabric) |
| <View> | RCTView | RCTViewComponentView |
| <Text> | RCTTextView | RCTTextComponentView |
| <Image> | RCTImageView | RCTImageComponentView |
| <ScrollView> | RCTScrollView | RCTScrollViewComponentView |
1. Host View란?
React Native에서 Host View는 실제 플랫폼의 네이티브 뷰를 말함.
| 용어 | 의미 | 예시 |
| Host View | 실제 플랫폼이 제공하는 네이티브 뷰 | iOS의 UIView, Android의 View |
| Host Platform | 앱이 실행되는 플랫폼 | iOS, Android |
React Native 코드가 최종적으로 변환되는 목적지가 Host View라고 함.
3. RCTViewComponentView는 UIView를 상속받는다
UIView (iOS 기본 클래스)↑ 상속RCTViewComponentView (React Native 클래스)
상속받았기 때문에:
New Architecture (Fabric)에서의 변화
React Native 0.76+ (New Architecture)에서는 컴포넌트 이름이 조금 다름:
ComponentView 접미사가 붙은 클래스들은 Fabric 렌더러에서 사용되는 새로운 네이티브 뷰 구현!
5. 왜 Xcode에서 보면 "진짜 네이티브"라는 걸 알 수 있을까?
Xcode의 View Hierarchy Debugger는:
여기서 RCTViewComponentView가 보인다는 건:
만약 웹뷰 기반 하이브리드 앱이었다면, Xcode에서 WKWebView 하나만 보이고 그 안의 HTML 구조는 안 보였을 것!!
| UIImageView |
| 이미지 표시 |
| <ScrollView> | <div> | ScrollView | UIScrollView | 스크롤 컨테이너 |
| <TextInput> | <input> | EditText | UITextField | 텍스트 입력 |
JS와 Native는 서로 다른 언어다. 데이터를 주고받으려면:
JavaScript 객체↓ JSON.stringify()JSON 문자열↓ (Bridge로 전송)JSON 문자열↓ JSON.parse()Native 객체
macOS의 대소문자를 구분하지 않는 파일시스템(case-insensitive)에서 발생하는 잘못된 경고
// https://docs.expo.dev/guides/using-eslint/const { defineConfig } = require('eslint/config');const expoConfig = require('eslint-config-expo/flat');module.exports = defineConfig([expoConfig,{ignores: ['dist/*'],rules: {'import/no-unresolved': 'off',},},]);