이미 알고 있는 내용은 넘어갑니다.
완전히 동일한 개념은 아니지만, 대략 저런 느낌으로 이해하면 될듯.
공식에서 제공하는 리액트 네이티브 커스텀 모듈 사이트가 있다.
대충 훑어보니 firebase, google 연동 등도 이미 구현해둔 게 있는듯?
가져다 쓰자.
웹에서도 용도에 따라 다른 태그를 사용하도록 시멘틱 태그가 권장되어 있고 내부 성능도 조금씩 다르긴 하지만, 앱은 그 세분화 정도가 좀더 심하다고 생각이 들었다.
네이티브가 아니라 브리지라서 그런가?
스크롤뷰와 플랫리스트만 해도 아래와 같다.
항목 | ScrollView | FlatList |
---|---|---|
렌더링 방식 | 모든 아이템을 한 번에 렌더링 | 보이는 영역만 렌더링 (레이지 로딩) |
레이지 로딩 | ❌ 없음 | ✅ 있음 |
성능 (아이템 많을 때) | ❌ 성능 저하, 메모리 사용 많음 | ✅ 성능 최적화, 메모리 효율적 |
사용 방식 | <ScrollView> children </ScrollView> | data , renderItem props를 통해 사용 |
키 속성 | 자동 적용 안 됨 (직접 설정 필요) | keyExtractor 또는 key 필드 자동 처리 가능 |
추가 기능 | 기본 스크롤만 제공 | 무한 스크롤, pull-to-refresh 등 내장 기능 지원 |
사용 용도 | 적은 양의 고정 콘텐츠 (예: 설정 화면) | 많은 양의 데이터 리스트 (예: 게시판, 피드 등) |
포트 8081에서 실행된다. 비워 놓자.
웹팩보다 가볍고, 기능도 적다.
-> 트리셰이킹도 안하고 코드 스플리팅도 안 함.
.mp3, .wav, .mp4, .mov, .html 및 .pdf 전부 모듈처럼 require로 처리된다고.
-> Webpack은 이미지 URL을 만들어주거나 CDN에 올리지만, React Native는 로컬에 포함해서 앱 설치 시 같이 번들링됨.
즉, 웹에서는 이미지 최적화를 통해 초기 로딩 속도를 줄였다면 앱에서는 이미지 최적화가 초기 로딩 속도에 영향을 주지 않는다는 말이군요
앱에서 이미지 캐싱을 안 한다??
로컬 이미지(require)가 아니라 url로 불러오는 경우는 네트워크 요청으로 인식되어 디스크에 캐싱 안 된다고...
react-native-fast-image같은 외부라이브러리를 쓰거나, Image.prefetch() 로 사전 페칭해서 네이티브 캐싱 전략에 올라타야한다.
플러터에서는 호출브리지를 앱 개발자와 상의해서 하나하나 뚫었어야 했는데,
리액트 네이티브는 그냥 제공을 해준다. 야호.
-> 다만 그만큼 자율도는 낮아질듯...
에러 로그에서 원래 소스 코드 라인으로 매핑하는 것.
디버깅 서드파티와도 연동된다! (센트리 같은..)
리액트 네이티브는 실행 중인 플랫폼이 안드로이드인지 ios인지 구분할 수 있다.
-> window 대신 Platform을 쓰는 느낌인가?
import {Platform, StyleSheet} from 'react-native';
const styles = StyleSheet.create({
height: Platform.OS === 'ios' ? 200 : 100,
}); //'ios' | 'android' | 'native' | 'default'
혹은 아예 이렇게 플랫폼에 따라 다른 파일을 가져다 쓰게 할 수도 있다고!
BigButton.ios.js
BigButton.android.js
그냥 import로 가져다 쓰면 React Native는 실행 중인 플랫폼에 따라 올바른 파일을 자동으로 선택해준다! 일일이 분기문 넣을 필요가 없다!
참고로 이렇게 웹/모바일로 나눌 수도 있다. 웹팩은 위에 걸 선택하고, 메트로는 아래 걸 선택해서 자동으로 알아서 나눠줌. 편하다.
Container.js
Container.native.js
무엇보다 이렇게 확장자를 나눠 놓으면, 각 플랫폼별로 번들링할때 특정 확장자는 무시하도록 하게 해서 번들 크기를 줄일 수 있다고.
-> 나무를 내가 직접 흔들어야 하는군요...
TV 장치 지원은 기존 React Native 애플리케이션이 Apple TV 및 Android TV에서 작동하도록 하기 위해 구현되었으며 애플리케이션의 JavaScript 코드를 거의 또는 전혀 변경할 필요가 없습니다.
tv가 없어서 쓸 것 같진 않지만..
어쨌던 애플 비전도 지원하고, macOS, window도 지원한다고 한다.
일렉트론은 크로니움 엔진을 자체적으로 돌리고, 리액트 네이티브는 어디까지나 브리지다!!
구분 | React Native (Windows/macOS) | Electron |
---|---|---|
기본 구조 | 네이티브 UI 컴포넌트와 API 직접 호출 | Chromium + Node.js |
UI 렌더링 | 네이티브 위젯 사용 | 웹뷰 내 렌더링(무겁다!) |
네이티브 API 접근성 | 직접 네이티브 API 접근 및 제어 가능 | Node.js API 통한 제한적 접근 |
생태계 | 성장 중 | 매우 활발 |
개발 생산성 | 네이티브 코드 병행 필요, 학습 곡선 존재 | 웹 개발자 친숙, 빠른 프로토타이핑 |
요즘 nextjs때문에 리액트에서 tailwind가 핫하듯, 리액트 네이티브에서는 nativewind라는 걸 많이 쓴다고 한다.
// BAD
const icon = this.props.active
? 'my-icon-active'
: 'my-icon-inactive';
<Image source={require('./' + icon + '.png')} />;
// GOOD
const icon = this.props.active
? require('./my-icon-active.png')
: require('./my-icon-inactive.png');
<Image source={icon} />;
이미지를 flex로 동적으로 확대/축소해야 하는 경우 style 속성을 수동으로 설정해야 {width: undefined, height: undefined} 함!
-> 참고로, url로 가져오는 경우는 width, height를 명확하게 스타일에 명시해주어야 함.
이미지에서 직접 uri를 가져다 박을수도 있다.
-> 다만 작은 이미지에서만 사용하자..
<Image
style={{
width: 51,
height: 51,
resizeMode: 'contain',
}}
source={{
uri: 'data:image/png;base64,iVBORw0KGgoAAA..',
}}
/>
<Image
source={{
uri: 'https://reactjs.org/logo-og.png',
cache: 'only-if-cached',
}}
style={{width: 400, height: 400}}
/>
ios에서는 이미지 캐시 제한을 재정의할 수도 있다.
기본 AppDelegate 코드 내에서 호출하라는데 아마 일렉트론의 메인같은 부분이겠지요?
RCTSetImageCacheLimits(4*1024*1024, 200*1024*1024);
iOS는 카메라 롤에 동일한 이미지에 대해 여러 크기를 저장하므로..
-> 현재 화면도 정확히 일치하는 항목이 있으면 그것 선택
-> 없으면 최소 50% 더 큰 항목 씀
-> 리액트 네이티브가 자동으로 해주니까 내가 뭘 할 필요는 없다!
리액트 네이티브는 이미지 디코딩을 메인 스레드가 아닌 별도 스레드에서 처리한다!
-> 즉, 이미지가 커도 메인프레임 렌더링을 방해하지 않음!
이미지가 아니면 크기 정보가 전달되지 않는다!
flexGrow 대신 절대위치를 써야함.
-> 단, 네이티브 코드에 직접 연결된 영상은 상관없다고.
제스처와 애니메이션 값을 매핑할 수 있다!
import {
...,
Animated,
useWindowDimensions,
useAnimatedValue,
} from 'react-native';
const App = () => {
const scrollX = useAnimatedValue(0);
const {width: windowWidth} = useWindowDimensions();
return (
<SafeAreaProvider>
<SafeAreaView style={styles.container}>
<View style={styles.scrollContainer}>
<ScrollView
horizontal={true}
pagingEnabled
showsHorizontalScrollIndicator={false}
onScroll={Animated.event([
{
nativeEvent: {
contentOffset: {
x: scrollX,
},
...
useNavieDriver 옵션을 true로 줘서 쓸 수 있다
js에서 애니메이션이 실행되어 매 프레임 결과를 네이티브에 전달
-> 성능 저하!
애니메이션을 시작하기 전에 애니메이션을 네이티브로 전송!
-> 브리지를 거치지 않고 애니메이션 수행 = 메인 스레드와 별개로 애니메이션
레이아웃 변경에 사용되는 애니메이션
-> 레이아웃에 영향 주는 리플로우 변경을 자동 추적함!
-> 코어 애니메이션을 활용한다!
iOS의 시스템 레벨에서 제공하는 애니메이션 프레임워크
-> JS 스레드 및 메인 스레드 프레임 드롭의 영향을 받지 않는다!
UIManager.setLayoutAnimationEnabledExperimental(true);
프레임이 떨어지는 애니메이션(초당 60프레임 미만으로 수행)에서 할 수 있는 최적화 사항
제스처의 수명 주기 관리
앱의 느낌을 좋게 하려면 모든 작업에 다음과 같은 속성이 있어야 한다!
리액트에서 쓰는 거랑 별반 다르지 않음.
다만 흥미로워 보이는 게 몇 개 있어서 추가해본다.
속성명 | 설명 | 예시 |
---|---|---|
changedTouches | 이번 이벤트에서 바뀐 터치들의 배열 | 멀티 터치 중, 하나의 손가락만 움직였을 경우 → 해당 손가락 정보만 들어 있음 |
identifier | 터치 고유 ID | 멀티 터치 시 각 손가락 구별 |
target | 이벤트를 발생시킨 뷰 ID | 이벤트 발생 요소 확인 |
locationX | 터치한 요소 기준의 X 좌표 | 버튼의 왼쪽에서 20px 떨어진 곳을 눌렀다면 locationX = 20 |
pageX | 전체 화면 기준의 X 좌표 | 스크롤 내린 상태에서 눌렀다면 pageX 는 더 커짐 |
timestamp | 이벤트 발생 시각 (ms) | 터치 이동 속도 계산 |
touches | 현재 화면에 모든 활성화된 터치들의 배열 | 2개의 손가락으로 터치하고 있다면 touches.length === 2 |
ShouldSet 응답기들은 버블링되어 가장 깊은 노드에서 자동 호출!
-> 중간에서 호출하고 싶다면..
-> 끝까지 버블링되기 전에 ShouldSetResponderCapture를 실행하면 됨.
네이티브 앱에는 CORS의 개념이 없다!!
기본적으로 iOS 9.0 이상은 ATS(앱 전송 보안)를 적용-> https만 가능!
배포할때.. 방법을.. 찾아봅시다...
암호화되지 않은 비동기 키-값 저장소를 제공하는 React Native용 커뮤니티 유지 관리 모듈
-> 앱별 샌드박스 존재 (공유x)
-> 웹으로 따지면 Local storage같은 거라고!
expo-secure-store나 react-native-keychain같은 외부 라이브러리를 사용해야 접근 가능
ios에서는 앱에 키체인이라는 암호화된 데이터베이스에 소량의 사용자 데이터를 저장할 수 있다!
위와 같이 data는 한번 더 암호화됨.
자세한 건 애플 공식문서를 참조.
영구 키-값 데이터 저장소.
일반 공유 환경설정과 달리 키와 값을 자동으로 암호화해줌.
암호화 키를 컨테이너에 저장 -> 꺼내기 더 어려워짐!
모바일 앱 고유의 취약점.
외부 소스에서 네이티브 애플리케이션으로 직접 데이터를 보내는 방법.
url을 직접 수정해서 앱의 특정 페이지에 접근하는 것 같다.
뭔소리냐?
그..이래도 되는거임?
일반 HTTPs URL을 이용한 딥링크 방식.
웹은 DNS가 관리해주고 있다는 부분을 차용한 듯?
같은 스킴을 쓰는 다른 앱이 있다면 사용자가 선택하게 한다고.
js 스레드 성능은 개발 모드에서 크게 저하된다
-> 성능 테스트를 할거면 릴리스해서 하자.
병목 현상을 일으킨다.
바벨 플러그인 중 콘솔 로그 싹 없애서 빌드해주는 걸 쓰자.
npm i babel-plugin-transform-remove-console --save-dev
//.babelrc
{
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}
웹에서도 blur를 쓸 때 그랬지만, 메모리가 터질 수 있다..
아래 두 개를 사용해서 최적화할 수 있음.
최적화하기 위해 컴포넌트 재렌더링 필요성을 코드로 알려주어야 한다.
메모 쓰라는 말을 이렇게 어렵게 할 일인지?
iOS에서는 이미지 width, height를 조정할 때마다 원본 이미지를 직접 조정
-> 이미지가 클수록 비싸다!
-> width, height를 직접 바꾸기보다 transform으로 scale을 바꿔주자.
터치 반응 애니메이션이 onPress 함수가 반환될 때까지 실행되지 않을 때가 있다!
-> requestAnimationFrame 에서 onPress 핸들러 내부의 모든 작업을 래핑하면 된다는데..
-> 공식문서가 왜 자꾸 클래스형 접근을 예시로 드냐?? 함수형으로 바꿔옴.
const handleOnPress = useCallback(() => {
requestAnimationFrame(() => {
doExpensiveAction();
});
}, []);
특정 CPU 아키텍처에서 실행 가능한 이진 코드 형식과 운영체제 간의 약속
뭔소리냐?
어떤 CPU에서, 어떤 방식으로 실행할 수 있는 앱이냐를 결정하는 규격!
-> ARM CPU용 컴파일 코드와 Intel CPU용 컴파일 코드는 서로 다른 이진코드!
-> 따라서, 각각 다른 ABI로 표시해주어야 한다!
로컬 빌드시 아래 ABI를 모두 빌드
-> 로컬에서 테스트할 땐 하나만 빌드해도 된다.
-> 이러면 속도가 75%까지 줄어든다!
이 외에도 뭐 빌드 캐시 사용전략 이런 게 있는데.. 당장 필요하진 않을 것 같아서 건너뜀.