Skip to main content

26 posts tagged with "engineering"

View All Tags

React Native 0.75 - 레이아웃에서 퍼센트 값 지원, 새로운 아키텍처 안정화, 템플릿 및 init 업데이트 등

· 26 min read
Gabriel Donadel Dall'Agnol
Gabriel Donadel Dall'Agnol
Software Engineer at Expo
Siddharth Kulkarni
Siddharth Kulkarni
Software Engineer at Coinbase
Thibault Malbranche
Thibault Malbranche
Lead Mobile Engineer at Brigad
Blake Friedman
Blake Friedman
Software Engineer at Meta
Riccardo Cipolleschi
Riccardo Cipolleschi
Software Engineer at Meta
Nicola Corti
Nicola Corti
Software Engineer at Meta

오늘, React Native 0.75 버전을 출시하게 되어 기쁘게 생각합니다!

이번 릴리스에서는 % 값을 지원하는 Yoga 3.1, 새로운 아키텍처를 위한 여러 안정화 수정, 그리고 React Native 프레임워크 사용을 권장하는 내용을 포함한 여러 기능을 제공합니다.

주요 내용

주요 변경 사항

React Native 0.71-RC0 Android 장애 사후 분석

· 14 min read
Nicola Corti
Nicola Corti
Software Engineer at Meta
Lorenzo Sciandra
Lorenzo Sciandra
Senior Software Engineer at Microsoft

이제 0.71 버전이 공개되었으므로, 2022년 11월 4일 React Native와 Expo의 첫 0.71 릴리스 후보를 출시하면서 발생한 Android 빌드 문제에 대한 주요 정보를 공유하려 한다.

이 문제를 해결하는 데 기여한 개발자들은 최근 사후 분석 회의에 참여해 발생한 일, 이를 통해 배운 점, 그리고 앞으로 유사한 장애를 방지하기 위해 취할 조치에 대해 자세히 논의했다.

First-class Support for TypeScript

· 11 min read
Luna Wei
Luna Wei
Software Engineer at Meta
Nick Gerleman
Nick Gerleman
Software Engineer at Meta

React Native 0.71 버전 출시와 함께, TypeScript 사용 경험을 개선하기 위해 다음과 같은 변화가 도입되었다:

이 글에서는 TypeScript나 Flow 사용자에게 이러한 변화가 어떤 의미를 가지는지 설명한다.

Preparing Your App for iOS 15 and Android 12

· 6 min read
Samuel Susla
Samuel Susla
Software Engineer at Meta

안녕하세요, 여러분!

올해 말 출시 예정인 새로운 모바일 OS 버전에 대비해, React Native 앱을 미리 준비해 두는 것을 추천합니다. 이렇게 하면 일반적으로 사용 가능해질 때 발생할 수 있는 문제를 미리 방지할 수 있습니다.

Introducing new iOS WebViews

· 4 min read
Software Engineer at Facebook

오랜 시간 동안 애플은 UIWebView 대신 WKWebView 사용을 권장해왔다. 앞으로 몇 달 안에 출시될 iOS 12에서는 UIWebView가 공식적으로 지원 중단될 예정이다. 리액트 네이티브의 iOS WebView 구현은 UIWebView 클래스에 크게 의존하고 있다. 이러한 변화를 고려하여, 리액트 네이티브의 WebView 컴포넌트를 WKWebView 기반으로 재구성한 새로운 네이티브 iOS 백엔드를 개발했다.

이 변경 사항의 마지막 부분은 이 커밋에 반영되었으며, 0.57 릴리스에서 사용할 수 있다.

이 새로운 구현을 사용하려면 useWebKit prop을 사용한다:

<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>

개선 사항

UIWebView는 웹뷰에서 실행되는 자바스크립트와 React Native 간의 통신을 정상적으로 지원하지 않았다. 웹뷰에서 메시지를 보낼 때, 우리는 이를 React Native로 전달하기 위해 해킹적인 방법에 의존해야 했다. 간단히 말해, 메시지 데이터를 특수한 스키마가 포함된 URL로 인코딩한 후, 웹뷰를 해당 URL로 이동시켰다. 네이티브 측에서는 이 네비게이션을 가로채고 취소한 후, URL에서 데이터를 파싱하여 최종적으로 React Native를 호출했다. 이 구현 방식은 오류가 발생하기 쉽고 보안상 취약점이 있었다. 이제 우리는 WKWebView의 기능을 활용해 이를 완전히 대체했으며, 이 소식을 기쁘게 전한다.

WKWebView가 UIWebView에 비해 갖는 다른 장점으로는 더 빠른 자바스크립트 실행 속도와 멀티 프로세스 아키텍처가 있다. 자세한 내용은 2014 WWDC를 참고한다.

주의 사항

컴포넌트에서 아래와 같은 props를 사용한다면, WKWebView로 전환할 때 문제가 발생할 수 있다. 당분간은 이 props들을 사용하지 않는 것을 권장한다.

일관되지 않은 동작:

automaticallyAdjustContentInsetscontentInsets (커밋)

WKWebViewcontentInsets를 추가해도 WKWebView의 뷰포트는 변경되지 않는다. 뷰포트는 프레임과 동일한 크기를 유지한다. 반면 UIWebView에서는 뷰포트 크기가 실제로 변경된다(만약 contentInsets가 양수라면 더 작아진다).

backgroundColor (커밋)

새로운 iOS WebView 구현체를 사용하면, 이 속성을 사용할 때 배경색이 깜빡이며 나타날 가능성이 있다. 또한 WKWebView는 투명한 배경을 UIWebview와 다르게 렌더링한다. 자세한 내용은 커밋 설명을 참고한다.

지원되지 않는 기능:

scalesPageToFit (커밋)

WKWebViewscalesPageToFit prop을 지원하지 않기 때문에, React Native의 WebView 컴포넌트에서 이 기능을 구현할 수 없었다.

Accessibility API Updates

· 12 min read
Ziqi Chen
Student at UC Berkeley

동기

기술이 발전하고 모바일 앱이 일상생활에서 점점 더 중요해짐에 따라, 접근성 있는 애플리케이션을 만드는 필요성도 그만큼 커졌다.

React Native의 제한된 접근성 API는 개발자들에게 항상 큰 고민거리였다. 그래서 우리는 더 포괄적인 모바일 애플리케이션을 쉽게 만들 수 있도록 접근성 API를 몇 가지 업데이트했다.

기존 API의 문제점

문제 1: 완전히 다르지만 유사한 두 가지 props - accessibilityComponentType (Android)와 accessibilityTraits (iOS)

