Skip to main content

26 posts tagged with "engineering"

View All Tags

트위터 앱 로딩 애니메이션을 React Native로 구현하기

· 19 min read
Eli White
Eli White
Software Engineer at Meta

Twitter의 iOS 앱에 있는 로딩 애니메이션이 마음에 든다.

앱이 준비되면 Twitter 로고가 확장되며 앱이 나타난다.

이 로딩 애니메이션을 React Native로 재현하는 방법을 찾아보고 싶었다.


이 애니메이션을 어떻게 구현할지 이해하기 위해, 먼저 로딩 애니메이션의 세부 요소를 분석했다. 미묘한 차이를 확인하려면 애니메이션 속도를 늦춰보는 것이 가장 좋다.

이 애니메이션에는 몇 가지 주요 요소가 있다. 이 요소들을 어떻게 구현할지 알아내야 한다.

  1. 새 모양의 로고를 확대한다.
  2. 로고가 커지면서 앱이 밑에서 나타난다.
  3. 마지막에 앱을 약간 축소한다.

이 애니메이션을 만드는 데 꽤 오랜 시간이 걸렸다.

처음에는 파란색 배경과 Twitter 로고가 앱 위에 있는 레이어라고 잘못 생각했다. 로고가 커지면서 투명해지면 앱이 나타나는 것이라고 가정했는데, 이 방식은 작동하지 않았다. 로고가 투명해지면 파란색 레이어가 보이지 앱이 보이지 않기 때문이다.

다행히 여러분은 내가 겪었던 실수를 반복하지 않고도 이 튜토리얼을 통해 바로 핵심을 배울 수 있다!


올바른 접근 방식

코드를 살펴보기 전에, 이 효과를 어떻게 분해할지 이해하는 것이 중요하다. 이 효과를 시각적으로 이해하기 위해 CodePen에서 재현했다(몇 단락 아래에 임베드됨). 이를 통해 여러분은 각 레이어를 인터랙티브하게 확인할 수 있다.

이 효과에는 세 가지 주요 레이어가 있다. 첫 번째는 파란색 배경 레이어다. 이 레이어는 앱 위에 나타나는 것처럼 보이지만, 실제로는 가장 뒤에 위치한다.

그 다음은 단순한 흰색 레이어다. 마지막으로 가장 앞쪽에는 앱이 위치한다.


이 애니메이션의 핵심은 Twitter 로고를 mask로 사용해 앱과 흰색 레이어를 모두 마스킹하는 것이다. 마스킹의 세부 사항은 깊이 다루지 않겠다. 여기, 여기, 여기와 같은 온라인 리소스가 많이 있다.

이 컨텍스트에서 마스킹의 기본 개념은 마스크의 불투명한 픽셀이 마스킹하는 내용을 보여주고, 투명한 픽셀은 마스킹하는 내용을 숨기는 것이다.

Twitter 로고를 마스크로 사용해 두 레이어를 마스킹한다. 단색 흰색 레이어와 앱 레이어다.

앱을 드러내기 위해 마스크를 전체 화면보다 크게 확대한다.

마스크가 확대되는 동안 앱 레이어의 투명도를 서서히 높여 앱을 보여주고, 뒤에 있는 흰색 레이어를 숨긴다. 효과를 마무리하기 위해 앱 레이어를 1보다 큰 크기에서 시작해 애니메이션이 끝날 때 1로 축소한다. 그런 다음, 더 이상 보이지 않을 비앱 레이어를 숨긴다.

사진 한 장이 천 마디 말보다 낫다고 한다. 인터랙티브한 시각화는 몇 마디 말에 해당할까? "Next Step" 버튼을 클릭해 애니메이션을 단계별로 확인해 보자. 레이어를 보여주면 측면 뷰를 확인할 수 있다. 그리드는 투명한 레이어를 시각적으로 이해하는 데 도움을 준다.

React Native로 구현하기

이제 우리가 만들 애니메이션의 동작 방식을 이해했으니, 본격적으로 코드를 작성해 보자. 여러분이 이 글을 읽는 주된 이유일 것이다.

이 퍼즐의 핵심은 React Native의 코어 컴포넌트 중 하나인 MaskedViewIOS다.

import {MaskedViewIOS} from 'react-native';

<MaskedViewIOS maskElement={<Text>Basic Mask</Text>}>
<View style={{backgroundColor: 'blue'}} />
</MaskedViewIOS>;

MaskedViewIOSmaskElementchildren을 props로 받는다. childrenmaskElement에 의해 마스킹된다. 마스크는 이미지일 필요가 없으며, 어떤 뷰든 가능하다. 위 예제의 동작은 파란색 뷰를 렌더링하지만, maskElement의 "Basic Mask"라는 텍스트가 있는 부분에서만 보이게 된다. 우리는 단순히 복잡한 파란색 텍스트를 만든 것이다.

우리가 원하는 것은 파란색 레이어를 렌더링하고, 그 위에 Twitter 로고가 있는 마스크와 흰색 레이어를 렌더링하는 것이다.

{
fullScreenBlueLayer;
}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Image source={twitterLogo} />
</View>
}>
{fullScreenWhiteLayer}
<View style={{flex: 1}}>
<MyApp />
</View>
</MaskedViewIOS>;

이 코드는 아래와 같은 레이어를 만들어 준다.

애니메이션 구현하기

이제 모든 준비가 끝났으니, 다음 단계는 애니메이션을 적용하는 것이다. 자연스러운 애니메이션을 만들기 위해 React Native의 Animated API를 활용한다.

Animated를 사용하면 자바스크립트에서 선언적으로 애니메이션을 정의할 수 있다. 기본적으로 이 애니메이션은 자바스크립트에서 실행되며, 매 프레임마다 네이티브 레이어에 어떤 변경을 해야 하는지 알려준다. 하지만 자바스크립트가 매 프레임마다 애니메이션을 업데이트하려고 해도 충분히 빠르게 처리하지 못해 프레임이 누락되는 현상(지연)이 발생할 수 있다. 이는 우리가 원하는 결과가 아니다!

Animated는 이러한 지연 없이 애니메이션을 구현할 수 있도록 특별한 기능을 제공한다. Animated에는 useNativeDriver라는 플래그가 있는데, 이는 애니메이션 시작 시 자바스크립트에서 네이티브로 애니메이션 정의를 전달한다. 이를 통해 네이티브 측에서 매 프레임마다 자바스크립트와 통신하지 않고도 애니메이션 업데이트를 처리할 수 있다. useNativeDriver의 단점은 특정 속성만 업데이트할 수 있다는 점이다. 주로 transformopacity 속성을 사용할 수 있다. useNativeDriver로는 배경색과 같은 속성을 애니메이션할 수 없다. 아직은 그렇지만, 시간이 지나면서 더 많은 속성을 추가할 예정이다. 물론 필요한 속성을 직접 PR로 제출해 커뮤니티 전체에 기여할 수도 있다 😀.

이 애니메이션을 부드럽게 만들기 위해 이러한 제약 조건 내에서 작업할 것이다. useNativeDriver의 내부 동작을 더 깊이 이해하려면 공식 블로그 포스트를 참고한다.

애니메이션 단계별 분석

애니메이션은 크게 4가지 단계로 구성된다:

  1. 새를 확대하며 앱과 흰색 레이어를 드러낸다.
  2. 앱을 서서히 나타나게 한다.
  3. 앱의 크기를 축소한다.
  4. 흰색 레이어와 파란색 레이어를 숨긴다.

Animated를 사용해 애니메이션을 정의하는 두 가지 주요 방법이 있다. 첫 번째는 Animated.timing을 사용하는 방법으로, 애니메이션의 지속 시간과 움직임을 부드럽게 하는 이징 곡선을 직접 지정할 수 있다. 두 번째는 물리 기반 API인 Animated.spring를 사용하는 방법이다. Animated.spring에서는 스프링의 마찰력과 장력을 지정하고 물리 엔진이 애니메이션을 실행하도록 한다.

여러 애니메이션을 동시에 실행해야 하며, 이들은 서로 밀접하게 연관되어 있다. 예를 들어, 마스크가 드러나는 중간에 앱을 서서히 나타나게 해야 한다. 이러한 애니메이션은 서로 밀접하게 연결되어 있기 때문에, 단일 Animated.Value를 사용해 Animated.timing으로 구현할 것이다.

Animated.Value는 애니메이션의 상태를 나타내는 네이티브 값을 감싼 래퍼다. 일반적으로 전체 애니메이션에 대해 하나의 Animated.Value만 사용한다. Animated를 사용하는 대부분의 컴포넌트는 이 값을 상태에 저장한다.

이 애니메이션을 시간에 따라 진행되는 단계로 생각하기 때문에, Animated.Value를 0(0% 완료)에서 시작해 100(100% 완료)로 끝나도록 설정할 것이다.

초기 컴포넌트 상태는 다음과 같다.

state = {
loadingProgress: new Animated.Value(0),
};

애니메이션을 시작할 준비가 되면, Animated에게 이 값을 100으로 애니메이션하도록 지시한다.

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true, // 이 부분이 중요하다!
}).start();

