React Native CLI를 중심으로 모바일 앱 개발에 필요한 핵심 개념과 실무 팁을 정리한 초안입니다.
- Facebook(현 Meta)이 만든 크로스플랫폼 프레임워크인 React Native를 순정(베어) 환경에서 사용하는 명령줄 도구입니다.
- iOS/Android의 네이티브 프로젝트 구조를 그대로 생성하고, Xcode/Android Studio와 긴밀하게 연동됩니다.
- Expo와 달리 네이티브 코드 수준의 커스터마이징(플러그인, 네이티브 모듈, 빌드 설정 변경 등)에 제약이 적습니다.
react-native log-android
react-native log-ios
brew install node # npm, npx 포함
brew install watchman # 파일 시스템 감시 도구(file watcher), 파일이 변경되었는지 실시간으로 감지해주는 툴
brew install ruby # pod install 명령이 ruby 스크립트로 실행
brew install ruby-build # 루비 버전을 설치해주는 도구로 rbenv와 함께 사용됨
brew install rbenv # 루비 버전 관리를 위함
- iOS 설정 확인:
- Xcode 설치, Command Line Tools 설정
- AND 설정 확인:
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/emulator
export PATH=$PATH:$ANDROID_HOME/platform-tools
- 프로젝트 생성:
npx react-native init FirstProject
- ios pod 설치: 의존설 관리 도구
cd ios && pod install
- 실행
npm run ios
- Expo: 빠른 시작, OTA 업데이트, 풍부한 SDK, 관리형/베어 워크플로우 제공. 단, 일부 네이티브 커스터마이징은 제한될 수 있음(관리형 기준).
- React Native CLI: 초기 설정은 더 수고롭지만, 네이티브 레벨 변경에 유연. 대규모/커스텀 네이티브 요구사항에 유리.
- 팀/프로젝트 요구사항에 따라 선택하거나, Expo Bare로 전환/혼합 전략도 가능.
- AND: gradle
- build.gradle
- iOS: cocoapods
- Podfile
- Flipper
- Layout Inspector
- React DevTools
- Images
- Database
- React-Native-Debugger
- 등 여러가지 Debugging Tool이 있고 각자의 스타일에 따라 사용
- AndroidManifest.xml: 앱의 권한, 액티비티, 서비스, 딥링크, 최소 SDK 등 메타데이터 선언 파일.
- Activity: 화면 단위의 컴포넌트. RN는 기본 Activity 위에 ReactRootView를 올려 RN UI를 렌더링합니다.
- Application: 앱 전역 초기화 지점, 네이티브 모듈 초기화, 디버그 설정 등을 배치.
- AppDelegate: 앱 생명주기 진입점, 푸시/딥링크 등 핸들링.
- Info.plist: 번들 메타데이터(권한 설명, URL Scheme, 설정 키 등)를 선언하는 파일.
- Build Phase: 빌드 시 실행되는 단계별 작업들로, 소스코드를 앱으로 변환하는 과정을 자동화하는 스크립트 모음
- AND: Manifest에 권한 선언 + 런타임 권한 요청 필요 (위험 권한의 경우). 사용자가 거부해도 앱은 반복해서 권한 요청 가능. 단, "다시 묻지 않음"을 선택하면 시스템 다이얼로그는 나타나지 않고, 설정으로 유도해야 함.
- iOS: Info.plist에 권한 설명 키 (NSCameraUsageDescription 등) 필수. 최초 1회 권한 요청만 시스템이 다이얼로그로 처리. 사용자가 거부하면, 이후 재요청해도 시스템 다이얼로그는 다시 뜨지 않음.
- RN에서는 react-native-permissions 등의 라이브러리를 통해 두 플랫폼의 권한 요청 흐름을 통합할 수 있음
- URL Scheme/Universal Links(App Links)를 통해 외부에서 앱을 열거나 특정 화면으로 동.
- iOS: Info.plist URL Types 설정, Android: Manifest intent-filter 설정 필요.
- scheme 테스트:
npx uri-scheme open "scheme://path" --ios
npx uri-scheme open "scheme://path" --android
- JavaScript 스레드와 네이티브(UI/메인) 스레드가 브리지 또는 JSI를 통해 상호작용합니다.
- JS에서 네이티브 기능(카메라, 센서 등)을 호출하고, 네이티브는 결과를 JS로 전달합니다.
- MainThread: 네이티브 UI를 그려주고, 사용자 입력을 처리하는 역할
- Javascript Thread: 작성한 JS 코드를 실행하고 로직을 처리하는 역할
- Native Module Thread: JS에서 호출된 네이티브 모듈을 처리하는 역할
- Shadow Thread: 레이아웃 계산 (Yoga 엔진 사용), 실제 레이아웃 정보가 계산되면 Main Thread에 전달되어 UI가 그려짐
sequenceDiagram
participant User
participant OS
participant MainThread as Main Thread<br/>(UI Thread)
participant JSThread as JavaScript Thread
participant ShadowThread as Shadow Thread
participant YogaEngine as Yoga Layout Engine
User->>OS: 앱 아이콘 터치
OS->>MainThread: 앱 프로세스 시작
MainThread->>MainThread: 초기 화면 준비
MainThread->>JSThread: JavaScript 번들 로드<br/>(index.js, App.js)
Note over JSThread: React 컴포넌트 로직 실행
JSThread->>JSThread: React Element Tree 생성
JSThread->>JSThread: Diffing 작업 수행
JSThread->>ShadowThread: 변경된 트리 구조 전달
ShadowThread->>YogaEngine: 레이아웃 계산 요청
YogaEngine->>ShadowThread: Shadow Node Tree 생성
ShadowThread->>MainThread: 레이아웃 정보 전달
MainThread->>MainThread: 네이티브 UI 컴포넌트<br/>생성/업데이트
MainThread->>User: 화면에 UI 표시
- JS에서 접근할 수 있도록 네이티브 기능을 래핑한 모듈입니다.
- iOS(Objective-C/Swift), Android(Java/Kotlin)로 구현하며, JS/TS로 공개 API를 제공합니다.
- Fabric(UI 레이어)와 TurboModules(네이티브 모듈) + JSI 기반으로 브리지 의존도를 낮추고 성능/타입 안전성 개선을 지향합니다.
- Codegen을 통해 네이티브-JS 인터페이스를 자동 생성하여 일관성 및 유지보수성을 높입니다.
- [AS-IS:
[JS Thread]
|
(JSON)
|
[Bridge] ← 느림 / 비동기 / 병목
|
(JSON)
|
[Native Thread]
- TO-BE
JS Thread <-> JSI (C++) ⇄ Native Code
동기 / 빠름 / 직렬화 없음
// 기존 방식
import { NativeModules } from 'react-native';
const { CameraModule } = NativeModules;
// TurboModules 방식
import { TurboModuleRegistry } from 'react-native';
const CameraModule = TurboModuleRegistry.get('CameraModule');
기존: JavaScript → Bridge → Shadow Thread → Main Thread
새로운: JavaScript → JSI → Fabric → Main Thread
모바일에 특화된 빠르고 가벼운 JavaScript 엔진
- 도입 이전
- App 실행 -> JS파싱 -> 컴파일 -> 실행
- 런타임에 Javascript 파싱 및 컴파일
- 도입 이후
- 빌드 시점 -> 바이트코드 생성 -> 앱 실행 시 바로 실행
- 개선효과
- 앱 시작 시간 단축
- 메모리 사용량 감소
- 더 작은 번들 사이즈(Android)
React Native에서 값을 비교할 때 사용하는 두 가지 연산자의 차이점을 알아보겠습니다.
타입 변환을 수행한 후 값을 비교합니다. 서로 다른 타입의 값도 같다고 판단할 수 있습니다.
5 == "5" // true (문자열 "5"를 숫자 5로 변환)
true == 1 // true (true를 1로 변환)
false == 0 // true (false를 0으로 변환)
null == undefined // true
"" == 0 // true (빈 문자열을 0으로 변환)
타입 변환 없이 값과 타입을 모두 비교합니다. 값과 타입이 모두 같아야 true를 반환합니다.
5 === "5" // false (타입이 다름: number vs string)
true === 1 // false (타입이 다름: boolean vs number)
false === 0 // false (타입이 다름: boolean vs number)
null === undefined // false (타입이 다름)
"" === 0 // false (타입이 다름: string vs number)
5 === 5 // true (값과 타입 모두 같음)
React Native 개발에서는 ===
를 사용하는 것을 강력히 권장합니다.
- 예측 가능한 동작: 타입 변환으로 인한 예상치 못한 결과를 방지
- 성능: 타입 변환 과정이 없어 더 빠름
- 코드 품질: 더 명확하고 안전한 코드 작성 가능
if (userInput === "") {
// 빈 문자열인 경우만 처리
}
if (count === 0) {
// 숫자 0인 경우만 처리
}
if (userInput == 0) {
// "0", "", false 등 다양한 값이 true가 될 수 있음
// 예상치 못한 동작을 유발할 수 있음
}
React Native에서는 의도치 않은 타입 변환을 피하고 더 안전한 코드를 작성하기 위해 ===
를 사용하는 것이 좋습니다.