accessibilityComponentTypeaccessibilityTraits는 각각 Android의 TalkBack과 iOS의 VoiceOver에게 사용자가 상호작용하는 UI 엘리먼트의 종류를 알려주는 프로퍼티이다. 이 두 프로퍼티의 가장 큰 문제점은 다음과 같다:

  1. 두 프로퍼티는 사용 방법이 다르지만 동일한 목적을 가지고 있다. 기존 API에서는 두 프로퍼티가 플랫폼별로 분리되어 있어 불편할 뿐만 아니라 많은 개발자에게 혼란을 주었다. iOS의 accessibilityTraits는 17가지 값을 지원하는 반면, Android의 accessibilityComponentType는 단 4가지 값만 허용한다. 또한, 두 프로퍼티의 값은 대부분 겹치지 않는다. 심지어 입력 타입도 다르다. accessibilityTraits는 트레이트 배열이나 단일 트레이트를 전달할 수 있지만, accessibilityComponentType는 단일 값만 허용한다.
  2. Android의 기능이 매우 제한적이다. 기존 프로퍼티를 사용할 때 TalkBack이 인식할 수 있는 UI 엘리먼트는 "button", "radiobutton_checked", "radiobutton_unchecked" 뿐이었다.

문제 2: 접근성 힌트 부재

접근성 힌트는 TalkBack이나 VoiceOver를 사용하는 사용자가 접근성 엘리먼트에 액션을 수행할 때 어떤 일이 일어날지 이해하는 데 도움을 준다. 이러한 힌트는 설정 패널에서 켜거나 끌 수 있다. 이전에는 React Native의 API가 접근성 힌트를 전혀 지원하지 않았다.

문제 3: 반전 색상 무시하기

시각 장애를 가진 일부 사용자는 화면 대비를 높이기 위해 휴대폰에서 색상을 반전시켜 사용한다. 애플은 iOS에서 특정 뷰를 무시할 수 있는 API를 제공한다. 이를 통해 사용자가 반전 색상 설정을 켜도 이미지와 비디오가 왜곡되지 않는다. 그러나 이 API는 현재 React Native에서 지원되지 않는다.

새로운 API 설계

첫 번째 해결책: accessibilityComponentType (안드로이드)와 accessibilityTraits (iOS) 통합

accessibilityComponentTypeaccessibilityTraits 사이의 혼란을 해결하기 위해, 두 속성을 단일 속성으로 통합하기로 결정했다. 이는 두 속성이 기술적으로 동일한 기능을 목표로 했기 때문에 합리적이었다. 이를 통해 개발자는 접근성 기능을 구현할 때 플랫폼별 세부 사항을 고민할 필요가 없어졌다.

배경

iOS에서는 UIAccessibilityTraits를 모든 NSObject에 설정할 수 있는 속성이다. 자바스크립트 속성을 통해 전달된 17개의 트레이트는 Objective-C의 UIAccessibilityTraits 요소로 매핑된다. 각 트레이트는 long int로 표현되며, 설정된 모든 트레이트는 OR 연산으로 결합된다.

반면 안드로이드에서 AccessibilityComponentType는 React Native에서 만든 개념으로, 안드로이드의 속성과 직접적으로 매핑되지 않는다. 접근성은 접근성 델리게이트를 통해 처리된다. 각 뷰는 기본 접근성 델리게이트를 가지고 있다. 접근성 액션을 커스텀하려면 새로운 접근성 델리게이트를 생성하고, 커스텀하려는 특정 메서드를 오버라이드한 후, 해당 뷰의 접근성 델리게이트를 새 델리게이트로 설정해야 한다. 개발자가 AccessibilityComponentType를 설정하면, 네이티브 코드는 전달된 컴포넌트를 기반으로 새로운 델리게이트를 생성하고, 뷰에 해당 접근성 델리게이트를 설정한다.

변경 사항

새로운 속성을 위해 두 속성의 상위 집합을 만들기로 했다. 기존 속성인 accessibilityTraits를 주로 모델로 삼기로 결정했는데, 이는 accessibilityTraits가 훨씬 더 많은 값을 가지고 있기 때문이다. 안드로이드에서 이 트레이트의 기능은 접근성 델리게이트를 수정함으로써 폴리필로 구현된다.

accessibilityTraits는 iOS에서 설정할 수 있는 17개의 UIAccessibilityTraits 값을 가지고 있다. 하지만 이 중 일부는 새로운 속성에 포함하지 않았다. 이는 일부 트레이트를 설정했을 때의 효과가 잘 알려져 있지 않고, 많은 값이 실제로 거의 사용되지 않기 때문이다.

UIAccessibilityTraits 값은 일반적으로 두 가지 목적 중 하나를 가진다. UI 엘리먼트의 역할을 설명하거나, UI 엘리먼트의 상태를 설명한다. 우리가 관찰한 이전 속성의 사용 사례 대부분은 일반적으로 하나의 역할을 나타내는 값을 사용하고, 이를 "state selected", "state disabled" 또는 둘 다와 결합했다. 따라서 두 개의 새로운 접근성 속성을 만들기로 결정했다: accessibilityRoleaccessibilityState.

accessibilityRole

새로운 속성인 accessibilityRole은 Talkback이나 Voiceover에 UI 엘리먼트의 역할을 알려준다. 이 속성은 다음 값 중 하나를 가질 수 있다:

  • none
  • button
  • link
  • search
  • image
  • keyboardkey
  • text
  • adjustable
  • header
  • summary
  • imagebutton

이 속성은 하나의 값만 전달할 수 있다. 일반적으로 UI 엘리먼트는 논리적으로 이 중 하나 이상의 역할을 가지지 않기 때문이다. 예외는 이미지와 버튼이므로, 두 가지를 결합한 imagebutton 역할을 추가했다.

accessibilityStates

새로운 속성인 accessibilityStates는 Talkback이나 Voiceover에 UI 엘리먼트의 상태를 알려준다. 이 속성은 다음 값 중 하나 또는 둘 다를 포함하는 배열을 가진다:

  • selected
  • disabled

두 번째 해결책: 접근성 힌트 추가

이를 위해 accessibilityHint라는 새로운 속성을 추가했다. 이 속성을 설정하면 Talkback이나 Voiceover가 사용자에게 힌트를 읽어준다.

accessibilityHint

이 속성은 String 형태로 읽을 접근성 힌트를 받는다.

iOS에서는 이 속성을 설정하면 뷰의 해당 네이티브 속성인 AccessibilityHint가 설정된다. 이후 iPhone에서 접근성 힌트가 켜져 있으면 Voiceover가 이 힌트를 읽는다.

Android에서는 이 속성을 설정하면 힌트 값이 접근성 라벨 끝에 추가된다. 이 구현의 장점은 iOS의 힌트 동작을 모방한다는 점이지만, 단점은 iOS처럼 설정에서 이 힌트를 끌 수 없다는 것이다.

Android에서 이렇게 결정한 이유는 일반적으로 접근성 힌트가 특정 동작(예: 클릭)에 해당하기 때문이며, 플랫폼 간 동작을 일관되게 유지하고 싶었다.

문제 3의 해결책

accessibilityIgnoresInvertColors