그런 다음 애니메이션의 각 부분과 전체 애니메이션 진행에 따라 원하는 값을 추정한다. 아래 표는 애니메이션의 각 부분과 시간에 따라 달라지는 값을 보여준다.

트위터 새 마스크는 스케일 1에서 시작해 작아졌다가 다시 크게 확대된다. 애니메이션의 10% 지점에서 스케일 값은 0.8이 되고, 마지막에는 스케일 70까지 확대된다. 70이라는 값은 사실 꽤 임의적으로 정한 것이다. 새가 화면을 완전히 드러낼 수 있을 만큼 충분히 커야 했고, 60으로는 부족했다.😀 흥미로운 점은 이 숫자가 클수록 같은 시간 안에 더 빠르게 커지는 것처럼 보인다는 것이다. 이 로고에 적합한 값을 찾기 위해 여러 번 시도했다. 로고나 디바이스 크기가 다르면 화면을 완전히 드러내기 위해 이 최종 스케일 값을 조정해야 한다.

앱은 트위터 로고가 작아지는 동안 불투명하게 유지되어야 한다. 공식 애니메이션을 참고해, 새가 확대되는 중간에 앱을 보이기 시작하고 빠르게 완전히 드러나도록 했다. 따라서 애니메이션의 15% 지점에서 앱이 보이기 시작하고, 30% 지점에서 완전히 나타난다.

앱의 스케일은 1.1에서 시작해 애니메이션 끝에 정상 크기로 축소된다.

코드로 구현하기

위에서 한 작업은 애니메이션 진행률을 각 요소에 적용할 값으로 매핑하는 것이다. Animated.interpolate를 사용해 이를 구현한다. this.state.loadingProgress를 기반으로 보간된 값을 사용해 애니메이션의 각 부분에 대한 스타일 객체를 3개 생성한다.

const loadingProgress = this.state.loadingProgress;

const opacityClearToVisible = {
opacity: loadingProgress.interpolate({
inputRange: [0, 15, 30],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
// clamp는 입력이 30-100일 때 출력을 1로 유지한다는 의미
}),
};

const imageScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 10, 100],
outputRange: [1, 0.8, 70],
}),
},
],
};

const appScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 100],
outputRange: [1.1, 1],
}),
},
],
};

이제 이 스타일 객체를 사용해 앞서 설명한 뷰 스니펫을 렌더링할 수 있다. Animated.Value를 사용하는 스타일 객체는 Animated.View, Animated.Text, Animated.Image만 사용할 수 있다는 점에 유의한다.

const fullScreenBlueLayer = (
<View style={styles.fullScreenBlueLayer} />
);
const fullScreenWhiteLayer = (
<View style={styles.fullScreenWhiteLayer} />
);

return (
<View style={styles.fullScreen}>
{fullScreenBlueLayer}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Animated.Image
style={[styles.maskImageStyle, imageScale]}
source={twitterLogo}
/>
</View>
}>
{fullScreenWhiteLayer}
<Animated.View
style={[opacityClearToVisible, appScale, {flex: 1}]}>
{this.props.children}
</Animated.View>
</MaskedViewIOS>
</View>
);

이제 애니메이션 조각이 원하는 대로 보인다. 이제 더 이상 보이지 않을 파란색과 흰색 레이어를 정리하기만 하면 된다.

이 레이어를 언제 정리할지 알려면 애니메이션이 완료된 시점을 알아야 한다. 다행히 Animated.timing을 호출할 때 .start는 애니메이션이 완료되면 실행되는 콜백을 선택적으로 받는다.

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true,
}).start(() => {
this.setState({
animationDone: true,
});
});

이제 애니메이션이 완료되었는지 알 수 있는 state 값을 가지게 되었으므로, 파란색과 흰색 레이어를 수정해 이 값을 사용할 수 있다.

const fullScreenBlueLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenBlueLayer]} />
);
const fullScreenWhiteLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenWhiteLayer]} />
);

이제 애니메이션이 작동하고, 애니메이션이 완료되면 사용하지 않는 레이어를 정리한다. 트위터 앱 로딩 애니메이션을 완성했다!

잠깐, 내 코드가 작동하지 않아요!

걱정하지 마세요. 저도 가이드에서 코드 조각만 제공하고 완성된 소스를 보여주지 않을 때 답답함을 느낍니다.

이 컴포넌트는 npm에 등록되어 있으며, GitHub에서 react-native-mask-loader로 확인할 수 있습니다. 여러분의 휴대폰에서 직접 테스트해보고 싶다면, Expo에서도 사용 가능합니다.

추가 학습 자료 / 심화 과제

  1. 이 gitbook은 React Native 문서를 읽은 후 Animated에 대해 더 깊이 배우기에 좋은 자료다.
  2. 실제 트위터 애니메이션은 끝 부분에서 마스크가 더 빨리 드러나는 것처럼 보인다. 로더를 수정해 다른 easing 함수(또는 spring!)를 사용해 그 동작을 더 잘 맞춰보자.
  3. 현재 마스크의 end-scale은 하드 코딩되어 있으며, 태블릿에서는 전체 앱이 드러나지 않을 가능성이 있다. 화면 크기와 이미지 크기를 기반으로 end scale을 계산해보는 것도 멋진 PR이 될 것이다.

React Native Monthly #6

· 7 min read
Tomislav Tenodi
Speck 창립자

리액트 네이티브 월간 미팅은 여전히 활발히 진행 중이다! 다음 세션에 대한 안내는 이 글의 하단에 있는 노트를 꼭 확인하길 바란다.

Expo

  • Devin AbbottHoussein Djirdeh가 "Full Stack React Native" 책의 프리릴리즈를 발표한 것을 축하한다! 이 책은 여러 작은 앱을 만들어가며 React Native를 배우는 과정을 안내한다. 책을 구매하기 전에 https://www.fullstackreact.com/react-native/에서 해당 앱들을 직접 시험해볼 수 있다.

  • ReasonML을 쉽게 시도해볼 수 있도록 도와주는 reason-react-native-scripts의 첫 번째(실험적) 버전을 출시했다.

  • Expo SDK 24가 출시되었다! 이번 버전은 React Native 0.51을 기반으로 하며, 여러 새로운 기능과 개선사항을 포함한다: 스탠드얼론 앱에서 이미지 번들링(첫 로드 시 캐시할 필요 없음!), 이미지 조작 API(자르기, 크기 조정, 회전, 뒤집기), 얼굴 인식 API, 새로운 릴리스 채널 기능(특정 채널에 대한 활성 릴리스 설정 및 롤백), 스탠드얼론 앱 빌드를 추적할 수 있는 웹 대시보드, OpenGL Android 구현과 Android 멀티태스커의 오랜 버그 수정 등이 있다.

  • 이번 1월부터 React Navigation에 더 많은 리소스를 할당하기로 했다. React 컴포넌트와 Animated, react-native-gesture-handler 같은 기본 요소만으로 React Native 네비게이션을 구축하는 것이 가능하고 바람직하다고 강력히 믿으며, 계획된 몇 가지 개선사항에 대해 매우 기대하고 있다. 커뮤니티에 기여하고 싶다면 react-native-mapsreact-native-svg를 확인해보길 바란다. 두 프로젝트 모두 도움이 필요하다!

Infinite Red

Microsoft

  • 풀 리퀘스트가 시작되어 React Native Windows 브릿지를 .NET Standard로 마이그레이션하는 작업이 진행 중이다. 이로 인해 OS에 구애받지 않는 구조로 변환될 전망이다. 이 작업이 완료되면 다양한 .NET Core 플랫폼에서 자체 스레딩 모델, JavaScript 런타임, 그리고 UIManager를 활용해 브릿지를 확장할 수 있게 된다. 예를 들어 JavaScriptCore, Xamarin.Mac, Linux Gtk#, 그리고 Samsung Tizen 같은 옵션들이 가능해진다.

Wix

  • Detox

    • E2E 테스트를 확장하기 위해 CI 시간을 최소화하려고 노력 중이다. 이를 위해 Detox의 병렬화 지원을 개발 중이다.
    • E2E에서 모킹을 더 잘 지원하기 위해 커스텀 플레이버 빌드를 지원하는 풀 리퀘스트를 제출했다.
  • DetoxInstruments

    • DetoxInstruments의 핵심 기능을 개발하는 것은 매우 도전적인 작업이다. 특정 시점에 JavaScript 백트레이스를 얻기 위해서는 JS 스레드 일시 중단을 지원하는 커스텀 JSCore 구현이 필요하다. Wix 앱에서 프로파일러를 테스트한 결과 JS 스레드에 대한 흥미로운 통찰을 얻었다.
    • 이 프로젝트는 아직 일반 사용에 충분히 안정적이지 않지만, 활발히 개발 중이며 곧 공개할 예정이다.
  • React Native Navigation

    • V2 개발 속도가 크게 증가했다. 지금까지는 1명의 개발자가 20%의 시간을 할애했지만, 이제는 3명의 개발자가 풀타임으로 작업 중이다.
  • Android 성능

    • RN에 번들된 구버전 JSCore를 최신 버전(webkitGTK 프로젝트의 최신 버전, 커스텀 JIT 설정 포함)으로 교체했더니 JS 스레드 성능이 40% 향상되었다. 다음 단계는 64비트 버전을 컴파일하는 것이다. 이 작업은 JSC 빌드 스크립트 for Android를 기반으로 한다. 현재 진행 상황은 여기에서 확인할 수 있다.