애플의 AccessibilityIgnoresInvertColors API를 자바스크립트로 노출했다. 이제 색상 반전을 원하지 않는 뷰(예: 이미지)에서 이 속성을 true로 설정하면 색상이 반전되지 않는다.

새로운 사용법

이 새로운 속성들은 React Native 0.57 버전부터 사용할 수 있다.

업그레이드 방법

현재 accessibilityComponentTypeaccessibilityTraits를 사용하고 있다면, 새로운 속성으로 업그레이드하는 방법을 단계별로 안내한다.

1. jscodeshift 사용하기

가장 간단한 사용 사례는 jscodeshift 스크립트를 실행하여 해결할 수 있다.

스크립트는 다음 인스턴스를 대체한다:

accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}

다음과 같이 변경한다:

accessibilityRole= “trait”

또한 이 스크립트는 AccessibilityComponentType 인스턴스를 제거한다. (AccessibilityComponentType을 설정한 모든 곳에서 AccessibilityTraits도 함께 설정했다고 가정한다.)

2. 수동 코드 변환 사용하기

AccessibilityTraits를 사용했지만 AccessibilityRole에 해당하는 값이 없는 경우, 그리고 AccessibilityTraits에 여러 특성이 전달된 경우에는 수동으로 코드를 변환해야 한다.

일반적으로,

accessibilityTraits= {["button", "selected"]}

위 코드를 아래와 같이 수동으로 교체한다.

accessibilityRole="button"
accessibilityStates={["selected"]}

이러한 속성들은 이미 Facebook 코드베이스에서 사용되고 있다. Facebook의 코드 변환은 놀랍도록 간단했다. jscodeshift 스크립트가 약 절반의 인스턴스를 수정했고, 나머지 절반은 수동으로 수정했다. 전체 과정은 몇 시간도 걸리지 않았다.

업데이트된 API가 유용하게 쓰이길 바란다! 그리고 계속해서 앱을 접근 가능하게 만들길 바란다! #포용성

State of React Native 2018

· 10 min read
Sophie Alpert
Engineering Manager on React at Facebook

React Native의 최신 상태에 대해 업데이트를 공유한 지 꽤 시간이 지났다.

Facebook에서는 이전보다 더 많은 프로젝트에서 React Native를 사용하고 있다. 가장 인기 있는 제품 중 하나는 매월 8억 명이 사용하는 앱의 최상위 탭 중 하나인 Marketplace다. 2015년에 생성된 이후, Marketplace는 앱의 다양한 부분에 걸쳐 100개가 넘는 전체 화면 뷰를 포함해 모두 React Native로 구축되었다.

또한 앱의 새로운 부분에서도 React Native를 활용하고 있다. 지난달 F8 키노트를 본 사람이라면 Blood Donations, Crisis Response, Privacy Shortcuts, Wellness Checks 등을 기억할 것이다. 이 모든 최신 기능이 React Native로 만들어졌다. 메인 Facebook 앱 외부 프로젝트에서도 React Native를 사용한다. 새로운 Oculus Go VR 헤드셋에는 React Native로 완전히 구축된 컴패니언 모바일 앱이 포함되어 있으며, 헤드셋 자체에서도 React VR이 다양한 경험을 제공한다.

당연히 앱을 구축하기 위해 다른 기술들도 많이 사용한다. LithoComponentKit는 앱에서 광범위하게 사용하는 두 라이브러리다. 둘 다 네이티브 화면을 구축하기 위해 React와 유사한 컴포넌트 API를 제공한다. React Native가 다른 모든 기술을 대체하는 것이 목표는 아니었다. 우리는 React Native 자체를 개선하는 데 집중하지만, 다른 팀들이 React Native의 아이디어를 차용해 즉시 리로드와 같은 기능을 비자바스크립트 코드에도 적용하는 것을 보는 것을 좋아한다.

아키텍처

2013년 React Native 프로젝트를 시작할 때, 우리는 JavaScript와 네이티브 사이에 단일 "브릿지"를 설계했다. 이 브릿지는 비동기적이고 직렬화 가능하며 일괄 처리되는 특징을 가졌다. React DOM이 React 상태 업데이트를 document.createElement(attrs).appendChild()와 같은 DOM API에 대한 명령형 호출로 변환하는 것처럼, React Native는 [["createView", attrs], ["manageChildren", ...]]과 같이 수행할 뮤테이션 목록을 담은 단일 JSON 메시지를 반환하도록 설계했다. 우리는 이 시스템이 동기 응답을 받는 데 의존하지 않도록 설계했고, 목록에 있는 모든 것이 JSON으로 완전히 직렬화될 수 있도록 했다. 이는 우리에게 유연성을 제공하기 위한 선택이었다. 이 아키텍처 위에서, WebSocket 연결을 통해 모든 JavaScript 코드를 비동기적으로 실행하는 Chrome 디버깅과 같은 도구를 구축할 수 있었다.

지난 5년 동안, 이러한 초기 설계 원칙이 일부 기능 구현을 어렵게 만든다는 사실을 발견했다. 비동기 브릿지는 JavaScript 로직을 동기 응답을 기대하는 많은 네이티브 API와 직접 통합할 수 없게 한다. 네이티브 호출을 큐에 넣는 일괄 처리 브릿지는 React Native 앱이 네이티브로 구현된 함수를 호출하기 어렵게 만든다. 직렬화 가능한 브릿지는 두 세계 사이에서 메모리를 직접 공유하는 대신 불필요한 복사를 유발한다. React Native로 완전히 구축된 앱의 경우, 이러한 제약은 대체로 감내할 만하다. 하지만 React Native와 기존 앱 코드 간의 복잡한 통합이 필요한 앱에서는 불편함을 초래한다.

우리는 React Native를 대규모로 재구성하여 프레임워크를 더 유연하게 만들고, 하이브리드 JavaScript/네이티브 앱에서 네이티브 인프라와 더 잘 통합할 수 있도록 하고 있다. 이 프로젝트를 통해 지난 5년간 배운 교훈을 적용하고, 점진적으로 더 현대적인 아키텍처로 전환할 계획이다. React Native의 내부를 대부분 재작성하고 있지만, 대부분의 변경 사항은 내부적으로만 이루어진다. 기존 React Native 앱은 거의 또는 전혀 변경 없이 계속 작동할 것이다.

React Native를 더 가볍게 만들고 기존 네이티브 앱에 더 잘 통합하기 위해, 이 재구성은 세 가지 주요 내부 변경을 포함한다. 첫째, 스레딩 모델을 변경한다. 각 UI 업데이트가 세 개의 다른 스레드에서 작업을 수행해야 하는 대신, 높은 우선순위 업데이트를 위해 모든 스레드에서 JavaScript를 동기적으로 호출할 수 있게 하면서도, 낮은 우선순위 작업은 메인 스레드에서 벗어나 반응성을 유지할 수 있도록 한다. 둘째, 비동기 렌더링 기능을 React Native에 통합하여 여러 렌더링 우선순위를 허용하고 비동기 데이터 처리를 단순화한다. 마지막으로, 브릿지를 더 빠르고 가볍게 단순화한다. 네이티브와 JavaScript 간의 직접 호출은 더 효율적이며, 크로스 언어 스택 트레이스와 같은 디버깅 도구를 더 쉽게 구축할 수 있게 한다.