다음 회의 주제

이번 회의를 특정 주제에 초점을 맞춰 진행하는 방안에 대해 논의 중이다. 예를 들어 네비게이션, React Native 모듈을 별도 저장소로 분리, 문서화 등 구체적인 주제를 깊이 있게 다룰 예정이다. 이 방식을 통해 React Native 커뮤니티에 더 큰 기여를 할 수 있을 것으로 기대한다. 다음 회의에서 이 주제를 다룰 가능성이 높다. 다루고 싶은 주제가 있다면 트위터를 통해 의견을 공유해 주시기 바란다.

React Native Monthly #5

· 7 min read
Tomislav Tenodi
Founder at Speck

리액트 네이티브 월간 미팅이 계속됩니다! 각 팀이 어떤 작업을 진행 중인지 살펴봅시다.

Callstack

  • React Native CI 작업을 진행했다. 가장 중요한 것은 Travis에서 Circle로 마이그레이션하여 React Native가 단일 통합 CI 파이프라인을 갖추게 된 것이다.
  • Hacktoberfest - React Native 버전을 주최했다. 참가자들과 함께 오픈소스 프로젝트에 많은 풀 리퀘스트를 제출하려고 노력했다.
  • Haul 작업을 계속하고 있다. 지난달에는 webpack 3 지원을 포함한 두 가지 새로운 릴리스를 제출했다. CRNAExpo 지원을 추가하고 더 나은 HMR을 작업할 계획이다. 로드맵은 이슈 트래커에 공개되어 있다. 개선 사항을 제안하거나 피드백을 주고 싶다면 알려주기 바란다!

Expo

  • Expo SDK 22를 출시했다. (React Native 0.49 사용) 그리고 이를 위해 CRNA를 업데이트했다.
    • 개선된 스플래시 스크린 API, 기본 ARKit 지원, "DeviceMotion" API, iOS11에서의 SFAuthenticationSession 지원 등을 포함한다. 더 많은 내용은 블로그에서 확인할 수 있다.
  • Snack에서 이제 여러 JavaScript 파일을 사용할 수 있으며, 이미지와 다른 리소스를 에디터에 드래그 앤 드롭으로 업로드할 수 있다.
  • react-navigation에 기여하여 iPhone X 지원을 추가했다.
  • Expo로 대규모 애플리케이션을 구축할 때 발생하는 문제점에 집중했다. 예를 들어:
    • 스테이징, 프로덕션, 임의의 채널 등 여러 환경에 배포하는 퍼스트클래스 지원. 채널은 롤백 기능과 특정 채널에 대한 활성 릴리즈 설정을 지원한다. 초기 테스터가 되고 싶다면 @expo_io로 알려달라.
    • 독립 실행형 앱 빌드 인프라를 개선하고, 독립 실행형 앱 빌드에서 이미지와 코드가 아닌 다른 리소스를 번들링하는 동시에 리소스를 무선으로 업데이트할 수 있는 기능을 추가하는 작업도 진행 중이다.

Facebook

  • 더 나은 RTL 지원:
    • 방향을 인식하는 스타일을 여러 가지 도입한다.
      • 위치:
        • (left|right) → (start|end)
      • 마진:
        • margin(Left|Right) → margin(Start|End)
      • 패딩:
        • padding(Left|Right) → padding(Start|End)
      • 테두리:
        • borderTop(Left|Right)Radius → borderTop(Start|End)Radius
        • borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius
        • border(Left|Right)Width → border(Start|End)Width
        • border(Left|Right)Color → border(Start|End)Color
    • RTL에서 위치, 마진, 패딩, 테두리 스타일의 "left"와 "right" 의미가 서로 바뀌었다. 몇 달 내에 이 동작을 제거하고 "left"는 항상 "왼쪽", "right"는 항상 "오른쪽"을 의미하도록 변경할 예정이다. 이 변경 사항은 플래그 아래에 숨겨져 있다. React Native 컴포넌트에서 I18nManager.swapLeftAndRightInRTL(false)를 사용해 이 변경을 적용할 수 있다.
  • Flow를 사용해 내부 네이티브 모듈의 타입을 정의하고, 이를 기반으로 Java에서는 인터페이스, ObjC에서는 프로토콜을 생성하는 작업을 진행 중이다. 이 코드 생성 도구가 내년 초에 오픈소스로 공개될 것으로 기대한다.

Infinite Red

  • React Native 및 기타 프로젝트를 지원하는 새로운 오픈소스 도구를 개발 중이다. 자세한 내용은 여기에서 확인할 수 있다.
  • 새로운 보일러플레이트 릴리스를 위해 Ignite를 개편 중이다 (코드명: Bowser).

Shoutem

  • Shoutem의 개발 프로세스를 개선했다. 앱 생성부터 첫 커스텀 화면 구현까지의 과정을 간소화하고, 새로운 React Native 개발자가 쉽게 접근할 수 있도록 했다. 새로운 기능을 시험하기 위해 여러 워크숍을 준비했다. 또한 Shoutem CLI를 개선해 새로운 워크플로를 지원한다.
  • Shoutem UI는 여러 컴포넌트 개선과 버그 수정을 거쳤다. 최신 React Native 버전과의 호환성도 확인했다.
  • Shoutem 플랫폼에는 몇 가지 주목할 만한 업데이트가 적용됐다. 새로운 통합 기능이 오픈소스 확장 프로젝트의 일부로 추가됐다. 다른 개발자들이 Shoutem 확장 기능을 적극적으로 개발하는 모습을 보며 기쁘게 생각한다. 우리는 그들의 확장 기능에 대해 조언과 가이드를 제공하며 적극적으로 소통한다.

다음 세션

다음 세션은 2017년 12월 6일 수요일에 예정되어 있다. 세션 결과물을 개선할 방법에 대한 제안이 있다면 트위터를 통해 언제든지 연락해 주세요.

React Native Monthly #4

· 6 min read
Mike Grabowski
Mike Grabowski
CTO and Co-Founder at Callstack

React Native 월간 회의가 계속되고 있다! 각 팀의 진행 상황 요약은 다음과 같다:

콜스택 소식

  • React Native EU가 종료되었다. 33개국에서 온 300명 이상의 참가자가 브로츠와프를 방문했다. 발표 내용은 YouTube에서 확인할 수 있다.
  • 컨퍼런스 이후 오픈 소스 일정으로 천천히 복귀하고 있다. 현재 react-native-opentok의 다음 버전을 작업 중이며, 대부분의 기존 문제를 해결할 예정이다.

GeekyAnts

개발자들이 React Native를 더 쉽게 접할 수 있도록 다음과 같은 노력을 기울이고 있다:

  • React Native EU에서 BuilderX.io를 발표했다. BuilderX는 JavaScript 파일(현재는 React Native만 지원)과 직접 연동되어 아름답고 가독성 높으며 편집 가능한 코드를 생성하는 디자인 도구다.
  • ReactNativeSeed.com을 출시했다. 이 사이트는 다음 React Native 프로젝트를 위한 다양한 보일러플레이트를 제공한다. 데이터 타입을 위한 TypeScript와 Flow, 상태 관리를 위한 MobX, Redux, mobx-state-tree 등 다양한 옵션과 함께 CRNA 및 일반 React-Native 스택을 지원한다.

Expo

  • 곧 SDK 21을 출시할 예정이다. 이번 업데이트는 react-native 0.48.3을 지원하며, 비디오 녹화, 새로운 스플래시 화면 API, react-native-gesture-handler 지원, 그리고 개선된 오류 처리 기능을 포함한 다양한 버그 수정 및 신뢰성 향상이 이루어졌다.

  • react-native-gesture-handler에 대해, Software MansionKrzysztof Magiera가 이 프로젝트를 계속해서 발전시키고 있다. Expo 팀은 이 프로젝트의 테스트를 지원하고 개발 시간의 일부를 후원하고 있다. SDK21에서 이 기능이 통합되면 Snack에서 쉽게 사용해볼 수 있게 되어, 커뮤니티에서 어떤 결과물을 만들어낼지 기대된다.

  • 개선된 오류 로깅 및 처리에 대해서는 이 Expo 내부 PR의 gist에서 자세한 로깅 정보를 확인할 수 있다. 특히 "Problem 2"를 참고하면 된다. 또한 이 커밋은 npm 표준 라이브러리 모듈을 가져오려고 시도했을 때 실패한 경우를 처리하는 변경 사항을 보여준다. React Native에서 오류 메시지를 개선할 여지가 많으며, Expo 팀은 이에 대한 후속 PR을 작업할 예정이다. 커뮤니티의 참여도 매우 환영한다.

  • native.directory는 계속해서 성장하고 있다. 여러분의 프로젝트를 GitHub 저장소에서 추가할 수 있다.

  • 북미 전역의 해커톤에 참여할 예정이다. PennApps, Hack The North, HackMIT, 그리고 곧 MHacks 등을 방문할 계획이다.

Facebook

  • Android에서 <Text><TextInput> 컴포넌트 개선 작업을 진행 중이다. (<TextInput>의 자동 크기 조절 기능, 깊게 중첩된 <Text> 컴포넌트의 레이아웃 문제 해결, 더 나은 코드 구조, 성능 최적화 등)
  • 이슈 관리와 풀 리퀘스트 검토를 도와줄 추가 기여자를 여전히 찾고 있다.

마이크로소프트

  • CodePush에 코드 서명 기능을 추가했다. 이제 React Native 개발자들이 CodePush에서 애플리케이션 번들에 서명할 수 있다. 자세한 내용은 여기에서 확인할 수 있다.
  • CodePush를 Mobile Center와 통합하는 작업을 진행 중이다. 테스트 및 크래시 통합도 고려하고 있다.

다음 세션

다음 세션은 2017년 10월 10일 수요일에 예정되어 있다. 이번이 네 번째 모임이기 때문에, 우리는 이 자료가 React Native 커뮤니티에 어떤 도움을 주는지 알고 싶다. 만약 모임의 결과물을 어떻게 개선할지 제안할 사항이 있다면, 트위터를 통해 언제든지 연락해 주기 바란다.

React Native Monthly #3

· 8 min read
Mike Grabowski
Mike Grabowski
CTO and Co-Founder at Callstack

React Native 월간 미팅이 계속됩니다! 이번 달 미팅은 대부분의 팀이 출시 작업으로 바빠서 조금 짧게 진행됐습니다. 다음 달에는 폴란드 브로츠와프에서 열리는 React Native EU 컨퍼런스에 참석합니다. 티켓을 구매하고 현장에서 만나요! 그동안 우리 팀이 어떤 일을 하고 있는지 살펴보겠습니다.

팀 소개

세 번째 모임에는 다음 5개 팀이 참여했다:

노트

각 팀의 노트는 다음과 같습니다:

Callstack

  • 최근 오픈소스로 공개한 react-native-material-palette는 이미지에서 주요 색상을 추출해 시각적으로 매력적인 앱을 만들 수 있도록 도와준다. 현재는 Android만 지원하지만, 향후 iOS 지원도 추가할 계획이다.
  • haul에 HMR(Hot Module Replacement) 지원을 추가했고, 여러 멋진 기능도 함께 포함했다. 최신 릴리즈를 확인해 보자.
  • React Native EU 2017이 다가온다! 다음 달은 폴란드에서 React Native에 관한 모든 것을 다룰 예정이다. 남은 티켓을 여기에서 서둘러 구매하자.

Expo

  • Snack에서 npm 패키지 설치를 지원한다. 기존 Expo 제한 사항이 적용되며, 패키지는 Expo에 포함되지 않은 커스텀 네이티브 API에 의존할 수 없다. 또한 Snack에서 여러 파일 지원과 에셋 업로드 기능을 추가하기 위해 작업 중이다. SatyajitReact Native Europe에서 Snack에 대해 발표할 예정이다.
  • SDK20을 출시했다. 카메라, 결제, 보안 스토리지, 자기계, 파일 다운로드 일시 중지/재개 기능, 그리고 스플래시/로딩 화면 개선이 포함된다.
  • Krzysztof와 함께 react-native-gesture-handler 작업을 계속 진행 중이다. PanResponder나 네이티브 제스처 인식기를 사용해 이전에 구현한 제스처를 다시 만들어 보고, 문제가 발생하면 알려달라고 요청한다.
  • JSC 디버깅 프로토콜을 실험 중이며, Canny에서 다양한 기능 요청을 처리하고 있다.

Facebook

  • 지난달에는 GitHub 이슈 트래커 관리에 대해 논의했고, 프로젝트의 유지 보수성을 개선하기 위해 노력하겠다고 이야기했다.
  • 현재 오픈된 이슈 수는 약 600개 수준으로 유지되고 있으며, 당분간 이 상태가 계속될 것으로 보인다. 지난달 동안 활동이 없는 이슈(최근 60일 동안 댓글이 없는 경우) 690개를 닫았다. 이 중 58개 이슈는 다양한 이유로 다시 열렸다(메인테이너가 수정을 약속하거나, 기여자가 이슈를 계속 열어둘 만한 타당한 이유를 제시한 경우).
  • 당분간 활동이 없는 이슈를 자동으로 닫는 정책을 계속 유지할 계획이다. 이슈 트래커에 등록된 모든 중요한 이슈가 적절히 처리되는 상태를 목표로 하고 있지만, 아직 그 수준에는 이르지 못했다. 메인테이너들의 도움이 절실하다. 특히 새로 생성된 프로젝트에 영향을 미치는 회귀 버그나 주요 변경 사항을 놓치지 않도록 이슈를 분류하고 관리해야 한다. 도움을 주고 싶은 사람은 Facebook GitHub Bot을 사용해 이슈와 풀 리퀘스트를 분류할 수 있다. 새로운 메인테이너 가이드에는 이슈 분류와 GitHub Bot 사용 방법에 대한 자세한 정보가 포함되어 있다. 이슈 태스크 포스에 참여하고, 다른 활발한 커뮤니티 멤버들도 참여하도록 독려해 주길 바란다!

마이크로소프트

  • 새로운 Skype 앱은 플랫폼 간 코드 공유를 최대화하기 위해 React Native를 기반으로 개발되었다. React Native 기반의 Skype 앱은 현재 Android와 iOS 앱 스토어에서 이용 가능하다.
  • Skype 앱을 React Native로 개발하면서, 발견한 버그와 누락된 기능을 해결하기 위해 React Native에 풀 리퀘스트를 보냈다. 지금까지 약 70개의 풀 리퀘스트가 병합되었다.
  • React Native를 통해 Android와 iOS Skype 앱을 동일한 코드베이스로 구동할 수 있었다. 이 코드베이스를 Skype 웹 앱에도 활용하고자 한다. 이를 위해 React/React Native 위에 얇은 레이어인 ReactXP를 개발하고 오픈소스로 공개했다. ReactXP는 iOS/Android를 타겟팅할 때는 React Native로, 웹을 타겟팅할 때는 react-dom으로 매핑되는 크로스 플랫폼 컴포넌트를 제공한다. ReactXP의 목표는 React Native for Web이라는 오픈소스 라이브러리와 유사하다. 두 라이브러리의 접근 방식 차이에 대한 간단한 설명은 ReactXP FAQ에서 확인할 수 있다.

Shoutem

  • Shoutem을 사용해 앱을 개발할 때 개발자 경험을 개선하고 단순화하기 위한 노력을 계속하고 있다.
  • 모든 앱을 react-navigation으로 마이그레이션하기 시작했지만, 더 안정적인 버전이 출시되거나 네이티브 네비게이션 솔루션 중 하나가 안정화될 때까지 이 작업을 연기하기로 결정했다.
  • 모든 extensions과 대부분의 오픈소스 라이브러리(animation, theme, ui)를 React Native 0.47.1로 업데이트하고 있다.

다음 세션

다음 세션은 2017년 9월 13일 수요일에 예정되어 있다. 이번이 세 번째 모임이므로, 이 노트가 React Native 커뮤니티에 어떻게 도움이 되는지 알고 싶다. 모임 결과물을 개선할 방법에 대한 제안이 있다면 트위터를 통해 언제든지 연락해 주세요.

React Native Performance in Marketplace

· 10 min read
Software Engineer at Facebook

React Native는 메인 Facebook 앱의 상단 탭을 포함해 Facebook 계열의 여러 앱에서 다양하게 활용된다. 이 글에서 중점적으로 살펴볼 제품은 Marketplace다. Marketplace는 약 12개 국가에서 서비스되며, 사용자가 다른 사용자가 제공하는 상품과 서비스를 발견할 수 있도록 도와준다.

2017년 상반기에 Relay 팀, Marketplace 팀, Mobile JS Platform 팀, 그리고 React Native 팀의 협력을 통해 Year Class 2010-11 기기에서의 Marketplace Time to Interaction(TTI)를 절반으로 줄였다. Facebook은 이 기기들을 저사양 안드로이드 기기로 분류하며, 이 기기들은 모든 플랫폼 또는 기기 타입 중 가장 느린 TTI를 보여준다.

일반적인 React Native 시작 과정은 다음과 같다:

주의: 비율은 대표적이지 않으며, React Native가 어떻게 구성되고 사용되는지에 따라 달라진다.

먼저 React Native 코어(일명 "Bridge")를 초기화한 후, 제품별 JavaScript를 실행한다. 이 JavaScript는 Native Processing Time 동안 React Native가 어떤 네이티브 뷰를 렌더링할지 결정한다.

다른 접근 방식