이러한 변경이 완료되면, 더 긴밀한 통합이 가능해진다. 현재는 복잡한 해킹 없이는 네이티브 네비게이션 및 제스처 처리나 UICollectionView, RecyclerView와 같은 네이티브 컴포넌트를 통합할 수 없다. 스레딩 모델 변경 후에는 이러한 기능을 쉽게 구축할 수 있을 것이다.

이 작업이 완료에 가까워지면 올해 안에 더 자세한 내용을 공개할 예정이다.

커뮤니티

페이스북 내부 커뮤니티와 함께, 우리는 페이스북 외부에서도 활발한 React Native 사용자와 기여자들을 보유하고 있다는 점을 자랑스럽게 생각한다. 우리는 React Native 사용자에게 더 나은 서비스를 제공하고, 프로젝트에 기여하기 쉽게 만드는 방식으로 React Native 커뮤니티를 더욱 지원하고 싶다.

우리의 아키텍처 변경이 React Native가 다른 네이티브 인프라와 더 깔끔하게 상호작용하도록 돕는 것처럼, React Native는 JavaScript 생태계에 더 잘 맞도록 JavaScript 측면에서 더 가벼워져야 한다. 이는 VM과 번들러를 교체 가능하게 만드는 것을 포함한다. 우리는 주요 변경 사항의 속도가 따라가기 어려울 수 있다는 것을 알고 있으므로, 주요 릴리즈를 줄이는 방법을 모색하고 싶다. 마지막으로, 일부 팀이 시작 최적화와 같은 주제에서 더 철저한 문서를 원한다는 것을 알고 있으며, 우리의 전문 지식이 아직 문서화되지 않은 부분이 있다. 이러한 변화들이 올해 중에 이루어질 것으로 기대해도 좋다.

React Native를 사용한다면, 여러분은 우리 커뮤니티의 일원이다. React Native를 더 나은 도구로 만들기 위해 우리에게 계속 의견을 알려주길 바란다.

React Native는 모바일 개발자의 도구 상자 중 하나일 뿐이지만, 우리는 이 도구를 강력히 믿고 있다. 그리고 우리는 지난 한 해 동안 500명 이상의 기여자가 2500개 이상의 커밋을 통해 매일 더 나은 도구로 만들고 있다.

React Native에서 TypeScript 사용하기

· 13 min read
Ash Furrow
Artsy 소프트웨어 엔지니어

JavaScript! 모두가 좋아하는 언어다. 하지만 어떤 이들은 타입도 좋아한다. 다행히 JavaScript에 강력한 타입을 추가할 수 있는 옵션이 있다. 필자는 TypeScript를 선호하지만, React Native는 기본적으로 Flow를 지원한다. 어떤 것을 선택할지는 취향에 달렸다. 각각 JavaScript에 타입을 추가하는 방식이 다르기 때문이다. 오늘은 React Native 앱에서 TypeScript를 사용하는 방법을 살펴본다.

이 글은 Microsoft의 TypeScript-React-Native-Starter 저장소를 가이드로 삼는다.

업데이트: 이 블로그 글이 작성된 이후로 상황이 더 간단해졌다. 이 글에서 설명하는 모든 설정을 단 하나의 커맨드로 대체할 수 있다:

npx react-native init MyAwesomeProject --template react-native-template-typescript

하지만 Babel의 TypeScript 지원에는 몇 가지 제한 사항이 있다. 위 블로그 글에서 자세히 다룬다. 이 글에서 설명한 단계도 여전히 유효하며, Artsy는 여전히 react-native-typescript-transformer를 프로덕션에서 사용하고 있다. 하지만 React Native와 TypeScript를 가장 빠르게 시작하려면 위 커맨드를 사용하면 된다. 나중에 필요하면 언제든지 전환할 수 있다.

어쨌든, 즐겁게 진행해보자! 원본 블로그 글은 아래에서 계속된다.

사전 준비

여러분이 다양한 플랫폼에서 개발하거나 여러 타입의 디바이스를 대상으로 작업할 수 있기 때문에 기본 설정이 복잡할 수 있다. 먼저 TypeScript 없이 일반 React Native 앱을 실행할 수 있는지 확인해야 한다. React Native 공식 웹사이트의 시작하기 가이드를 따라 기본 설정을 완료한다. 디바이스나 에뮬레이터에 앱을 배포할 수 있게 되면, TypeScript를 사용한 React Native 앱 개발을 시작할 준비가 된 것이다.

또한 Node.js, npm, 그리고 Yarn이 필요하다.

초기화

일반적인 React Native 프로젝트를 스캐폴딩해본 후, TypeScript를 추가할 준비가 되었다. 바로 시작해보자.

react-native init MyAwesomeProject
cd MyAwesomeProject

TypeScript 추가하기

다음 단계는 프로젝트에 TypeScript를 추가하는 것이다. 아래 명령어들은 다음과 같은 작업을 수행한다:

  • 프로젝트에 TypeScript를 추가한다.
  • 프로젝트에 React Native TypeScript Transformer를 추가한다.
  • 빈 TypeScript 설정 파일을 초기화한다. 이 파일은 다음 단계에서 설정할 것이다.
  • 빈 React Native TypeScript Transformer 설정 파일을 추가한다. 이 파일도 다음 단계에서 설정할 것이다.
  • React와 React Native를 위한 타입 정의를 추가한다.

이제 아래 명령어를 실행해 보자.

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

tsconfig.json 파일은 TypeScript 컴파일러의 모든 설정을 포함한다. 위 명령어로 생성된 기본값은 대부분 적합하지만, 파일을 열고 다음 줄의 주석을 해제한다.

{
/* 설정 파일에서 다음 줄을 찾아 주석을 해제한다. */
// "allowSyntheticDefaultImports": true, /* 기본 내보내기가 없는 모듈에서 기본 import를 허용한다. 이 설정은 코드 생성에는 영향을 미치지 않고, 타입 검사에만 영향을 준다. */
}

rn-cli.config.js 파일은 React Native TypeScript Transformer의 설정을 포함한다. 파일을 열고 다음 내용을 추가한다.

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

타입스크립트로 마이그레이션

생성된 App.js__tests_/App.js 파일을 App.tsx로 이름을 변경한다. index.js.js 확장자를 그대로 유지한다. 모든 새로운 파일은 .tsx 확장자를 사용한다. (JSX를 포함하지 않는 파일은 .ts 확장자를 사용한다.)