초기에 우리가 저지른 실수 중 하나는 Systrace와 CTScan이 성능 개선 작업을 주도하도록 내버려 둔 것이다. 이 도구들은 2016년에 많은 쉬운 문제들을 찾는 데 도움을 주었지만, Systrace와 CTScan이 실제 프로덕션 환경을 대표하지 못하며 실제 상황에서 발생하는 문제를 제대로 재현할 수 없다는 사실을 깨달았다. 시간 분할 비율이 종종 틀리거나 심지어 전혀 엉뚱한 경우도 있었다. 극단적인 경우, 우리가 몇 밀리초 정도 걸릴 것으로 예상했던 작업들이 실제로는 수백 또는 수천 밀리초가 걸리기도 했다. 그렇지만 CTScan은 여전히 유용하며, 우리는 이 도구가 프로덕션 환경에 도달하기 전에 약 1/3의 성능 저하 문제를 잡아낸다는 사실을 발견했다.

안드로이드에서 이러한 도구들의 한계는 1) React Native가 멀티스레드 프레임워크라는 점, 2) Marketplace가 뉴스피드와 같은 복잡한 뷰 및 다른 최상위 탭과 함께 배치된다는 점, 3) 계산 시간이 크게 변동한다는 사실에 기인한다. 따라서 이번 반기에는 프로덕션 측정과 분석 결과가 거의 모든 의사결정과 우선순위 설정을 주도하도록 했다.

프로덕션 계측 과정의 여정

프로덕션 계측은 표면적으로는 간단해 보이지만, 실제로는 꽤 복잡한 과정이다. 각각 2-3주에 걸친 여러 번의 반복 주기를 거쳤다. 마스터 브랜치에 커밋을 적용하고, 앱을 Play Store에 푸시하고, 충분한 프로덕션 샘플을 수집해 작업에 대한 확신을 얻기까지의 지연 시간 때문이다. 각 반복 주기에서는 분석이 정확한지, 적절한 수준의 세분화를 가지고 있는지, 전체 시간 범위에 올바르게 합산되는지 확인했다. 알파와 베타 릴리스는 일반 사용자를 대표하지 않기 때문에 의존할 수 없었다. 결국, 우리는 수백만 개의 샘플을 집계해 매우 정확한 프로덕션 트레이스를 매우 세심하게 구축했다.

분석에서 매 밀리초가 상위 메트릭에 올바르게 합산되는지 꼼꼼히 확인한 이유 중 하나는 초기에 계측에 공백이 있음을 깨달았기 때문이다. 초기 분석은 스레드 점프로 인한 지연을 고려하지 않았다. 스레드 점프 자체는 비용이 크지 않지만, 이미 작업 중인 바쁜 스레드로의 점프는 매우 비용이 크다. 결국, 적절한 순간에 Thread.sleep() 호출을 추가해 이러한 차단을 로컬에서 재현했고, 다음과 같은 방법으로 문제를 해결했다:

  1. AsyncTask에 대한 의존성을 제거했다.
  2. UI 스레드에서 ReactContext와 NativeModules의 강제 초기화를 취소했다.
  3. 초기화 시점에 ReactRootView를 측정하는 의존성을 제거했다.

이러한 스레드 차단 문제를 해결함으로써 시작 시간을 25% 이상 단축했다.

프로덕션 메트릭은 우리의 기존 가정 중 일부에 도전하기도 했다. 예를 들어, 시작 경로에서 많은 JavaScript 모듈을 미리 로드했는데, 이는 모듈을 하나의 번들에 함께 배치하면 초기화 비용이 줄어든다는 가정 때문이었다. 그러나 이러한 모듈을 미리 로드하고 함께 배치하는 비용은 이점을 훨씬 초과했다. 인라인 require 블랙리스트를 재구성하고 시작 경로에서 JavaScript 모듈을 제거함으로써, Relay Classic과 같은 불필요한 모듈을 로드하지 않을 수 있었다(Relay Modern만 필요한 경우). 현재, RUN_JS_BUNDLE 분석은 75% 이상 빨라졌다.

제품별 네이티브 모듈을 조사해 얻은 성과도 있었다. 예를 들어, 네이티브 모듈의 의존성을 지연 로딩함으로써 해당 모듈의 비용을 98% 줄였다. Marketplace 시작을 다른 제품과의 경합에서 제거함으로써, 동일한 시간만큼 시작 시간을 단축했다.

가장 좋은 점은 이러한 개선 사항 중 상당수가 React Native로 구축된 모든 화면에 광범위하게 적용될 수 있다는 것이다.

결론

많은 사람들이 React Native의 시작 성능 문제를 자바스크립트의 속도가 느리거나 네트워크 시간이 과도하게 길기 때문이라고 생각한다. 물론 자바스크립트 속도를 높이면 TTI(Time to Interactive)를 상당히 줄일 수 있지만, 이전에 생각했던 것보다 이러한 요소들이 TTI에 미치는 영향은 훨씬 작다.

지금까지의 교훈은 측정, 측정, 또 측정이다! 일부 성능 개선은 런타임 비용을 빌드 시간으로 옮기는 방식에서 얻을 수 있다. 예를 들어 Relay Modern과 Lazy NativeModules가 그렇다. 다른 개선은 코드를 병렬화하거나 죽은 코드를 제거하는 방식으로 작업을 피함으로써 얻을 수 있다. 또한 React Native의 대규모 아키텍처 변경, 예를 들어 스레드 블로킹을 정리하는 것과 같은 방법으로도 성능을 개선할 수 있다. 성능 문제에 대한 만능 해결책은 없으며, 장기적인 성능 개선은 점진적인 도구화와 개선을 통해 이루어진다. 인지 편향이 결정에 영향을 미치지 않도록 주의해야 한다. 대신, 실제 프로덕션 데이터를 신중하게 수집하고 해석하여 미래 작업을 이끌어야 한다.

향후 계획

장기적으로는 Marketplace TTI가 네이티브로 개발된 유사 제품과 비슷한 수준이 되길 바라며, React Native의 성능이 네이티브 수준에 근접하도록 개선할 계획이다. 또한, 이번 분기에 브릿지 시작 비용을 약 80%나 줄였지만, Prepack과 같은 프로젝트를 통해 React Native 브릿지 비용을 거의 0에 가깝게 낮추려고 한다. 더불어 빌드 시간 처리도 더욱 최적화할 예정이다.

React Native Monthly #2

· 14 min read
Tomislav Tenodi
Shoutem 제품 매니저

React Native 월간 미팅이 계속됩니다! 이번 세션에서는 Chain React, the React Native Conference를 주최한 Infinite Red가 함께했습니다. 대부분의 참가자들이 Chain React에서 발표를 준비 중이었기 때문에, 미팅을 일주일 뒤로 미뤘습니다. 컨퍼런스의 발표 내용은 온라인에 업로드되었으니, 꼭 확인해 보세요. 이제 각 팀이 어떤 일을 하고 있는지 살펴보겠습니다.

참여 팀

두 번째 미팅에는 총 9개 팀이 참여했다:

노트

각 팀의 노트는 다음과 같다:

Airbnb

  • React Native 관련 프로젝트를 확인하려면 Airbnb 저장소를 방문한다.

콜스택 업데이트

  • Mike Grabowski는 여전히 React Native의 월별 릴리스를 관리하고 있으며, 몇 가지 베타 버전도 배포했다. 특히 Windows 사용자를 위한 v0.43.5 빌드를 npm에 출시하기 위해 노력 중이다.

  • Haul 프로젝트는 느리지만 꾸준히 진행 중이다. HMR(Hot Module Replacement)을 추가하는 풀 리퀘스트가 있으며, 다른 개선 사항도 적용되었다. 최근 몇몇 업계 리더들이 이를 도입했다. 해당 분야에서 풀타임 유료 작업을 시작할 계획도 있다.

  • Jest 팀의 Michał Pierzchała가 이번 달에 콜스택에 합류했다. 그는 Haul 유지보수를 도울 예정이며, Metro BundlerJest 작업에도 참여할 가능성이 있다.

  • Satyajit Sahoo가 이제 우리와 함께하게 되었다!

  • OSS 부서에서 다양한 멋진 작업이 진행 중이다. 특히 Material Palette API를 React Native로 가져오는 작업을 진행하고 있다. 네이티브 iOS 컴포넌트와 1:1로 동일한 느낌을 제공하는 네이티브 iOS 키트를 마침내 출시할 계획이다.