이 상태에서 앱을 실행하면 object prototype may only be an object or null과 같은 오류가 발생할 수 있다. 이 오류는 React의 기본 내보내기와 명명된 내보내기를 동일한 줄에서 가져오지 못해 발생한다. App.tsx 파일을 열고 상단의 import 문을 다음과 같이 수정한다:

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

이 문제는 Babel과 타입스크립트가 CommonJS 모듈을 처리하는 방식의 차이에서 비롯된다. 향후 두 도구는 동일한 동작 방식으로 안정화될 것이다.

이제 React Native 앱을 실행할 수 있어야 한다.

타입스크립트 테스트 환경 설정

React Native는 기본적으로 Jest를 제공한다. 따라서 React Native 앱을 타입스크립트로 테스트하려면 devDependenciests-jest를 추가해야 한다.

yarn add --dev ts-jest

그런 다음 package.json 파일을 열고 jest 필드를 다음과 같이 교체한다:

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

이 설정은 Jest가 .ts.tsx 파일을 ts-jest로 실행하도록 구성한다.

의존성 타입 선언 설치하기

TypeScript에서 최상의 경험을 얻으려면 타입 체커가 의존성의 구조와 API를 이해할 수 있도록 해야 한다. 일부 라이브러리는 .d.ts 파일(타입 선언/타입 정의 파일)을 패키지와 함께 배포한다. 이 파일은 기본 JavaScript의 구조를 설명한다. 다른 라이브러리의 경우, @types/ npm 범위에서 적절한 패키지를 명시적으로 설치해야 한다.

예를 들어, Jest, React, React Native, React Test Renderer에 대한 타입이 필요하다.

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

이 선언 파일 패키지를 dev 의존성으로 저장했다. 이는 React Native 이 런타임이 아닌 개발 중에만 이 의존성을 사용하기 때문이다. NPM에 라이브러리를 배포한다면, 일부 타입 의존성을 일반 의존성으로 추가해야 할 수도 있다.

.d.ts 파일에 대해 더 자세히 알아보려면 여기를 참고한다.

더 많은 파일 무시하기

소스 컨트롤을 위해 .jest 폴더를 무시하고 싶을 것이다. git을 사용한다면 .gitignore 파일에 항목을 추가하면 된다.

# Jest
#
.jest/

체크포인트로, 파일을 버전 컨트롤에 커밋하는 것을 고려해 보자.

git init
git add .gitignore # 파일을 무시하기 위해 먼저 이 작업을 수행하는 것이 중요하다
git add .
git commit -am "Initial commit."

컴포넌트 추가하기

앱에 새로운 컴포넌트를 추가해 보자. Hello.tsx 컴포넌트를 만들어 보자. 이 컴포넌트는 실제 앱에서 사용할 만한 것은 아니지만, React Native에서 TypeScript를 어떻게 사용하는지 보여주는 교육용 예제이다.

components 디렉토리를 만들고 다음 예제를 추가해 보자.

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'조금 더 열정을 보여주세요. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="감소"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="증가"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// 스타일
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

이 코드는 상당히 길지만, 주요 부분을 살펴보자:

  • HTML 엘리먼트인 div, span, h1 등을 렌더링하는 대신, ViewButton 같은 네이티브 컴포넌트를 사용한다. 이 컴포넌트들은 다양한 플랫폼에서 동작한다.
  • 스타일은 React Native가 제공하는 StyleSheet.create 함수를 사용해 지정한다. React의 스타일시트는 플렉스 박스를 통해 레이아웃을 제어하고, CSS와 유사한 구문으로 스타일을 적용할 수 있다.

컴포넌트 테스트 추가하기

이제 컴포넌트를 만들었으니 테스트를 작성해 보자.

이미 테스트 러너로 Jest를 설치했다. 컴포넌트에 대한 스냅샷 테스트를 작성할 것이므로, 필요한 애드온을 추가한다:

yarn add --dev react-addons-test-utils