Expo 최신 소식

  • 최근 React Native 생태계 내 라이브러리의 발견과 평가를 돕기 위해 Native Directory를 출시했다. 문제는 많은 라이브러리가 존재하지만 테스트가 어렵고, 수동으로 휴리스틱을 적용해야 하며, 어떤 라이브러리가 가장 좋은지 즉시 알기 어렵다는 것이다. 또한 CRNA/Expo와 호환되는지 여부를 파악하기도 어렵다. Native Directory는 이러한 문제를 해결하려고 한다. 여기에서 라이브러리 목록을 확인할 수 있다. 이는 첫 번째 시도이며, Expo 팀뿐만 아니라 커뮤니티가 주도적으로 운영하길 바란다. 이 프로젝트가 가치 있다고 생각한다면 기여해 주길 바란다!
  • Snack에서 Expo SDK 19와 함께 npm 패키지를 설치할 수 있는 초기 지원을 추가했다. 문제가 발생하면 알려주길 바란다. 아직 몇 가지 버그를 수정 중이다. Native Directory와 함께 JS 의존성만 있거나 Expo SDK에 포함된 의존성을 가진 라이브러리를 쉽게 테스트할 수 있어야 한다. 다음 예제를 시도해 보라:
  • Expo SDK19를 출시하며 다양한 개선 사항을 적용했고, 업데이트된 Android JSC를 사용하고 있다.
  • Alexander Kotliarskyi와 함께 앱의 사용자 경험을 개선하는 방법에 대한 팁 목록을 포함한 가이드를 작성 중이다. 이 목록에 추가하거나 작성에 도움을 주길 바란다!
  • 오디오/비디오, 카메라, 제스처(Software Mansion의 react-native-gesture-handler와 협력), GL 카메라 통합 작업을 계속 진행 중이며, SDK20(8월)에서 처음으로 이 기능들을 제공할 계획이다. 또한 이때까지 다른 기능들도 크게 개선할 예정이다. Expo 클라이언트에 백그라운드 작업(위치 정보, 오디오, 알림 처리 등)을 위한 인프라 구축을 시작했다.
  • Adam Miskiewiczreact-navigation에서 UINavigationController의 트랜지션을 모방하는 데 큰 진전을 이루었다. 그의 트윗에서 초기 버전을 확인할 수 있다. 곧 출시될 예정이다. 또한 그가 업스트림MaskedViewIOS도 확인해 보라. MaskedView를 Android에 구현할 능력과 의지가 있다면 매우 환영한다!

Facebook

  • Facebook은 내부적으로 React Native 안에 네이티브 ComponentKitLitho 컴포넌트를 임베드할 수 있는 방법을 연구하고 있다.
  • React Native에 기여하는 것은 매우 환영한다! 기여 방법이 궁금하다면, "How to Contribute" 가이드를 참고해 개발 프로세스와 첫 풀 리퀘스트를 보내는 단계를 확인할 수 있다. 코드 작성 없이도 기여할 수 있는 방법이 있다. 이슈를 분류하거나 문서를 업데이트하는 것도 그 예시다.
    • 현재 React Native에는 635개오픈 이슈249개오픈 풀 리퀘스트가 있다. 이는 관리자들에게 부담이 되며, 내부적으로 문제가 해결되더라도 관련 작업이 업데이트되기 어려운 상황이다.
    • 커뮤니티를 만족시키면서 이 문제를 해결할 최선의 방법을 아직 확정하지 못했다. 몇 가지 옵션(모든 것이 아님!)으로는 오래된 이슈를 닫기, 더 많은 사람에게 이슈 관리 권한 부여하기, 이슈 템플릿을 따르지 않는 이슈를 자동으로 닫기 등이 있다. 관리자들이 기대하는 바를 명확히 하고 예상치 못한 상황을 피하기 위해 "What to Expect from Maintainers" 가이드를 작성했다. 관리자와 이슈 및 풀 리퀘스트를 열어주는 사람들이 모두 만족할 수 있는 방법에 대한 아이디어가 있다면 알려주기 바란다!

GeekyAnts

  • Chain React에서 React Native 파일과 호환되는 디자이너 툴을 시연했다. 많은 참가자들이 대기자 명단에 등록했다.
  • Google Flutter, Kotlin Native, Apache Weex와 같은 크로스 플랫폼 솔루션도 살펴보고 있다. 이들의 아키텍처 차이를 이해하고, React Native의 전반적인 성능을 개선할 수 있는 방법을 모색 중이다.
  • 대부분의 앱에서 react-navigation으로 전환했고, 이로 인해 전반적인 성능이 향상되었다.
  • 또한 NativeBase Market을 발표했다. 이는 React Native 컴포넌트와 앱을 위한 마켓플레이스로, 개발자들이 직접 참여하고 활용할 수 있는 플랫폼이다.

Infinite Red

마이크로소프트

  • CodePush가 이제 Mobile Center에 통합되었다. 기존 사용자는 작업 흐름에 변화가 없다.
    • 일부 사용자가 중복 앱 문제를 보고했다. Mobile Center에 이미 앱이 존재하는 경우다. 이 문제를 해결 중이며, 두 개의 앱이 있다면 알려주면 병합해 줄 수 있다.
  • Mobile Center는 이제 CodePush용 푸시 알림을 지원한다. 또한 알림과 CodePush를 조합해 A/B 테스트를 수행하는 방법을 보여주었다. 이는 ReactNative 아키텍처에서만 가능한 독특한 기능이다.
  • VS Code에는 ReactNative 디버깅 문제가 알려져 있다. 며칠 내로 출시될 다음 확장 버전에서 이 문제가 해결될 예정이다.
  • 마이크로소프트 내부에서도 여러 팀이 React Native를 개발하고 있다. 다음 회의에서는 모든 그룹의 참여를 더 잘 반영할 수 있도록 노력할 것이다.

Shoutem

  • Shoutem에서 React Native 개발을 더 쉽게 만드는 과정을 완료했다. Shoutem에서 앱을 개발할 때 모든 표준 react-native 커맨드를 사용할 수 있다.
  • React Native에서 프로파일링을 최적으로 수행하는 방법을 찾기 위해 많은 노력을 기울였다. 문서의 상당 부분이 오래되어, 공식 문서에 풀 리퀘스트를 보내거나 최소한 블로그 포스트에 우리의 결론을 작성할 계획이다.
  • 네비게이션 솔루션을 react-navigation으로 전환했으므로, 곧 피드백을 얻을 수 있을 것이다.
  • 툴킷에 새로운 HTML 컴포넌트를 출시했다. 이 컴포넌트는 원시 HTML을 React Native 컴포넌트 트리로 변환한다.

Wix

  • Metro Bundlerreact-native-repackager 기능을 추가하는 풀 리퀘스트 작업을 시작했다. 프로덕션에서 사용하는 RN 44를 지원하도록 react-native-repackager를 업데이트했다. 이를 detox의 모킹 인프라에 활용하고 있다.
  • 지난 3주 동안 Wix 앱을 detox 테스트로 커버하고 있다. 40명 이상의 엔지니어가 참여하는 대규모 앱에서 수동 QA를 줄이는 방법에 대해 많은 것을 배울 수 있었다. 그 과정에서 detox의 여러 문제를 해결했고, 새로운 버전을 출시했다. "제로 불안정성 정책"을 충실히 지키고 있으며, 현재까지 테스트가 지속적으로 통과되고 있다는 점을 기쁘게 알린다.
  • Detox for Android는 순조롭게 진행 중이다. 커뮤니티로부터 큰 도움을 받고 있다. 약 2주 내로 초기 버전을 출시할 예정이다.
  • 성능 테스트 도구인 DetoxInstruments는 원래 계획보다 더 큰 규모로 발전하고 있다. 이제 이를 detox와 긴밀하게 결합되지 않은 독립형 도구로 전환할 계획이다. 이를 통해 iOS 앱의 성능을 일반적으로 조사할 수 있게 될 것이다. 또한 detox와 통합해 성능 지표에 대한 자동화 테스트를 실행할 수 있을 것이다.

다음 세션

다음 세션은 2017년 8월 16일로 예정되어 있다. 이번이 두 번째 모임이기 때문에, 이 회의록이 React Native 커뮤니티에 어떻게 도움이 되는지 알고 싶다. 회의 결과물을 개선할 방법에 대한 제안이 있다면 트위터를 통해 언제든지 연락해 주시기 바란다.

React Native Monthly #1

· 11 min read
Tomislav Tenodi
Shoutem 제품 관리자

Shoutem에서는 React Native가 처음 등장했을 때부터 함께 작업할 기회를 얻었다. 우리는 이 놀라운 커뮤니티의 일원이 되기로 결심했다. 하지만 곧 커뮤니티가 성장하고 개선되는 속도를 따라잡는 것이 거의 불가능하다는 것을 깨달았다. 그래서 우리는 주요 React Native 기여자들이 자신들의 노력과 계획을 간단히 발표할 수 있는 월간 모임을 조직하기로 결정했다.

월간 회의

2017년 6월 14일, 첫 번째 월간 회의를 진행했다. React Native 월간 회의의 목표는 간단하고 명확하다: React Native 커뮤니티를 개선하는 것. 각 팀의 노력을 공유하면 오프라인에서 이루어지는 팀 간 협업이 더 원활해진다.

팀 소개

첫 번째 모임에는 8개 팀이 참여했다:

앞으로 더 많은 핵심 기여자들이 다음 세션에 참여하기를 기대한다!

공유 자료

팀의 계획이 더 많은 독자들에게 유용할 수 있다고 판단해, React Native 블로그를 통해 공유한다. 아래 내용을 확인해 보자:

Airbnb

  • ViewAccessibilityInfo 네이티브 모듈에 접근성(A11y) API를 추가할 계획이다.
  • 안드로이드 네이티브 모듈에 스레드를 지정해 실행할 수 있는 API를 추가하는 방안을 검토 중이다.
  • 초기화 성능 향상을 위한 가능성을 연구 중이다.
  • "unbundle" 위에 적용할 더 정교한 번들링 전략을 탐구 중이다.