이제 components 디렉토리에 __tests__ 폴더를 만들고 Hello.tsx에 대한 테스트를 추가한다:

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('기본값으로 정상 렌더링된다', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

테스트를 처음 실행하면 렌더링된 컴포넌트의 스냅샷을 생성하고 components/__tests__/__snapshots__/Hello.tsx.snap 파일에 저장한다. 컴포넌트를 수정할 때는 스냅샷을 업데이트하고 의도치 않은 변경 사항을 검토해야 한다. React Native 컴포넌트 테스트에 대해 더 자세히 알고 싶다면 여기를 참고한다.

다음 단계

공식 React 튜토리얼과 상태 관리 라이브러리 Redux를 확인해 보자. 이 리소스들은 React Native 앱을 개발할 때 유용하다. 또한 ReactXP도 살펴볼 만하다. ReactXP는 TypeScript로 작성된 컴포넌트 라이브러리로, 웹용 React와 React Native를 모두 지원한다.

더 안전한 타입 시스템을 활용한 React Native 개발 환경에서 즐거운 코딩을 해보자!

React Native에서 <InputAccessoryView> 구축하기

· 12 min read
Peter Argany
페이스북 소프트웨어 엔지니어

배경

3년 전, React Native에서 입력 액세서리 뷰를 지원해 달라는 GitHub 이슈가 올라왔다.

그 후 몇 년 동안 수많은 '+1' 투표와 다양한 해결 방법이 제안되었지만, 이 문제와 관련해 React Native에는 구체적인 변화가 없었다. 오늘부터 iOS를 시작으로 네이티브 입력 액세서리 뷰에 접근할 수 있는 API를 공개한다. 이 API를 어떻게 만들었는지 여러분과 공유하게 되어 기쁘다.

배경

입력 액세서리 뷰란 무엇일까? Apple 개발자 문서를 살펴보면, 리스폰더가 퍼스트 리스폰더가 될 때 시스템 키보드 상단에 고정할 수 있는 커스텀 뷰라는 것을 알 수 있다. UIResponder를 상속받는 모든 객체는 .inputAccessoryView 프로퍼티를 읽기-쓰기로 재선언하고, 여기에 커스텀 뷰를 관리할 수 있다. 리스폰더 인프라는 이 뷰를 마운트하고, 시스템 키보드와 동기화를 유지한다. 드래그나 탭과 같이 키보드를 닫는 제스처는 프레임워크 레벨에서 입력 액세서리 뷰에 적용된다. 이를 통해 키보드 닫기와 상호작용이 가능한 콘텐츠를 만들 수 있으며, iMessage나 WhatsApp 같은 최상위 메시징 앱에서 필수적인 기능이다.

키보드 상단에 뷰를 고정하는 두 가지 일반적인 사용 사례가 있다. 첫 번째는 Facebook 컴포저의 배경 선택기와 같은 키보드 툴바를 만드는 것이다.

이 시나리오에서는 키보드가 텍스트 입력 필드에 포커스되고, 입력 액세서리 뷰는 추가 키보드 기능을 제공하는 데 사용된다. 이 기능은 입력 필드의 타입에 따라 달라진다. 지도 애플리케이션에서는 주소 제안이 될 수 있고, 텍스트 편집기에서는 서식 지정 도구가 될 수 있다.


이 시나리오에서 <InputAccessoryView>를 소유하는 Objective-C UIResponder는 명확해야 한다. <TextInput>이 퍼스트 리스폰더가 되면, 내부적으로 UITextView 또는 UITextField의 인스턴스가 된다.

두 번째 일반적인 시나리오는 스틱키 고정 텍스트 입력이다:

여기서 텍스트 입력은 실제로 입력 액세서리 뷰 자체의 일부이다. 이는 메시징 애플리케이션에서 일반적으로 사용되며, 이전 메시지 스레드를 스크롤하면서 메시지를 작성할 수 있다.


이 예제에서 <InputAccessoryView>를 소유하는 것은 누구일까? 다시 UITextViewUITextField가 될 수 있을까? 텍스트 입력이 입력 액세서리 뷰 안에 있으므로, 이는 순환 의존성처럼 들린다. 이 문제를 해결하는 것만으로도 별도의 블로그 포스트가 있다. 스포일러: 소유자는 일반적인 UIView 서브클래스이며, 수동으로 becomeFirstResponder를 호출한다.

API 설계

이제 <InputAccessoryView>가 무엇인지, 그리고 어떻게 사용할지 이해했다. 다음 단계는 두 가지 사용 사례에 적합하고, <TextInput>과 같은 기존 React Native 컴포넌트와 잘 작동하는 API를 설계하는 것이다.

키보드 툴바를 설계할 때 고려해야 할 사항은 다음과 같다:

  1. 일반적인 React Native 뷰 계층 구조를 <InputAccessoryView>로 끌어올릴 수 있어야 한다.
  2. 이 일반적이고 분리된 뷰 계층 구조가 터치를 받아들이고 애플리케이션 상태를 조작할 수 있어야 한다.
  3. 특정 <TextInput><InputAccessoryView>를 연결할 수 있어야 한다.
  4. 코드를 중복하지 않고 여러 텍스트 입력 필드 간에 <InputAccessoryView>를 공유할 수 있어야 한다.

첫 번째 요구사항은 React 포털과 유사한 개념을 사용해 달성할 수 있다. 이 설계에서 React Native 뷰를 응답자 인프라가 관리하는 UIView 계층 구조로 포털한다. React Native 뷰는 UIView로 렌더링되기 때문에 이 작업은 매우 간단하다. 다음과 같은 메서드를 재정의하면 된다:

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex

그리고 모든 하위 뷰를 새로운 UIView 계층 구조로 연결한다. 두 번째 요구사항을 위해 <InputAccessoryView>에 새로운 RCTTouchHandler를 설정한다. 상태 업데이트는 일반적인 이벤트 콜백을 사용해 수행한다. 세 번째와 네 번째 요구사항을 위해 <TextInput> 컴포넌트 생성 시 네이티브 코드에서 액세서리 뷰 UIView 계층 구조를 찾기 위해 nativeID 필드를 사용한다. 이 함수는 기본 네이티브 텍스트 입력의 .inputAccessoryView 속성을 사용한다. 이를 통해 <InputAccessoryView><TextInput>을 ObjC 구현에서 효과적으로 연결한다.

스크롤 가능한 텍스트 입력(시나리오 2)을 지원하려면 몇 가지 제약 조건이 추가된다. 이 설계에서 입력 액세서리 뷰는 텍스트 입력을 하위 요소로 가지므로 nativeID를 통해 연결할 수 없다. 대신, 일반적인 오프스크린 UIView.inputAccessoryView를 네이티브 <InputAccessoryView> 계층 구조로 설정한다. 이 일반적인 UIView를 수동으로 첫 번째 응답자로 지정하면, 응답자 인프라에 의해 계층 구조가 마운트된다. 이 개념은 앞서 언급한 블로그 포스트에서 자세히 설명한다.

문제점과 해결 과정

이 API를 구축하는 과정에서 모든 것이 순조롭게 진행되지는 않았다. 여기서는 우리가 마주친 몇 가지 문제점과 그 해결 방법을 설명한다.

이 API를 구축하기 위한 초기 아이디어는 NSNotificationCenter를 사용해 UIKeyboardWill(Show/Hide/ChangeFrame) 이벤트를 감지하는 것이었다. 이 패턴은 일부 오픈소스 라이브러리와 Facebook 앱의 일부에서 사용되고 있다. 그러나 UIKeyboardDidChangeFrame 이벤트는 스와이프 동작 시 <InputAccessoryView>의 프레임을 업데이트하기에 충분히 빠르게 호출되지 않았다. 또한, 키보드 높이 변화도 이러한 이벤트로 캡처할 수 없었다. 이로 인해 다음과 같은 버그가 발생했다:

iPhone X에서는 텍스트 키보드와 이모티콘 키보드의 높이가 다르다. 키보드 이벤트를 사용해 텍스트 입력 프레임을 조작하는 대부분의 애플리케이션은 위 버그를 해결해야 했다. 우리의 해결책은 .inputAccessoryView 프로퍼티를 사용하는 것이었다. 이렇게 하면 응답자 인프라가 프레임 업데이트를 처리할 수 있다.


우리가 마주친 또 다른 까다로운 버그는 iPhone X의 홈 바를 피하는 것이었다. "Apple이 safeAreaLayoutGuide를 개발한 이유가 바로 이것인데, 이건 간단한 문제야!"라고 생각할 수도 있다. 우리도 그렇게 생각했다. 첫 번째 문제는 네이티브 <InputAccessoryView> 구현이 나타나기 직전까지 앵커링할 윈도우가 없다는 것이다. 이 문제는 -(BOOL)becomeFirstResponder를 재정의하고 레이아웃 제약을 강제하는 것으로 해결할 수 있다. 이러한 제약을 준수하면 액세서리 뷰가 위로 올라가지만, 또 다른 버그가 발생한다:

입력 액세서리 뷰는 홈 바를 성공적으로 피하지만, 이제 안전하지 않은 영역 뒤의 콘텐츠가 보인다. 이 문제는 radar에서 해결책을 찾을 수 있었다. 나는 네이티브 <InputAccessoryView> 계층을 safeAreaLayoutGuide 제약을 따르지 않는 컨테이너로 감쌌다. 네이티브 컨테이너는 안전하지 않은 영역의 콘텐츠를 덮고, <InputAccessoryView>는 안전 영역 경계 내에 머무르게 된다.


예제 사용법

다음은 <TextInput>의 상태를 초기화하는 키보드 툴바 버튼을 만드는 예제이다.

class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}

render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}

스틱키 고정 텍스트 입력의 다른 예제는 저장소에서 확인할 수 있다.

이 기능을 언제 사용할 수 있나요?