콜스택

  • Detox를 E2E 테스트에 활용해 릴리스 프로세스를 개선하는 방안을 검토 중이다. 곧 풀 리퀘스트가 적용될 예정이다.
  • 작업 중이던 Blob 풀 리퀘스트가 머지되었고, 관련 후속 풀 리퀘스트가 곧 올라올 예정이다.
  • Haul을 내부 프로젝트에 점차 도입하며 Metro Bundler와의 성능 비교를 진행 중이다. 웹팩 팀과 협력해 멀티스레드 성능을 개선하는 작업도 진행 중이다.
  • 내부적으로 오픈소스 프로젝트를 관리하기 위한 더 나은 인프라를 구축했다. 앞으로 몇 주 안에 더 많은 내용을 공개할 계획이다.
  • React Native Europe 컨퍼런스가 다가오고 있지만, 아직 특별한 소식은 없다. 모두 초대한다!
  • 잠시 react-navigation에서 한 발 물러나 다른 네비게이션 솔루션(특히 네이티브 네비게이션)을 조사 중이다.

Expo

  • Snack에서 npm 모듈을 설치할 수 있도록 작업 중이다. 이 기능은 라이브러리 문서에 예제를 추가하는 데 유용할 것이다.
  • KrzysztofSoftware Mansion의 다른 사람들과 함께 Android에서 JSC 업데이트와 제스처 처리 라이브러리를 개발 중이다.
  • Adam Miskiewiczreact-navigation에 집중하기 위해 전환 중이다.
  • Create React Native App이 문서의 시작하기 가이드에 포함되었다. Expo는 라이브러리 작성자들이 자신들의 라이브러리가 CRNA와 호환되는지 명확히 설명하고, 호환된다면 설정 방법을 설명하도록 권장한다.

Facebook

  • React Native의 패키저가 이제 Metro Bundler로 독립된 리포지토리에서 관리된다. 런던에 위치한 Metro Bundler 팀은 커뮤니티의 요구를 해결하고, React Native 외의 다양한 사용 사례를 위한 모듈성을 개선하며, 이슈와 PR에 대한 응답성을 높이기 위해 노력하고 있다.
  • 앞으로 몇 달 동안 React Native 팀은 기본 컴포넌트의 API를 개선할 예정이다. 레이아웃 문제, 접근성, Flow 타이핑 등에서 개선이 이뤄질 것이다.
  • React Native 팀은 올해 코어 모듈성을 개선할 계획이다. Windows와 macOS와 같은 타사 플랫폼을 완벽히 지원하기 위해 리팩토링 작업을 진행할 것이다.

GeekyAnts

  • 팀은 .js 파일과 직접 연동되는 UI/UX 디자인 앱(코드명: Builder)을 개발 중이다. 현재는 React Native만 지원하며, Adobe XD나 Sketch와 유사한 기능을 제공한다.
  • 팀은 기존 React Native 앱을 에디터에서 불러와 디자이너가 시각적으로 수정한 뒤, 변경 사항을 바로 JS 파일에 저장할 수 있도록 열심히 작업 중이다.
  • 디자이너와 개발자 간의 간극을 줄이고, 두 팀이 동일한 레포지토리에서 협업할 수 있도록 노력하고 있다.
  • 또한 NativeBase가 최근 GitHub에서 5,000 스타를 달성했다.

마이크로소프트

  • CodePush가 이제 Mobile Center에 통합되었다. 이는 배포, 분석 및 기타 서비스와 더욱 통합된 환경을 제공하기 위한 첫 번째 단계다. 자세한 내용은 여기에서 확인할 수 있다.
  • VS Code에는 디버깅 관련 버그가 있으며, 현재 이를 수정 중이며 새로운 빌드를 출시할 예정이다.
  • 통합 테스트를 위해 Detox를 검토 중이며, JSC Context를 통해 크래시 리포트와 함께 변수를 확인하는 방법을 연구하고 있다.

Shoutem

  • 리액트 네이티브 커뮤니티의 도구를 활용해 Shoutem 앱 작업을 더 쉽게 만든다. Shoutem에서 생성한 앱을 실행하기 위해 모든 리액트 네이티브 커맨드를 사용할 수 있다.

  • 리액트 네이티브 프로파일링 도구를 연구 중이다. 설정 과정에서 많은 문제를 겪었고, 이를 해결하며 얻은 인사이트를 공유할 예정이다.

  • Shoutem은 기존 네이티브 앱과 리액트 네이티브를 더 쉽게 통합하는 방법을 개발 중이다. 회사 내부에서 구축한 개념을 문서화해 커뮤니티의 피드백을 받을 계획이다.

Wix

  • 내부적으로 Detox를 도입해 Wix 앱의 상당 부분을 "수동 QA 없이" 운영하는 방향으로 전환 중이다. 그 결과, 수십 명의 개발자가 프로덕션 환경에서 Detox를 활발히 사용하며 빠르게 성숙해지고 있다.
  • Metro Bundler에 빌드 중 파일 확장자를 오버라이드할 수 있는 기능을 추가 중이다. 기존의 "ios"와 "android"뿐만 아니라 "e2e"나 "detox"와 같은 커스텀 확장자도 지원할 예정이다. 이를 E2E 모킹에 활용할 계획이다. 이미 react-native-repackager라는 라이브러리가 있으며, 현재 PR 작업을 진행 중이다.
  • 성능 테스트 자동화를 연구 중이다. DetoxInstruments라는 새로운 저장소를 오픈소스로 개발 중이다. 관심 있다면 확인해 볼 수 있다.
  • KPN의 기여자와 함께 Detox의 Android 지원 및 실제 기기에서의 동작을 개선 중이다.
  • "Detox를 플랫폼으로 활용"해 시뮬레이터/기기를 자동화해야 하는 다른 도구를 구축할 수 있는 방안을 고민 중이다. 예를 들어 React Native용 Storybook이나 Ram의 통합 테스트 아이디어가 있다.

다음 모임

모임은 매 4주마다 진행된다. 다음 모임은 2017년 7월 12일로 예정되어 있다. 이번 모임을 시작으로, 여러분이 이 모임 기록이 React Native 커뮤니티에 어떻게 도움이 되는지 알고 싶다. 앞으로 다룰 주제나 모임 결과물을 개선할 방법에 대한 제안이 있다면 트위터를 통해 언제든지 연락해 주길 바란다.

Better List Views in React Native

· 11 min read
Spencer Ahrens
Software Engineer at Facebook

여러분 중 많은 분들이 이미 커뮤니티 그룹의 티저 발표를 보고 새로운 List 컴포넌트를 사용해 보기 시작했지만, 오늘 공식적으로 발표한다! 더 이상 ListViewDataSource, 오래된 행, 무시된 버그, 과도한 메모리 소비에 시달릴 필요가 없다. 최신 React Native 2017년 3월 릴리스 후보 버전(0.43-rc.1)에서 여러분은 새로운 컴포넌트 세트 중에서 가장 적합한 것을 선택할 수 있으며, 뛰어난 성능과 즉시 사용 가능한 기능들을 제공한다:

<FlatList>

이 컴포넌트는 간단하면서도 고성능 리스트를 구현할 때 주로 사용한다. 데이터 배열과 renderItem 함수만 제공하면 바로 사용할 수 있다:

<FlatList
data={[{title: 'Title Text', key: 'item1'}, ...]}
renderItem={({item}) => <ListItem title={item.title} />}
/>

<SectionList>

데이터를 논리적인 섹션으로 나누어 렌더링하고 싶다면, 예를 들어 알파벳 순 주소록처럼 섹션 헤더를 추가하거나, 프로필 뷰처럼 다양한 데이터와 렌더링 방식을 혼합하여 사용하고 싶다면 (예: 버튼, 컴포저, 사진 그리드, 친구 그리드, 스토리 리스트 순서로 구성된 화면), 이 컴포넌트를 사용하면 된다.

<SectionList
renderItem={({item}) => <ListItem title={item.title} />}
renderSectionHeader={({section}) => <H1 title={section.key} />}
sections={[ // 섹션 간 동일한 렌더링 방식
{data: [...], key: ...},
{data: [...], key: ...},
{data: [...], key: ...},
]}
/>

<SectionList
sections={[ // 섹션 간 다른 렌더링 방식
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
]}
/>

<VirtualizedList>

보다 유연한 API를 제공하는 내부 구현체다. 데이터가 일반 배열이 아닌 경우(예: 불변 리스트)에 특히 유용하다.

기능

리스트는 다양한 상황에서 활용되므로, 새로운 컴포넌트에는 대부분의 사용 사례를 즉시 처리할 수 있는 다양한 기능을 포함했다:

  • 스크롤 로딩 (onEndReached).
  • 당겨서 새로고침 (onRefresh / refreshing).
  • 구성 가능한 가시성(VPV) 콜백 (onViewableItemsChanged / viewabilityConfig).
  • 수평 모드 (horizontal).
  • 지능적인 아이템 및 섹션 구분자.
  • 다중 컬럼 지원 (numColumns).
  • scrollToEnd, scrollToIndex, 그리고 scrollToItem.
  • 향상된 Flow 타입 지정.

주의 사항

  • 아이템 서브트리의 내부 상태는 렌더링 윈도우를 벗어나 스크롤될 때 유지되지 않는다. 모든 데이터를 아이템 데이터나 Flux, Redux, Relay 같은 외부 저장소에 저장해야 한다.

  • 이 컴포넌트들은 PureComponent를 기반으로 동작한다. 따라서 props가 얕은 비교(shallow-equal)에서 동일하다면 리렌더링되지 않는다. renderItem 함수가 직접 의존하는 모든 값이 업데이트 후 === 비교에서 동일하지 않은 prop으로 전달되도록 해야 한다. 그렇지 않으면 UI가 변경 사항에 따라 업데이트되지 않을 수 있다. 이는 data prop과 부모 컴포넌트의 상태도 포함된다. 예를 들어:

    <FlatList
    data={this.state.data}
    renderItem={({item}) => (
    <MyItem
    item={item}
    onPress={() =>
    this.setState(oldState => ({
    selected: {
    // 새로운 인스턴스는 `===` 비교를 깨뜨림
    ...oldState.selected, // 기존 데이터 복사
    [item.key]: !oldState.selected[item.key], // 토글
    },
    }))
    }
    selected={
    !!this.state.selected[item.key] // renderItem은 state에 의존
    }
    />
    )}
    selected={
    // 기존 props와 충돌하지 않는 어떤 prop이라도 가능
    this.state.selected // selected가 변경되면 FlatList가 리렌더링됨
    }
    />
  • 메모리 사용을 제한하고 부드러운 스크롤을 가능하게 하기 위해, 콘텐츠는 오프스크린에서 비동기적으로 렌더링된다. 이는 스크롤 속도가 채우는 속도보다 빨라 잠깐 빈 콘텐츠가 보일 수 있음을 의미한다. 이는 각 애플리케이션의 필요에 따라 조정할 수 있는 트레이드오프이며, 내부적으로 이를 개선하기 위해 노력하고 있다.

  • 기본적으로 이 새로운 리스트는 각 아이템의 key prop을 찾아 React key로 사용한다. 또는 커스텀 keyExtractor prop을 제공할 수도 있다.

성능

새로운 리스트 컴포넌트는 API를 단순화할 뿐만 아니라, 성능 면에서도 상당한 개선을 이루었다. 가장 큰 장점은 행의 수와 상관없이 거의 일정한 메모리 사용량을 유지한다는 점이다. 이는 렌더링 윈도우 밖에 있는 엘리먼트를 '가상화'하여 컴포넌트 계층 구조에서 완전히 언마운트하고, React 컴포넌트의 JS 메모리와 섀도우 트리 및 UI 뷰의 네이티브 메모리를 회수함으로써 가능해졌다. 단, 이 경우 컴포넌트 내부 상태는 보존되지 않으므로, 중요한 상태는 컴포넌트 외부에서 관리해야 한다. 예를 들어 Relay, Redux, Flux 스토어 등을 활용할 수 있다.

렌더링 윈도우를 제한함으로써 React와 네이티브 플랫폼이 수행해야 하는 작업량도 줄어든다. 예를 들어 뷰 탐색 작업이 대표적이다. 수백만 개의 엘리먼트 중 마지막 항목을 렌더링하더라도, 새로운 리스트를 사용하면 모든 엘리먼트를 순회할 필요가 없다. scrollToIndex를 사용해 중간으로 바로 이동할 수 있으며, 이 과정에서 과도한 렌더링이 발생하지 않는다.

또한 스케줄링 측면에서도 개선이 이루어져 애플리케이션의 반응성이 향상되었다. 렌더링 윈도우의 가장자리에 위치한 항목은 적은 빈도로 렌더링되며, 활성 제스처나 애니메이션, 기타 상호작용이 완료된 후 낮은 우선순위로 처리된다.

고급 활용

ListView와 달리, 렌더링 윈도우 내의 모든 항목은 props가 변경될 때마다 리렌더링된다. 윈도우 기법이 항목 수를 일정하게 줄여주기 때문에 대부분의 경우 문제가 없지만, 항목이 복잡한 구조라면 성능을 위해 React의 모범 사례를 따르는 것이 좋다. React.PureComponent를 사용하거나 컴포넌트 내에서 shouldComponentUpdate를 적절히 활용해 재귀적 서브트리의 리렌더링을 제한할 수 있다.

항목을 렌더링하지 않고도 행의 높이를 계산할 수 있다면, getItemLayout prop을 제공해 사용자 경험을 개선할 수 있다. 이렇게 하면 scrollToIndex 등을 사용해 특정 항목으로 스크롤할 때 훨씬 부드러운 동작을 보장하며, 콘텐츠의 높이를 렌더링 없이도 결정할 수 있어 스크롤 표시기 UI도 향상된다.

불변 리스트(immutable list)와 같은 대체 데이터 타입을 사용한다면 <VirtualizedList>를 활용하는 것이 좋다. 이 컴포넌트는 getItem prop을 제공해 주어진 인덱스에 해당하는 항목 데이터를 반환할 수 있으며, 타입 체크도 좀 더 유연하게 처리한다.

특이한 사용 사례가 있다면 조정할 수 있는 다양한 파라미터도 있다. 예를 들어 windowSize를 사용해 메모리 사용량과 사용자 경험 사이의 균형을 조정하거나, maxToRenderPerBatch로 렌더링 속도와 반응성의 균형을 맞출 수 있다. onEndReachedThreshold를 사용해 스크롤 로딩이 발생하는 시점을 제어할 수도 있다.

향후 작업

  • 기존 컴포넌트의 마이그레이션 (최종적으로 ListView의 사용 중단).
  • 필요성에 따라 추가 기능 구현 (여러분의 의견을 보내주세요!).
  • 스틱키 고정 섹션 헤더 지원.
  • 더 많은 성능 최적화 작업.
  • 상태를 가진 함수형 아이템 컴포넌트 지원.

idx: The Existential Function

· 4 min read
Timothy Yung
Engineering Manager at Facebook

페이스북에서는 GraphQL로 가져온 데이터 구조에서 깊게 중첩된 값에 접근해야 하는 경우가 많다. 이러한 깊게 중첩된 값에 접근하는 과정에서 하나 이상의 중간 필드가 nullable인 경우가 흔하다. 중간 필드가 null이 될 수 있는 이유는 다양하다. 실패한 개인정보 확인부터, 치명적이지 않은 오류를 표현하는 가장 유연한 방법으로 null을 사용하는 경우까지 여러 가지가 있다.

안타깝게도, 깊게 중첩된 값에 접근하는 작업은 현재 매우 번거롭고 장황하다.

props.user &&
props.user.friends &&
props.user.friends[0] &&
props.user.friends[0].friends;

ECMAScript에 존재 여부를 확인하는 연산자를 도입하는 제안이 있어 이를 훨씬 편리하게 만들 수 있다. 하지만 이 제안이 확정되기 전까지, 우리는 삶의 질을 개선하고 기존 언어의 의미를 유지하며 Flow를 통한 타입 안전성을 장려하는 해결책을 원했다.

그래서 우리는 idx라는 존재 여부를 확인하는 _함수_를 만들었다.

idx(props, _ => _.user.friends[0].friends);

이 코드 스니펫의 호출은 위의 불리언 표현식과 유사하게 동작하지만, 반복이 훨씬 적다. idx 함수는 정확히 두 개의 인자를 받는다:

  • 일반적으로 중첩된 값에 접근하려는 객체나 배열 등의 값.
  • 첫 번째 인자를 받아 그 위에서 중첩된 값에 접근하는 함수.

이론적으로, idx 함수는 null이나 undefined에 접근할 때 발생하는 오류를 try-catch로 잡으려고 시도한다. 이런 오류가 잡히면 null이나 undefined를 반환한다. (이 구현 방법은 idx.js에서 확인할 수 있다.)

실제로, 모든 중첩된 속성 접근을 try-catch로 감싸는 것은 느리고, 특정 종류의 TypeError를 구분하는 것은 취약하다. 이러한 단점을 해결하기 위해, 우리는 위의 idx 호출을 다음 표현식으로 변환하는 Babel 플러그인을 만들었다:

props.user == null
? props.user
: props.user.friends == null
? props.user.friends
: props.user.friends[0] == null
? props.user.friends[0]
: props.user.friends[0].friends;

마지막으로, 우리는 idx에 대한 커스텀 Flow 타입 선언을 추가했다. 이를 통해 두 번째 인자에서의 탐색이 적절히 타입 체크되면서도 nullable 속성에 대한 중첩 접근이 허용된다.

이 함수, Babel 플러그인, 그리고 Flow 선언은 이제 GitHub에서 사용할 수 있다. idxbabel-plugin-idx npm 패키지를 설치하고 .babelrc 파일의 플러그인 목록에 "idx"를 추가하면 사용할 수 있다.