이 기능 구현에 대한 전체 커밋은 여기에서 확인할 수 있습니다. <InputAccessoryView>는 다음 v0.55.0 릴리스에서 사용 가능합니다.

즐거운 코딩 되세요 :)

React Native와 AWS 함께 사용하기

· 17 min read
Richard Threlkeld
AWS 모바일 부문 시니어 기술 제품 매니저

AWS는 클라우드 서비스 제공자로 기술 업계에서 널리 알려져 있다. 컴퓨팅, 스토리지, 데이터베이스 기술부터 완전 관리형 서버리스 서비스까지 다양한 제품을 제공한다. AWS 모바일 팀은 클라우드와 연결된 모바일 및 웹 애플리케이션을 더 안전하고 확장 가능하며 개발과 배포가 쉬워지도록 고객과 자바스크립트 생태계 구성원들과 긴밀히 협력해 왔다. 완전한 스타터 키트로 시작했지만, 최근에는 몇 가지 새로운 발전이 있었다.

이 블로그 포스트는 React와 React Native 개발자에게 흥미로운 내용을 다룬다:

  • AWS Amplify: 클라우드 서비스를 사용하는 자바스크립트 애플리케이션을 위한 선언적 라이브러리
  • AWS AppSync: 오프라인 및 실시간 기능을 갖춘 완전 관리형 GraphQL 서비스

AWS Amplify

React Native 애플리케이션은 Create React Native App이나 Expo 같은 도구를 사용해 쉽게 시작할 수 있다. 하지만 클라우드와 연결하려면 사용 사례에 맞는 인프라 서비스를 선택하는 과정이 복잡할 수 있다. 예를 들어, React Native 앱에서 사진을 업로드해야 한다고 가정해보자. 이를 사용자별로 보호해야 할까? 그렇다면 회원가입이나 로그인 프로세스가 필요할 것이다. 자체 사용자 디렉토리를 사용할지, 소셜 미디어 프로바이더를 사용할지도 고려해야 한다. 사용자가 로그인한 후 커스텀 비즈니스 로직을 포함한 API를 호출해야 할 수도 있다.

이런 문제를 해결하기 위해 AWS Amplify라는 라이브러리를 출시했다. 이 라이브러리는 AWS 특정 구현보다는 작업의 "카테고리"로 설계되었다. 예를 들어, 사용자가 회원가입하고 로그인한 후 개인 사진을 업로드해야 한다면, AuthStorage 카테고리를 애플리케이션에 추가하면 된다:

import { Auth } from 'aws-amplify';

Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err));

Auth.confirmSignIn(user, code)
.then(data => console.log(data))
.catch(err => console.log(err));

위 코드에서 Amplify가 도와주는 일반적인 작업의 예를 볼 수 있다. 이메일이나 SMS를 사용한 다중 인증(MFA) 코드 사용이 그 예이다. 현재 지원되는 카테고리는 다음과 같다:

  • Auth: 자격 증명 자동화를 제공한다. 기본 구현은 AWS 자격 증명을 사용해 서명하고, Amazon Cognito에서 OIDC JWT 토큰을 가져온다. MFA 기능 같은 일반적인 기능도 지원한다.
  • Analytics: 한 줄의 코드로 Amazon Pinpoint에서 인증된 사용자나 비인증 사용자를 추적할 수 있다. 필요에 따라 커스텀 메트릭이나 속성으로 확장할 수 있다.
  • API: AWS Signature Version 4을 활용해 RESTful API와 안전하게 상호작용한다. 이 API 모듈은 Amazon API Gateway를 사용한 서버리스 인프라에서 유용하다.
  • Storage: Amazon S3에 콘텐츠를 업로드, 다운로드, 목록화하는 간단한 커맨드를 제공한다. 사용자별로 데이터를 공개 또는 비공개로 쉽게 그룹화할 수 있다.
  • Caching: 웹 앱과 React Native 전반에서 사용할 수 있는 LRU 캐시 인터페이스를 제공하며, 구현별 지속성을 사용한다.
  • i18n and Logging: 국제화 및 지역화 기능, 디버깅 및 로깅 기능을 제공한다.

Amplify의 장점 중 하나는 특정 프로그래밍 환경에 맞는 "모범 사례"를 설계에 반영했다는 점이다. 예를 들어, React Native 개발자와 고객과 협력하면서 발견한 문제 중 하나는 빠르게 작동시키기 위해 개발 중에 취한 단축키가 프로덕션 스택으로 이어지는 경우가 많다는 것이다. 이는 확장성이나 보안을 저해할 수 있으며, 인프라 재구축과 코드 리팩토링을 강제할 수 있다.

이를 피하기 위해 도움을 주는 한 가지 예는 AWS Lambda를 사용한 서버리스 참조 아키텍처이다. 이는 백엔드를 구축할 때 Amazon API Gateway와 AWS Lambda를 함께 사용하는 모범 사례를 보여준다. 이 패턴은 Amplify의 API 카테고리에 반영되어 있다. 이 패턴을 사용해 여러 REST 엔드포인트와 상호작용하고, 커스텀 비즈니스 로직을 위해 헤더를 Lambda 함수까지 전달할 수 있다. 또한 AWS Mobile CLI를 출시해 새로운 React Native 프로젝트나 기존 프로젝트를 이 기능으로 시작할 수 있게 했다. 시작하려면 npm을 통해 설치하고, 설정 프롬프트를 따르면 된다:

npm install --global awsmobile-cli
awsmobile configure

모바일 생태계에 특화된 또 다른 모범 사례는 비밀번호 보안이다. 기본 Auth 카테고리 구현은 Amazon Cognito 사용자 풀을 사용해 사용자 등록과 로그인을 처리한다. 이 서비스는 Secure Remote Password 프로토콜을 구현해 인증 시도 중 사용자를 보호한다. 프로토콜의 수학적 원리를 읽어보면, 원시 루트를 통해 그룹을 생성할 때 큰 소수를 사용해야 한다는 것을 알 수 있다. React Native 환경에서는 JIT가 비활성화되어 있어, 이런 보안 작업을 위한 BigInteger 계산이 덜 효율적이다. 이를 고려해 Android와 iOS용 네이티브 브릿지를 출시했으며, 프로젝트 내에서 연결할 수 있다:

npm install --save aws-amplify-react-native
react-native link amazon-cognito-identity-js

또한 Expo 팀이 최신 SDK에 이를 포함시켜 Amplify를 사용할 수 있게 되어, eject 없이도 사용할 수 있다.

마지막으로, React Native(및 React) 개발에 특화된 Amplify는 고차 컴포넌트(HOCs)를 포함해 앱에 로그인이나 회원가입 기능을 쉽게 추가할 수 있다:

import Amplify, { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

class App extends React.Component {
...
}

export default withAuthenticator(App);

기본 컴포넌트는 <Authenticator />로도 제공되며, UI를 완전히 커스터마이즈할 수 있다. 또한 사용자 상태를 관리하는 데 필요한 속성(예: 로그인 여부나 MFA 확인 대기 상태)과 상태 변경 시 실행할 콜백도 제공한다.

마찬가지로, 다양한 사용 사례에 사용할 수 있는 일반 React 컴포넌트도 제공한다. 필요에 따라 커스터마이즈할 수 있으며, 예를 들어 Storage 모듈에서 Amazon S3의 모든 비공개 이미지를 표시할 수 있다:

<S3Album
level="private"
path={path}
filter={(item) => /jpg/i.test(item.path)}/>

앞서 보여준 것처럼, 공개 또는 비공개 스토리지 옵션을 통해 컴포넌트 기능을 제어할 수 있다. 특정 UI 컴포넌트와 상호작용할 때 자동으로 분석 데이터를 수집하는 기능도 있다:

return <S3Album track/>

AWS Amplify는 설정보다 규약을 우선하는 개발 스타일을 선호하며, 전역 초기화 루틴이나 카테고리 수준에서 초기화를 수행한다. 시작하는 가장 빠른 방법은 aws-exports 파일을 사용하는 것이다. 하지만 개발자는 기존 리소스와 함께 독립적으로 라이브러리를 사용할 수도 있다.

철학에 대한 깊은 이해와 전체 데모를 보려면 AWS re:Invent의 비디오를 확인해보자.

AWS AppSync

AWS Amplify 출시 직후, AWS AppSync도 공개했다. AWS AppSync는 오프라인과 실시간 기능을 모두 지원하는 완전 관리형 GraphQL 서비스다. 다양한 클라이언트 프로그래밍 언어(네이티브 Android 및 iOS 포함)에서 GraphQL을 사용할 수 있지만, 특히 React Native 개발자들 사이에서 인기가 높다. 이는 데이터 모델이 단방향 데이터 흐름과 컴포넌트 계층 구조에 잘 맞기 때문이다.

AWS AppSync를 사용하면 자신의 AWS 계정에 있는 리소스에 연결할 수 있다. 즉, 데이터를 소유하고 제어할 수 있다. 이는 데이터 소스를 사용해 이루어지며, Amazon DynamoDB, Amazon Elasticsearch, AWS Lambda를 지원한다. 이를 통해 NoSQL과 전체 텍스트 검색과 같은 기능을 단일 GraphQL API로 결합할 수 있다. 데이터 소스를 자유롭게 조합할 수 있다. AppSync 서비스는 스키마에서 프로비저닝할 수도 있다. 따라서 AWS 서비스에 익숙하지 않더라도 GraphQL SDL을 작성하고 버튼을 클릭하기만 하면 자동으로 실행할 수 있다.

AWS AppSync의 실시간 기능은 잘 알려진 이벤트 기반 패턴을 사용한 GraphQL 구독으로 제어된다. AWS AppSync의 구독은 스키마에서 GraphQL 지시어로 제어되며, 스키마는 모든 데이터 소스를 사용할 수 있다. 따라서 Amazon DynamoDB와 Amazon Elasticsearch Service를 통해 데이터베이스 작업에서 알림을 트리거하거나, AWS Lambda를 통해 인프라의 다른 부분에서 알림을 트리거할 수 있다.

AWS Amplify와 유사하게, AWS AppSync를 사용해 GraphQL API에 엔터프라이즈 보안 기능을 적용할 수 있다. 이 서비스는 API 키를 통해 빠르게 시작할 수 있게 해준다. 하지만 프로덕션 환경으로 전환할 때는 AWS Identity and Access Management(IAM) 또는 Amazon Cognito 사용자 풀의 OIDC 토큰을 사용할 수 있다. 타입에 대한 정책을 통해 리졸버 수준에서 접근을 제어할 수 있다. 또한 런타임에 세분화된 접근 제어를 위해 사용자가 특정 데이터베이스 리소스의 소유자인지 확인하는 등의 논리적 검사를 수행할 수 있다. 리졸버 실행 또는 개별 데이터베이스 레코드 접근을 위해 그룹 멤버십을 확인하는 기능도 제공한다.

React Native 개발자가 이 기술을 더 쉽게 배울 수 있도록, AWS AppSync 콘솔 홈페이지에서 내장된 GraphQL 샘플 스키마를 실행할 수 있다. 이 샘플은 GraphQL 스키마를 배포하고, 데이터베이스 테이블을 프로비저닝하며, 쿼리, 뮤테이션, 구독을 자동으로 연결한다. 또한 이 내장 스키마를 활용한 React Native 예제React 예제도 제공된다. 이를 통해 클라이언트와 클라우드 컴포넌트를 몇 분 만에 실행할 수 있다.

AWSAppSyncClient를 사용하면 시작이 간단하다. 이 클라이언트는 Apollo Client에 연결된다. AWSAppSyncClient는 GraphQL API의 보안 및 서명, 오프라인 기능, 구독 핸드셰이크 및 협상 프로세스를 처리한다:

import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from 'aws-appsync-react';
import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link";

const client = new AWSAppSyncClient({
url: awsconfig.graphqlEndpoint,
region: awsconfig.region,
auth: {type: AUTH_TYPE.API_KEY, apiKey: awsconfig.apiKey}
});

AppSync 콘솔은 GraphQL 엔드포인트, AWS 리전, API 키가 포함된 설정 파일을 제공한다. 그런 다음 React Apollo와 함께 클라이언트를 사용할 수 있다:

const WithProvider = () => (
<ApolloProvider client={client}>
<Rehydrated>
<App />
</Rehydrated>
</ApolloProvider>
);

이제 표준 GraphQL 쿼리를 사용할 수 있다:

query ListEvents {
listEvents{
items{
__typename
id
name
where
when
description
comments{
__typename
items{
__typename
eventId
commentId
content
createdAt
}
nextToken
}
}
}
}

위 예제는 AppSync가 프로비저닝한 샘플 앱 스키마를 사용한 쿼리를 보여준다. 이 예제는 DynamoDB와의 상호작용뿐만 아니라 데이터 페이징(암호화된 토큰 포함)과 EventsComments 간의 타입 관계도 보여준다. 앱이 AWSAppSyncClient로 구성되어 있기 때문에 데이터는 자동으로 오프라인에 저장되고, 디바이스가 다시 연결될 때 동기화된다.

이 비디오에서 이 기술의 심층 분석과 React Native 데모를 확인할 수 있다.

피드백

라이브러리를 개발한 팀은 여러분이 이 라이브러리와 서비스를 어떻게 활용하고 있는지 듣고 싶어 한다. 또한 React와 React Native를 클라우드 서비스와 함께 사용할 때 개발을 더 쉽게 만들기 위해 어떤 부분을 개선할 수 있는지 의견을 구하고 있다. AWS 모바일 팀에 연락하려면 AWS Amplify 또는 AWS AppSync GitHub 페이지를 방문하면 된다.