요즘 많은 사람들이 그들의 다음 그들의 다음 모바일 앱 개발에 사용할 플랫폼으로 리액트 네이티브에 다가가고 있다. 이것은 사소한 일이 아니다. 당신의 소프트웨어 개발 플랫폼을 바꾸는 것은 높은 초기 설정 비용을 수반하여 매일 프로그래밍 작업 플로우에 깊숙히 영향을 줄 것이다. 또한 지금까지 만들어놨던 모든 요소들을 전환하는 것도 가장 비싼 비용의 결정 중 하나이다.

아마 더 중요한 것은, 당신의 소프트웨어 개발 플랫폼 또한 소프트웨어 엔지니어로서 당신을 이루는 것 중 하나이다. 소프트웨어 개발 플랫폼은 한 언어만 사용하게하고, 어느 특정 아키텍처를 우선시하며, 특정 툴을 요구하고, 당신을 그 모든 생태계와 플랫폼의 개발자 커뮤니티와 결혼시켜버리도록 지향(혹은 강요)한다.

페이스북은 당신이 전환해오기를 바란다.

그리고 리액트 네이티브 팀의 꾸준하며, 공들인 노력은 그 야망과 같다. 그들은 우리의 전통적인 Xcode/Swift/Objective-C 스택을 대체할 수 있는지 고려해야하는, 너무 근본의 소프트웨어 개발 플랫폼을 만들어왔다.

이것이 실용적인 변화일 수 있을까? 내가 읽어온 리액트 네이티브에 관련된 블로그 포스팅들은 오히려 피상적인 접근을 알려주었다. 그것의 장점과 단점에대한 깊이있는 접근은 없고, 여러분이 누군가로부터 소프트웨어 개발 플랫폼을 바꿔라고 설득당한 사람이라고 가정하고 이야기를 시작한다.

지난 몇달동안 리액트 네이티브를 사용하면서, 나는 이것으로 개발할 수도 없고 이것을 사용하기를 추천하지도 않을 플랫폼인 것으로 결론이 나왔다. 이 글의 목표는 Swift 개발에서 리액트 네이티브로 전환하는 것에 대한 빈틈없는 평가의 찬반을 알려주는 것이다. 그리고 그 전환에대해 반대하는 이야기도 할 것이다.

찬성
선언 스타일(Declarative style)
리액트 네이티브와함께 작업할때 기뻤던 한가지는 UI 프로그래밍의 선언 스타일이었다. 리액트 방법으로 하면 UI는 함수의 상태와 프로퍼티들이다. 반면 Cocoa Touch에서는 UI를 명령적으로 짜야한다.

아래 예제가 내가 말하는 바를 설명해 줄 것이다. 화면 왼쪽 상단에 작은 사각형을 넣고 싶다고 가정하자. 이 작은 사각형은 사용자가 연결되있을 때는 빨강, 연결되지 않을때는 초록을 표시한다.

아래는 우리가 일반적으로 iOS에서 어떻게 했는지 보여준다.

명령형 스타일에서는 UI를 갱신하기 위해 이 모든 단계를 명시해주어야한다. isConnected가 바뀌었는지 듣고 있어야 하고, 뷰에 일치하게 갱신해야한다. 우리는 iOS에게 어떻게 상태를 컴퓨트할지 말해줘야한다.

이제 리액트의 선언 방법과 비교해보아라.

간단하게 어떻게 여러분의 앱이 주어진 점을 보여주는지 표현했고, 리액트는 데이터 갱신때마다 자동으로 모든 UI 갱신을 관리해줄 것이다.

리액트의 선언 스타일은 뷰의 render() 메소드로 작용하여 여러분의 UI를 묘사하개 해준다. 리액트 프레임워크는 상태의 모든 갱신을 다시 랜더링하도록 해준다. 여러분의 데이터 갱신(backgroundColor의 갱신이 일어날때)은 자동으로 UI 갱신으로 이어진다.

이렇게하면 모델의 갱신에대한 응답에서 손수 뷰를 갱신하지 않앋 되게 해준다. 여러분이 이 책임으로부터 벗어나고 싶든 아니든, 리액트는 당신의 설명에따라 갱신 관리를 보장해주는 점은 매우 좋은 점이며, 여러분은 더이상 isConnected 변수에대한 프로퍼티를 관리할 필요가 없다. 당신이 갱신한 모든 것은 상태이다.

또한 UI 요소들이 결국 클래스의 객체가 아니라 함수인것처럼 생각하게 될 것이다. 그것들은 상태를 받아서 UIKit 오브젝트로 랜더링한다. Component의 역할은, 요청이 들어왔을때 예상되는 상태에서의 그 자신을 반환하는 것이다.

UI에 대해서는 유용한 방법이라 생각한다. 그리고 MVC로부터 좋은 발전이다. 뷰는 단지 그 자체를 보여주는 역할만 하고 데이터를 관리하지는 않으며, UIViewController에서 시작하기를 좋아하고, 뷰와 컨트롤러 사이 관계에 밀접하게 붙어있는 장님으로 바뀐다.

빠른 반복
리액트 네이티브의 매력은 단지 지적임이 아니다. 프레임워크는 실용적이기까지 할 것이다.

여러분이 리액트 네이티브에서 프로그래밍을 하면, 프레임워크는 당신이 작업하고 있는 자바스크립트 코드를 서브하는 로컬 서버를 만들어 줄 것이다. 앱을 만들면 iOS 시뮬레이터나 기기에서 돌려보고, 리택트 네이티브는 자바스크립트로 만든 모든 변화를 앱에 반영하게 해준다.

여러분에게 두가지 옵션이 있다.
  • 실시간 리로딩(Live reloading)은 당신이 그 파일에 수정하고 저장할 때마다 앱을 리로드할 것이다. 기본적으로 시뮬레이터로 전환하는 것을 막고 ⌘ + R을 누르지 않아도 된다.
  • 핫 리로딩(Hot reloading)은 앱 전체를 리로드하지 않고 당신이 수정한 부분만 리로드한다. 당신이 네비게이션 스택 깊숙히 테이블 뷰 셀의 UI를 작업하고 있었다면, 그 바뀐점을 보기위해 시작화면에서 셀까지 찾아가지 않아도 된다. 그리고 컴포넌트는 이미 그 안에 있던 상태로 그대로 있을 것이다. 이것은 WYSIWYG 프로그래밍 경험(WYSIWYG programming experience)이다. 앱을 위해 Xcode가 결코 제공하지 않았던 호화로운 기능이다.

나는 앱을 시작에서부터 보여주고 싶은 ViewController마다마다 AppDelegate에 디버그 메소드를 넣었던 것을 기억한다. 그리하여 앱을 켤때마다 손수 그 페이지를 찾아가고 싶지 않아서 코드로 그 페이지를 찾아가게 했었다. 핫 리로딩은 나를 동굴에서 나온 사람의 기분으로 만들어 버렸다.

리액트 네이티브의 피드백 루프 주기는 넋이 나갈 정도로 낮다. 앱에서 파일을 저장하고 그것을 눈으로 보기까지 1~2초정도 걸린다. 이것은 Xcode에서 일반적으로 우리가 사용한 빌드&런보다 10배 작게 든다.

이것은 방송처럼 코드 갱신을 가능하게 해준다. 자바스크립트 코드에서의 어떤 변화라도 앱이 제품으로 출시되어 있는동안 사용자에게 즉시 적용될 수 있다.

크로스 플랫폼
품위없고 서튼 이야기를 기억한다.

"훌륭한 앱이군요! 안드로이드에서도 동작하나요? [...]오, 안드로이드 버전은 언제 배포할 계획이시죠?[...], 오."

이제 코드베이스가 수백만 개의 추가 장치에서 실행될 수있는 앱을 만들 수 있으며 아웃 리치를 몇 배 증가시킬 수 있다.

그리고 여러 플랫폼에서 동일한 코드가 더욱 확실하게 같은 모습과 같은 동작의 앱을 만들 것이라 확신한다.

같은 코드베이스로 여러분 앱의 안드로이드 것을 만들 수 있고, 그래픽적으로든 기능적으로든 iOS쪽과 동등해지게 한다.

크로스 플랫폼 프레임워크의 명백한 장점은 언제나 시장 개발, 통합 코드베이스 및 앱의 코드베이스를 유지하는데 필요한 통합 기술 세트이다.

반대
불확실한 로드맵
리액트 네이티브를 사용할 때 생기는 중요한 걱정은 그 프로젝트에서의 장기적인 보장이 없다는 것이다. 

만약 이 프로젝트가 네트워크 라이브러리나 CGPath를 그리는 SVG같은 부가적으로 합쳐서 쓸 수 있는 요소라면, 장기적인 지원이 두번째 걱정으로 밀려날것이다. 리액트 네이티브 개발자들이 그만둬버리거나 리액트 네이티브 개발 속도가 마음에 들지 않으면, 대충 비슷한 라이브러리로 대체하거나 아니면 내 스스로 고쳐 쓸수도 있었을 것이다. 이것은 큰 작업이 될지도 모르겠지만, 이러나저러나 거대한 일은 아니다. 한 라이브러리를 쓸 수 없게 되었다해서 프로젝트 전체가 붕괴되지 않음을 보장하기 위해선 모든 나의 써드파티 라이브러리/CocoaPods을 내 프로젝트에서 충분히 떼어놓으면 된다.

그러나 리액트 네이티브는 CocoaPod처럼 부가적으로 쓰는 요소가 아니며, 단지 SDK도 아니고, 단지 라이브러리도 아니다. 이것은 완전히 소프트웨어 개발 플랫폼이다. 내 앱이 이것에 "아주 붙어있다"고 말하는 것은 너무 조심스러운 표현이고, 애 앱이 그것에 완전히 의존적이라고 말해야한다. 만약 페이스북이 리액트 네이티브 유지보수를 중단하면, 그것을 쓰고 있던 내 앱은 썩어갈 것이고, 내가 할 수 있는 "리액트 네이티브 대체물"은 없을 것이다. 만약 그것을 내 스스로 개발하려 한다면 리액트 네이티브 소스에도 친숙해야할 뿐 아니라, React.js 코드베이스, 네이티브 CLI 툴, JavaScriptCore에도 익숙해져야 할것이다. 커뮤니티가 이 프로젝트의 생존을 보장해줄까? 아마-만약 그렇게 되버리면 우리가 사용할 수 있을 정도의 개발 속도가 나지 않을 것이다.

깃헙 저장소에서는 2주에 한번 정도 건강한 속도로 리액트 네이티브 릴리즈가 되고있다. 두개의 분리된 것과 복잡한 소프트웨어 개발 플랫폼을 타겟으로 하는 소프트웨어 개발 플랫폼에는 나쁘지 않다. 페이스북이 오늘 약속할지도 모르지만, 지속적인 네이티브를 유지하기위한 장기간의 약속을하지 않았다. 회사에서는 이 프로젝트에서 손을 떼지 않을 거라는 어떠한 보장도 하지 않고 있다. 가까운 미래뿐만 아니라 여러분의 앱이 살아있는 동안에 말이다. 다른 말로는, 현재 시점에서 리액트 네이티브가 iOS11이나 iOS12에 호환될거라는 보장은 어디에도 없다는 것이다.
여기서 다음년도로!
2016년 4월 이후 공식적인 리액트 네이티브 블로그 포스팅은 종료되었다. 그 이후엔 어떻게 되었을까? 그것에대해 페이스북은 걱정없이 침묵만 지키고 있다.
우리는 곧 계획을 발표할 것이다-Konstantin
2015년 12월에 리액티브 팀이 말한 것이다. 이 계획이란것은 아직도 발표되지 않고 있다. 확실히 리액트 네이티브의 페이스북 장기간 비용 분석 결과는 당신이 생각한 만큼 멋지게 보이지 않는다.

다시 말하면, 우리는 개별의 CocoaPod에대해 이야기하는게 아니라, 여러분의 코드가 실행될 수 있는 유일한 플랫폼에대해 이야기하고 있는 중이다. 굉장히 장기적인 시각으로 다뤄야할 중요한 문제이다.

명백히 위압하는(Patently daunting)
리액트 네이티브는 복제를 허용하는 BSD-스타일 라이센스와함께 페이스북의 추가적인 특허권 문서(Additional Grant of Potent Right)를 만들었다.(버전2) 이 파일을 넣은 페이스북의 입장은 분명하지 않고, 이 파일 자체도 불분명하다.

양극의 문서이다. 문서가 끝나자마자 리액트 네이티브를 사용하기 위해서는 "perpetual(영속의), worldwide(널리), royalty-free(무료저작권), non-exclusive(독점불가능한), irrevocable(변경불가능한)" 라이센스를 따라야한다고 한다. 아래 조항들을 제공받으면서 말이다.

The license granted hereunder will terminate, automatically and without notice, if you (or any of your subsidiaries, corporate affiliates or agents) initiate directly or indirectly, or take a direct financial interest in, any Patent Assertion: (i) against Facebook or any of its subsidiaries or corporate affiliates, (ii) against any party if such Patent Assertion arises in whole or in part from any software, technology, product or service of Facebook or any of its subsidiaries or corporate affiliates, or (iii) against any party relating to the Software.

나는 변호사에게 이것을 명확하게 해달라고 직접 물어보았다(여러분이라도 그렇게 할 수 있었을 것이다). 그 변호사 말에 따르면 이 조항은 다음으로 요약된다. 내가 만약 페이스북에게 특허권 위배를 주장하며 소송을 걸면 리액트 네이티브를 사용하는 라이센스가 즉시 사라질 수 있다.(아래 Appendix에서 그의 더 세부적인 설명을 인용했다)

실제로는 특허권으로 페이스북으로 고소하지 못하게 하는 것처럼 보였다. 이런 의미에서, 이것은 페이스북이 내 특허권을 침해할 수 있는 기회를 주는 것처럼 보였다.("당신이 여기에있는 훌륭한 양자 물리학 기술은 당신의 앱에 나쁜 일이 생길 경우 부끄러운 일이 될 것입니다.")

위 설명이 비판적이라고 생각할지도 모르겠다. 그러나 소프트웨어 라이센스와 특허권을 평가할 때는 비판적으로 해야한다. 알고리즘이 최악의 시나리오에서 얼마나 잘 동작하는지 그 효율을 평가한다. 똑같은 이유로서 소프트웨어 라이센스와 특허권도 그렇게 평가해야한다고 생각한다.

리니어 서치(linear search) 알고리즘은 타겟값이 리스트의 첫번째로 일어나는 시나리오에서는 최적화된 동작을 할 것이다. 그 이유 하나로, 모든 시나리오에 리니어 서치 알고리즘을 사용하라고 나를 설득하기엔 만족스러운 이유가 아니다. 만약 타겟값이 리스트 마지막에 있을 수 있다면, 내 리니어 서치 알고리즘의 평균 퍼포먼스는 굉장히 나빠질 것이다.

비슷한 맥락에서, 페이스 북이 내일 일련의 IP를 침해하는 것이 가능하고 React Native를 사용하기위한 라이센스를 취소함으로써 내측으로부터의 보복을 처벌 할 수 있다면, 페이스북이 오늘 일련의 IP 침해를 하지 않았다는 사실은 나를 조금 안심시킨다.(원문: Likewise, the fact that Facebook happens not to be a serial IP infringer today reassures me little if it is possible for Facebook to become a serial IP infringer tomorrow and punish any retaliation from my side by revoking my license to use React Native.) 만약 내일 페이스북이 내 IP를 침해하고(그러나 그것이 내 소프트웨어와 관련은 없다) 그들에게 소송을 걸면,  페이스북에게 내 앱에서 리액트 네이티브를 빼내게 만들 기회를 주는 셈이 되버린다.

내 앱이 그 플랫폼에 의존적인 것 뿐만 아니라 영리한 내 프로퍼티들까지도 위험하게된다.

iOS 앱들은 완전히 애플의 재량으로 앱스토어에 들어간다. 나는 쉽지 않은 느낌을 두배로 느끼고 싶지 않다.

명백히 침묵하는(Patently silent)
위의 해석이 정확한가? 최악의 시나리오 가능성인가? 이런것들을 걱정할 만큼 좋은 이유가 있는가? 걱정하지 않아도 될 좋은 이유가 있는가?

수많은 깃헙 이슈와 포럼 포스팅에서, 그들의 법률 부서가 이런 조항 때문에 리액트나 리액트 네이티브를 쓰지 마라고 조언한다고 이야기해왔다. 볍률 부서가 없는 개발자들에게는 유감스럽게도 이 문제들이 모호하게 남아있다.

좀 더 걱정스럽게도, 페이스북은 이 문제를 더 명확하게 하기 위한 근본적인 노력을 해오지 않고 있다. 2015년 페이스북 오픈소스 블로그 포스팅에서는 이러한 "혼란"에대해 인정했고, 기꺼히 추가적인 특허권 문제에대해 명확하게 할것이라 알렸었다. 깃헙에서 그것과 연관된 5개의 이슈 이후에도 그 불분명함은 사라지지 않고있따.

여러 페이스북 개발자들이 이 이슈에대해 응답해오고 있는데, 그들이 적어놓은 것에는 안심될만한게 하나도 없으면서 그것을 명확하게 하는 동안에 개발자들을 안심시키려고 노력하고 있다. 그들 중 한명은 Reddit과 HackerNews에있는 이 이슈에대한 토론 링크를 주기도 했었다(도움이 되지 않을 뿐더러 오해시키기까지 했다).

이것은 해석학과 추측의 작업으로 바뀌어버렸다. 페이스북은 리액트 네이티브 라이센스를 취소 할 수 있을까? 그렇다면 어떤 조건에서?

자바스크립트
Swift에서 리액트 네이티브로 전환할때 생기는 심각한 부정적인 면은 기술적 역행이다. 여러분은 자바스크립트에 적응하고 사용해야하는데 이 언어는 다음과 같은 특징을 담고있다.
  • 기술적으로 불완전함
  • 언세이프
  • 늦은 발전
왜 그렇게 말하는지 보자.

여기 이후에 예제에 나오는 모든 자바스크립트 코드는 ES2016에 유효하다.

자바스크립트의 결점
내가 좋아하는 법퍼 스티커에서 하는 말이다.
안전은 사고가 없는 것이다(Safety is no accident)
처음 봤을땐, 이 말은 중의적이었다. 우리는 안전의 정의를 사고가 나지 않는 것과 여러 안전장치 제품의 제안으로 알고 있다.

자동차에 안전벨트나 에어백이 탑재된 여러 안전장치의 제품은 안전하다고 할 수 있는가?

물론 대답은 같은 조건에서 둘 다이다.

운전자는 수많은 안정장치가 탑재된 차를 더 선호할 수 있다. 운전하기 어렵게 만들지라도 막을 수 있는 사고는 최대한 줄일 것이다.

비슷한 의미에서, 프로그래밍 언어는 프로그래머 에러에 대비한 안전장치를 제공할 수 있다.

수많은 운전자가 생산성을 위해 안전벨트를 착용하지 않고 운전한다는 사실은 옳바른 주장이 아니다. 비슷하게, 수많은 자바스크립트 개발자들이 생산성을 위해 태생의 언세이프 언어를 쓴다는 것도 옳바른 주장이 아니다.

프로그래밍 언어에서 안전의 중요성은 iOS 개발 툴이 진화하고 있는것의 진가를 인정하는 것이기도 하다.

오토 레퍼런스 카운팅이 Objective-C에 처음 나타났을때, 여러분의 iOS 프로젝트에서 그 옵션을 끄고 작업했을 수도 있다. 왜 ARC를 끄는게 나쁜 생각이었을까? 그 이유는, 이제 컴파일러가 당신의 오브젝트 라이프타임 계산을 할 수 있게 되었는데, 당신이 계산하는 것보다 더 빠르게 해준다. "컴파일러가 당신보다 더 똑똑해졌다"는 진언이고, 레퍼런스 카운팅에 관해서는 확실해졌다. 결과적으로 EXC_BAD_ACCESS 런타임 크레쉬를 줄였다는것을 알게되었을때 얼마나 만족했었는지 기억한다.

Obejctive-C는 한 변수 타입을 id로 설정할 수 있는데, 이것은 "어떤 것이든 모든 타입"의 의미이다. 그러나 컴파일러가 막을 수 있는 크레쉬임에도, 이러한 습관때문에 런타임 크레쉬가 일어날 수 있다. 컴파일러가 해결할 수 있는 문제라면, 컴파일러가 해결하게 놔두고 당신은 다른 문제를 해결하러 가면 된다.

여러분은 unrecognized selector sent to instance 크레쉬를 기억할 것이다. 응답하지 않는 오브젝트에 메소드를 호출할 때 생기는 크레쉬이다. 타입에러. 내 버그에서 3번째이다.

당연하게도 Swift를 사용하고나서 나의 첫 반응은 "Objective-C에서 런타임 크레쉬를 막고 싶었던 누군가가 만들었구만"이었다.

Swift는 안전하다. String을 받길 예상한 함수에 Int값을 넣도록 컴파일러가 허용하지 않는다. 사실 컴파일러가 타입 추론을 할 수 없었다면, 명시적으로 그렇게 할 수 있었을 것이다. 

그러나 자바스크립트는 프로그래머 에러에 대비한 안전장치가 부족하며, 여러분의 루틴에서 런타임 크레쉬를 막아야하고, 프로그래머 에러를 막아야한다.

타입 에러
자바스크립트는 변수나 함수에서 파라미터의 타입을 정해주지 않는다.

어떤 변수라도 언제든 무엇이든 될 수 있다.

자바스크립트는 class, typeof, instanceof와같은 키워드로 타입과 클래스 개념이 있다고 믿게 만든다.

여기서 우리는 다음을 보자.
  • 자바스크립트에서의 "class", "type", "instance" 개념은 주요 프로그래밍 세계에서의 개념과 완전히 다르다.
  • 자바스크립트에서는 타입을 너무 신뢰할 수 없게 정의해서, 이것들이 유용한 용도를 제공하지 못한다.

이  unrecognized selector sent to instance 크레쉬가 기억나는가? 이것이 이제 여러분 곁을 떠날것이라 생각하는가? 아래에는 리액트 네이티브의 것들이 있다.


옵셔널 결핍
Objective-C 코드(혹은 다른 수많은 오래된 언어)에서 엄청나게 많은 양의 버그들은 프로그래머가 부주의하게 nil에다가 메소드를 호출해서 생긴다.

리액트 네이티브와 자바스크립트 세계에서는 아래 에러를 종종 볼 수 있다.

그리고 이론적으로나 실무적으로나 막을 수 있다.

Swift는 옵셔널을 만들어냄으로서 이 문제를 해결했는데(옵셔널이란 nil일 수도 있고 값이 들어있을 수도 있는 타입이다), 이것을 사용할 때는 여러분에게 강제로 nil 체크를 거쳐서 사용하게 만든다.

함수 기호(function signature)의 결핍
자바스크립트에서는 함수가 리턴타입을 가지고 있지 않으며, 이 함수가 어떤 타입을 반환할지 모르거나 함수가 어떤것이든 반환할 수 있다.

좀 더 재미있게 만들어보자. 자바스크립트에서는 어떤 식이든 어떤 함수에의해 어떤 시간에든 계산될 수 있다. 자바스크립트 표준 라이브러리에 있는 map과 parseInt 함수를 사용한 예제를 생각해보자. map은 아마 Swift의 map과 동일한 것이고, parseInt는 문자열을 숫자로 파싱해주는 함수이다.
parseInt 함수는 2개의 파라미터(val, radix)를 받는데 반해, map은 3개의 파라미터(currentValue, index, array)를 보내기 때문에 이런 엉망의 결과가 생긴다. 이런것들이 여전히 합법적인 자바스크립트이다(일부 사람들이 함수형 프로그래밍 언어에 유용할것이라고 생각하는 언어).

불변성
자바스크립트가 지원하는 불변성은 매우 안 좋다.

const 연산자가 있는데, 이것은 기본타입(primitive)이 바뀌지 않는 것을 보장하는데 도움을 준다. 그러나 기본타입이 아닌 나머지 모든 것은 젤리처럼 유연하다.

유일한 복사? 아니다. 어떤 유일한 객체라도 언제든지 여러분의 앱에의해 수정될 수 있다. 멀티스레딩에 행운을 빈다. 정말로.

앱 개발을 힘들게하는 많은 것들은 가변을 쫓고 상태를 유지하는 것이다.

페이스북의 Immutable.js 문서에서 말한 내용이다(자바스크립트에서 불변 데이터 구조를 만들기위해 설계된 프레임워크이다).

그러나 아래는 리액트에서 어떻게 불변성을 만드는지이다.

이 컴포넌트에 넣는 인풋을 props라 부르는데, "properties"의 준말이다. 그들은 JSX 문법으로 속성들을 보낸다. 여러분은 이것을 컴포넌트에 불변할 것이라 생각할 수 있는데, 이것은 절때 this.props에 덮어 씌울 수 없다.

여러분은 상태를 바꾸지 말자고 공손히 물어본다. 그렇다 위 글은 리액트 네이티브 문서에서 가져온 것이다.

배열을 믿을 수 없다.
여러분은 배열이 "행과 열로 된, 비슷한 객체들의 질서정연한 배치"라 생각하는가? 아래를 보고 다시한번 생각해보아라.
자바스크립트에서 배열은 우리가 원래 배열이라 부르는 그것보다는, 보통의 자바스크립트 객체에 더 가깝다. 정확한 수서성의 결여와 가변성은 잘 동작하기 힘들게 만든다.

에러 핸들링하기 힘들다
자바스크립트에서는 경고 없이 런타임이나 예외를 던지는 함수를 만들 수 있다.
예외로 당신이 원하는 무엇(문자열, Date, 함수 등)이든 던질 수 있다. 팀원의 코드를 잠제적으로 크레쉬할 수 있다는 표시를 함수에 한다던가, 예외의 과정이 어떻게 되는지 명시할 메커니즘이 따로 없다. 문서에서는 대신에 if문을 사용하라고 권장한다.

당신의 예상하지 못한 예외 처리를 다루기 위해서는 마지막 라인의 방어로 예외를 남겨놓는게 최고이다. 그리고 예상되는 에러를 컨트롤 플로우 문으로 관리하기에도 최고이다.

디시멀(Decimal)을 지원하지 않는다.
하드웨어에서 대부분의 소수 자릿수들은 이진수로 정확하게 표현되지 않으며, 많은 프로그래밍 언어(자바스크립트와 Swift를 포함한)는 종종 수학적으로 잘못된 소수 계산을 내놓을 것이다.
이것이 왜 다른 언어의 표준 라이브러리가 소수 자릿수를 지원하는지 이유이다(예를들어 Swift에서는 Decimal을 사용할 수 있다). 자바스크립트에서는 서드파티 라이브러리나 여러분이 직접 만든 코드에 의지해야한다.

믿지 못하는 수학
초등학교 산수에도 주의를 기울여야함을 기억해라. 자바스크립트는 0과의 관계가 복잡하며 이것은 숫자가 아니다.

언세이프한 초기화
자바스크립트는 속성을 초기화 할 필요가 없으므로 객체를 만든 후 모순된 상태로 둘 수 있다.

if 다음에 선택적으로 가능한 커리 중괄호(curly brace)
if문 뒤에 커리 중괄호는 선택적이다.
여러분의 컨트롤 플로우 문에 모험의 취향을 추가한다.

모호한 커리 중괄호
프로그래머의 의도가 정확하게 추론된 것이 아니라면, 이 언어는 커리 중괄호를 선택적으로 할 수 있게 놔두지 않는다.

fallthrough 전환
switch  절에서 break를 깜빡했다면, 아래로 쭉쭉 떨어질 것이다. 또한 Swift에서는 케이스 철저성을 확인하지 않아도 된다.

어떤것이 '무(없음)'일까? (What's nothing)
 모든 의도나 목적에서 null이 아닌 것이 무가 될 수 있다. 도움이 되지 않는 구별이다.

그리고 변수에 어떤 데이터가 담겨있는지 아닌지 알고 싶다면 어떨까? 음, 확인이 너무 거추장스럽게 null과 undefined 둘 다 체크해야한다.


빈약한 표현력
  • 열거형이 없다. 연관타입을 가진 열거형은 말할것도 없다. 신뢰성 높은 상태 표현에 행운을 빌 수 있을까.
  • guard문이 없다.
  • 제네릭이 없다.
  • 컨트롤 플로우문의 표현력을 늘리기위한 where이 없다.

지극히 느린 진화
ES2016 때 자바스크립트에 새로 추가된 기능을 보라.
1. 배열을 위한 includes 메소드
배열이 특정 값을 가지고 있는지 확인한다. 아래에 어떻게 사용하는지 나와있다.
이건 사용하지 말자.
2. **연산자
지수 연산에 사용하기위한, a**b는 Math.pow(a, b)의 축약이다.

한번 생각해보면, 파이썬의 **연산자는 파이선1에 있었고, 루비의 **연산자는 20년도 전에 있었던 연산자이다.

따라서 자바스크립트는 이 기본적인 산수 연산자와 배열을 위한 꽤 제한된 맴버쉽 체크 메소드를 추가하는데 20년이나 걸렸고, 이 특징이 한 해중에 가장 의미있는 것이었다.

Flow로 구조받자!(Flow to the rescue!)
Flow는 위의 수많은 불평으로부터 내놓은 페이스북의 대답이다. 이것은 자바스크립트를 위한 정적 타입 체커이고, 여러분 코드에서 변수의 타입을 추론하고 추적할 수 있는 능력이 있으며, 여러분에게 일어날 운명(옮긴이: 실행되었을 때 일어날 오류)을 경고한다.

위에서 보았던 함수 기호의 결핍으로부터 나온 문제의 예제를 다시 보자.(number가 들어오길 예상한 divideByFour 함수는 문자열을 받는다) 여기엔 Flow가 이것을 어떻게 다루는지 보여준다.

함수 기호 부재 때문에 생기는 많은 문제를 고쳐준다.

제네릭 배열에도 동일하게 할 수 있다.

Flow는 nullability를 잘 다룰 수 있게해주고, 값이 null이되면 안되는데 null일 수 있을때 여러분에게 경고를 띄워 줄 것이다.

만약 j가 string으로 가정하지만, 언제든 null이 될 수 있으면, Flow는 그 사실을 우리에게 적절하게 알려주려 할 것이다. 따라서 인자로 string을 받되 null이면 안되는 곳에서 인자를 넘겨주기 전에 굳이 null 체크를 할 필요가 없다면 불만을 가질 수 있을 것이다.

Flow의 기능들은 타입체킹과 주석을 달아주는 것을 넘어, 새로운 구성체(new construct)를 지원한다. 그 중 하나는 리터럴 타입인데, 우리가 만드는데 사용할 수 있으며, 그 예로 열거형이 있다.

그리고 Flow는 direction이 "North"도 아니고 "South"도 아닌 것을 반환하려하면 뭔가 불평하고 있을 것이다.

내가 찾은 또다른 유용한 구성체는 유니온(Union) 타입이다. 이것은 딱 하나의 값으로 제한하고, 미리 정의해놓은 타입들 중에서 하나로 제한한다. 아래 예제는 Flow 문서에서 온 것이다.

Flow은 도움이되는한 꽤 도움이 되는 리액트 네이티브와 훌륭한 동반자이다. Flow 문서에서는 구성 요소의 유형에 올바르게 주석을 추가 할 때 어떤식으로 동작하는지 보여주는 좋은 예시를 제공한다.

저 주석은 Flow가 어디서 경고를 내는지 알려주며, 여러분 계약에 만족스럽지 않다고 충분히 꾸짖는다.

Flow는 전적으로 강력한 도구이다. 여기에는 여러분이 해볼 수 있는 유용한 커멘드라인 인터페이스 예제가 있다.
  • suggest는 주어진 파일에대해 타입 주석 제안을 보여준다(suggest Shows type annotation suggestions for given files)
  • type-at-pos는 주어진 파일과 위치에 타입을 보여준다(type-at-pos Shows the type at a given file and position)
  • get-def는 변수나 프로퍼티의 선언 위치를 받는다(get-def Gets the definition location of a variable or property)

Flow는 flossing와 비슷하다
이제 자바스크립트가 고쳐졌을까? 아니다.

Flow가 했을 공학의 노력에 감명받았고, 이것은 자바스크립트의 부모집합(superset)으로 남았으며, 따라서 당신을 태생적으로 약한 파운데이션에서 만들게 한다. 자바스크립트의 결점을 치료하는 어느 종류로서 Flow가 빗발치는 것은 (Monty Python and the Holy Grail으로부터) Swamp Castle and its King을 떠올리게 했다.

내가 여기 처음 왔을때, 여기는 전부 늪이였어. 여기 늪에 성을 세울거라는 말에 모두가 나를 얼간이라 했지만, 똑같이 만들어서 그냥 보여주었지. 그것이 늪으로 가라앉았고, 나는 두번째 것을 지었어. 그것도 늪으로 가라앉았고 세번째것을 또 지었어. 그것은 불에타고 쓰러져서 늪에 가라앉았어. 그러나 네번째는 버티고 있어. 그리고 이게 너가 얻어야 할 것이고, 영국에서 가장 강한 성을 얻어냈지.

안전하지 않은 파운데이션을 만들수 있을거라는 말이 파운데이션을 더 안전하게 만들진 않고, 더 효율적인 과정이지도 않을 것이다. 그리고 이 행동을 저지하려는 주장은 그 불합리함을 놓치게 만든다.

아마 더 의미있게, 자바스크립트 부모집합, 린터(linters), 정적 분석기는 당신이 더 안전한 언어를 고를 수 없는 플랫폼을 다룰때 완화시키는 방법으로서 일시적으로 억제해줄 것이다. 그게 된다면, 그것들을 사용하면서 도움을 구하지 않아도 될 것이다.

이런 일시적인 조치에는 또다른 근본적인 문제가 있다. 이것이 실제로 그 권위자가 쓰지도 않고 커뮤니티에서도 존경받지 못하면 법같은 이 안전장치는 아주 조금만 의미있게 된다.

Flow는 당신이 코드바운드로 리액트 네이티브 앱을 만들고 실행시키는 것부터 런타임 크레쉬를 생성하는 것까지 당신의 작업을 멈추게 하지는 않는다. 그리고 그것은 프로그래밍 언어에대한 기본적인 안전 요구사항이다. 만약 에러를 막을 수 있으면, 그 언어는 능동적으로 막으려고 할 수 있고, 안전하지 않은 코드를 짜고 실행시키는 것을 디폴트에의해 방해할 것이다(그 후에 하는게 아닌).

리턴값이 모호한 함수를 짜라고 팀에게 말할 수는 없을 것이고, 그것에 응답하지 않는 객체에 메소드를 호출하라고 나에게 말할 수도 없으며, 컴포넌트에 proptypes을 정의하지 않고 proptypes이 정의되있다는 코드 리뷰를 하는 동안 나에게 손수 끄집어내라고 할 수 없을 것이다.

유닛 테스트와 flossing과같은 Flow는 유익하고 선택적이고 지루한것의 저주를 낳는다. It’s in your next year’s resolutions.

그리고 여기에 현실이 있다. 깃헙에 공개된 .js 파일에 Flow를 사용하는 파일(즉, Flow가 체크할 수 있는 @flow가 포함되있는 파일)이 몇개나 될까? 어림잡아 80,000,000개의 .js파일에서 1,400,000개정도이다. 안전한 자바스크립트를 짜기 위해 2%보다 작은 곳에서 이 툴을 사용하고 있었다.

여기에 관련된 노트인, awesome-react-native에서 100개가 넘는 리액트 네이티브 저장소들이 있는데, 여기서 Flow를 제대로 사용하고 타입 주석을 사용한 저장소는 한개도 발견할 수 없었다. 오직 리액트 네이티브 튜토리얼에만 Flow를 사용하고 있었다.

자바스크립트 생태계: 속박과 굴레
자바스크립트 개발자를 제외한 나머지 모두는 자바스크립트의 결핍에대한 깊은 인상을 받은것 같다. 위에서 내가 설명한 자바스크립트는 흉측한 사마귀가 아니라는 사람들에게는, they’re “quirks” or “gotchas” that you, not your language, have to be on the lookout for.

왜냐하면 자바스크립트 개발자들은 자바스크립트가 불충분하다고 믿지 않기 때문이다.

언어에서 불변성을 지원하지 않는다? 그럼 만들면 된다. 언어에서 typing을 지원하지 않는다? 그럼 만들면 된다. 언어에서 decimal을 지원하지 않는다? 그럼 만들면 된다. 언어에서 안전한 함수형 프로그래밍 언어를 허용하지 않는다? 그럼 만들면 된다. 언어에서 nullability를 지원하지 않는다? 그럼 만들면 된다.

혹은... 당신이 이 기능들이 근본적으로 중요하다고 인정하면, 여러분은.. 바깥세상에서의 그것을 지원하는 언어로 전환해버리면 될까?(잘 모르겠다. 그냥 생각난것을 말한거다)

내 생각엔, 자바스크립트가 태생적으로 결핍하다고하여 그것을 계속 갈고 닦는게 아니라 다른것으로 대체하려 한다면 그것은 일반화된 고집의 거부(generalized obstinate refusal)이다. 갑자기 증가한 개선이나 버팀목의 결과는 살아있는 생태계의 신호로 보이나, 실제로 이것이 의미하는 것은 이 언어가 근본적으로 중요한 기능의 결핍이 있다는 것이다.

아래 그림은 이러한 상황을 만화로 잘 표현했다.

삽질으로부터의 자유는 당신이 필요한 것이 내장된 언어를 선택하는 것에 달렸다. 이러한 삽질은 에너지 낭비이다. 자바스크립트는 다음의 이유로 좋은 소프트웨어를 제작하는데 도움이 되지 않게 하고 있다: 자바스크립트는 우선 당신이 개발하게 만들고, 다른 언어가 제공하는 것에 의존적이게 만든다.

사슬(Chains)
자바스크립트는 튀긴 커다란 생선을 가지고 있다. 이 언어는 다양한 버전의 인터넷 브라우저의 몇십억 사용자 입맛에 맞춰주어야한다. 이것이 언어의 발전을 더디게 만든다.

typeof(null)==='object'를 기억하는가? 음, 예전에 type of null을 null로 바꾸자고 제안했었다. 그러나

이렇게하면 현존하는 수많은 사이트가 망가지게 될 것이다. 자바스크립트의 스피릿에 적합하지 않다.

그리고는 이 제안이 거절되었다. ES06에의해 null은 여전히 object이다.

자바스크립트의 발전 과정은 필요에의해 입맛에만 맞추고 있다.
  • 수많은 구식 버전의 브라우저 사용자들
  • 각색의 브라우저 벤더 집합
  • 수십억의 사이트와 그 사이트의 각 개발자들
이것들은 훌륭한 민주주의이긴하나 한편으로는 다른 언어에비해 굉장히 더디게 발전한다. 그리고 이것은 이식성이 좋은 만큼 개발자의 인간환경공학은 좋지 않을 것이다.

넓은 시각(Wider angles)
역사적인 관점에서, 한 언어가 인기를 얻고나서, 그것을 넘겨받고싶은 점유자들이 거부하게되는 패턴은 겉으로보기에 불충분한 언어에게 나타나는 익숙한 패턴이다.

여기 따뜻한 모닥불 옆에 앉아서 내 얘기를 좀 들어봐라.

1994년으로 돌아가보자. Richard Stallman은 "왜 Tcl을 쓰면 안되는가"라는 다소 모호한 타이틀로 comp.lang.tcl 뉴스그룹(news group)에 글을 하나 썼다. Stallman은 프로그래밍 언어로서 Tcl의 단점을 심술궂게 꾸짖었고, 목적에 맞지 않는 Tcl을 고발했다. 자바스크립트에대해 내 불평과 유사하게 매우 장관이었다.

Emacs의 주요 수업에선 확장을 위한 언어는 단지 "확장 언어"가 될 수 없다고 한다. 실제 프로그래밍언어가 되야하고, 상당한 양의 프로그램을 짤 수 있어야하며, 그것을 유지보수할 수 있게 설계되야한다. 왜냐하면 사람들이 그것을 바라니까! [...] Tcl은 진지한 프로그래밍 언어로 설계되지 않았다. 이것은 "스크립트 언어"가 되기위해 설계되었으며, "스크립트 언어"라 가정하는 것은 실제 프로그래밍 언어가 되려고 할 필요가 없었음을 의미한다. 따라서 Tcl은 하나의 능력을 빼먹었는데, 배열이 부족하다; 링크드리스트를 만들 수 있는 구조체를 빼먹었다. 그것은 가짜로 넘버를 가지고 있는데, 매우 느리지만 동작은 한다. Tcl은 작은 프로그램을 만드는데 적합하나, 그 넘어를 하기엔 적합하지 않을 것이다.

이 글은 1994년에 Tcl 전쟁을 촉발시킨 글이다. 그가 들었던 대답들중에 가장 인상적이었던 John Ousterhout(Tcl을 만든 사람)의 대답이다.

언어 설계자로서 이 언어가 왜 선천적으로 더 낫고 더 나쁜지 우열을 가리는 토론을 사랑한다. 그러나 이런 건방진 주장에는 많은 문제가 있다. 궁극적으로 모든 언어의 이슈는 그 사용자가 원하는 것을 투표할 때 합의가 생긴다. 만약 Tcl이 그들이 사용하는 것보다 더 생산적이게 만들었다면(혹은 이미 그렇게 되었다), 다른 언어 사용자들이 이것이 더 낫다고 하면서 온다면, 그때 사람들은 언어를 바꾼다. 이것이 법칙이고 이게 맞다.

Outsterhout의 반격은 내가 활동하고 있던 많은 사람들 앞에서 이루어졌고, 그의 일격은 더 가까이서 볼 수 있었다.

(몇몇 독자들은 Ousterhout가 순회적으로 말했다는 것을 알아차릴 것이다. "왜 사람들이 언어를 바꿀까? 그게 더 낫기 때문이다. 왜 그것이 더 나을까? 사람들이 많이 쓰기 때문이다." 그의 주장은 더 많은 설명이 필요함에도 불구하고 조금도 하지 않았는데, 우리는 종종 특정 기술 스택(예를들어 여러분이 생각하고 있는 리액트 네이티브)에 여러 주장에서 이런 추런 라인을 보았었기 때문이다)

Ousterhout에 따르면, 프로그래밍 언어는 그 언어가 더 낫다고 말할 수 있는 두가지 특징을 가지고 있는데, 바로 선천적인 특징들(프로그래밍 패러다임, 문법, 표준 라이브러리. 이식성과같은 그 고유의 특성)과 후천적인 특징들(수많은 개발자들이 폭넓게 채택한것)이다. 만약 언어A가 언어B보다 후천적으로 낫다면, 선천적으로 언어B가 언어A보다 더 낫다고해도 별로 소용없어질것이다.

다른말로 하자면, 채택되는 것이야말로 기술 우선순위에서 최고봉이다.(언어 설계자가 어떤것을 우위로 정의했는지 상관없이 말이다)

Ousterhout 주장에 딱 맞아보이는 앨범 커버이다.

더 많은 팬을 가진 가수가 그를 더 낫게 만든다. 요점은 팬이나 히트 기록의 양에의한 인기이며, 이것이 가수를 평가하는 좋은 기준이다.

그러나 언어가 널리 채택되고 그것으로 만들어진 멋진 앱들을 자랑할 수 있다는 사실로는 그 메리트로 당신의 판단에 설득시킬 수 없을지도 모른다.

복잡해보이거나 보기에 멋진 앱이 기술X로만 만들었다는 의미는 기술X가 복잡해보이거나 보기에 멋진 앱을 만들기에만 최적화되있고, 보통 일에는 최적화되있지 않다는 뜻일 수도 있다.

전세계에서 열리는 모래성 대회는 당신도 만드는데 쓸 수 있는 모래와 물로 아름다운 구조물을 만들어서 보여준다. 이것은 모래가 좋은 장난감임을 주장할 수 있는 근거가 될 수는 있으나, 모래성 대회를 근거로하여 모래로 여러분의 집을 지을 수 있다고 납득하기는 어려울 것이다. 그러나 많은 이들이 납득되어 버렸다.

The King of Swamp Castle은 수많은 해커의 왕이다.

비슷하게, 인기는 프로그래밍 언어를 평가하기엔 신뢰할 수 없는 기준이다. 아주 기본적인 이유로, 인기는 벤더를 속박시키거나(브라우저에서만 돌아가는 프로그래밍 언어가 된다던지), 벤드웨이건 효과나, 레거시(legacy) 코드베이스의 결과를 매우 잘 만들지도 모른다.

프로그래밍 언어의 고유 특징들(프로그래머 에러에 대비한 안전장치, 표준 라이브러리, 이식성 등)은 여러분의 기준에 기반해야한다. 이것은 생산성 향상의 기본요소이다.

사실은 많은 것들이 부정적인 생산성을 이끌기도 한다. 그 예이다.
  • 피할 수 있는 에러를 만드는 당신을 막는 안전장치의 부족
  • 많은 목적에 맞지 않는 작은 표준 라이브러리
  • 당신의 의도를 명확하게 표현하지 않는 문법과 의미론(syntax and sementics)
  • 더딘 발전
  • 위의 모든 것들(자바스크립트)

의존성
리액트 네이티브는 총 648개의 의존성을 가지고 있다.

특별히 놀랄 것 없게도, 의존성 체인은 세상의 npm 중에 길수 있으므로, 리액트 네이티브의 패키지 매니저이다.(원문: Not particularly surprising, as dependency chains can be long in the world of npm, React Native’s package manager.)

이 광경은 오픈소스의 동료관계이다. 여러분의 앱은 600명 이상의 사람들의 지속된 노력으로 만들어졌다.

이게 함정이기도한데, 여러분은 648명의 자원 봉사자가 그 라이브러리를 유지해주어야 앱이 잘 돌아가며, 그 자원봉사자들로부터 어떤 약속 따윈 없다.

그들의 라이센스가 당신의 소프트웨어에 맞게 유지될까? 희망적으로?

그리고 그 구현이 모두 보안면에서 최고의 실천을 하고 있을까? 혹은 648개의 별개의 잠재적인 보안 리스크를 묵인할 수 있겠는가?

더 나은 제안들
크로스-플랫폼 개발 때문에 리액트 네이티브를 선택하게 되었다면, 다른 옵션도 생각해볼 필요가 있다.

리액트 네이티브는 지금 Xamarin과 Appcelerator라는 두개의 크로스 플랫폼 개발과 경쟁하고 있다.

Xamarin과 Appcelerator 둘 다 iOS, 안드로이드, 윈도우 폰을 지원한다. 그리고 둘 다 아래의 것들도 지원한다.
  • 더 종합적인 API
  • 더 성숙한 IDE
  • 더 나은 문서
  • 명확하고 친숙한 라이센싱
  • 동등한 퍼포먼스(더 낮진 않더라도)

Xamarin 개발은 C#으로 하는데, 이것은 자바스크립트에 비해서 버그를 줄여주는 경향이 있고, 더 표현력이 좋다(more expressive). 만약 자바스크립트를 쓰는 개발자라면 Appcelerator 개발이 자바스크립트로 할 수 있다.

리액트 네이티브와 비교하여 Xamarin과 Appcelerator 둘 다 멀리 보았을때 더 나은 가능성을 가지고 있다. Appcelerator(Titanium의 아버지)(3억  5천만 기기에 앱으로 실행 되었었다)는 2016년 1월에 인수되었고 Xamarin(포춘지 선정 500 대 기업에서 100 기업 이상 사용했다)는 마이크로소프트에의해 2016년 2월에 인수되었다. 둘 다 이전보다 8천만달러 이상의 주식형펀드가 올랐다.

나에게 지원해주기 위한 비즈니스의 회사가 빽으로 있다면, 프로젝트들은 이 크로스 플랫폼이라는 레드오션에서 신뢰할 수 있는 소프트웨어 개발 플랫폼으로 살아남고 성장해야한다(최근에 세어보니 10개 이상의 활발한 개발 프로젝트들이 있었다).

(의미있는 언급으로 Flutter(이하 플루터)에 대해 이야기 하겠다. 이것은 구글이 만든 크로스-플랫폼 개발 플랫폼이다(iOS, 안드로이드 둘다 지원한다). Dart라는 언어를 사용하는데, 이 언어는 자바스크립트보다 안전하고 표현력도 더 좋다. 또한 메트리얼 디자인 원칙을 따르게 도와주고, 네이티브하게 컴파일 된 코드를 만들어준다. Xamarin과 Appcelerator와는 다르게 오픈소스이며, 아직 제품으로 준비되지는 않았지만 그럼에도 불구하고 약속해놓았다.)

결론
좋은 소프트웨어 개발 플랫폼은 4가지 필수 특징을 가진다.
  • 이식성 - 한가지 이상의 플랫폼을 타겟으로 한다.
  • 생산성 - 성숙한 IDE와 다른 개발 툴, 문서, 그리고 표현력 있는 그 개발 언어
  • 안전 - 당신이 만들 수 있는 실수를 그 플랫폼이 얼마나 막을 수 있는지의 정도
  • 지속성(longevity) - 당신의 앱이 살아있는 동안 플랫폼이 얼마나 오래 가는지
비록 우리 산업이 아직까지는 이것을 가늠하기위한 확립된 오픈 표준이 없지만, 나는 내 경험과 조사를 바탕으로 내 평가를 공유하고 싶다.


리액트 네이티브의 강점을 이식성과 생산정의 면에서 보자면 너무 과하고, 안전, 장기간 프로젝트에는 불확실성, 위압적인 특허 라이센스에는 부족함이 있다.


Xamarin과 Appcelerator와같은 성숙한 플랫폼은 최고의 이식성(윈도우 폰을 지원한다), 성숙된 개발 툴, 프로그래밍 언어를 선택할 수 있게 해주는 것을 제공하는데, 언어 선택으로 생산성과 안전성을 얻어낼 수도 있다. 그리고 이것들이 잘 투자받은 회사의 핵심 제품이라는 사실이 장기간의 면에서 안심시켜준다.


Swift 개발은 안전성, 지속성, 생산성 면에서는 좋다. 이식성의 면은 상대적으로 낮으나 무시해도 되는 수준이다. 안드로이드 버전을 개발 할 수 있게 해주는건 아니지만 macOS 버전이나 백엔드 서버 앱을 만들 수 있게 한다.





여기까지가 내가 왜 리액트 네이티브 개발자가 되지 않는지의 그 이유였다.

감사
이 글의 초안에 도움을 준 Rami Chowdhury와 Alkis Papadakis에게 감사를 표한다. 그리고 난해한 IP 텍스트들을 해독하는데 도움을 준 Greg McMullen에게도 감사하다.

바뀐 이력
이 글이 바뀐 이력은 여기에서 확인할 수 있다.

커멘트
이 글에 대한 토론 에 팔로우하여 참여하고 싶으면 Hacker News, Hacker News(Again)/r/programming로 와라.

참조
 



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

매년 우리의 Buffer iOS 앱은 애플의 World Wide Developer Conference(WWDC) 이후에 새로운 iOS 기능과 변화를 지원하기 위해 크게 업데이트 해왔었다.

올해에 큰 변화는 없었지만, 이번 포스팅에서는 퍼포먼스와 미래에대한 대비에대해 이야기 할 것이다.

새로운 버전의 Buffer 앱의 새로운 변화
WWDC가 끝나고나면 우리는 보통 앱을 실행해보고 이전 iOS 릴리즈 버전을 최소 타켓으로 조정한다. 이번의 경우 iOS9를 최소로 조정할 예정이었으나 iOS8 기기까지 지원하게 되었다. 그리하여 앱을 정리하고 이전 호환성에 더이상 필요없는 오래된 iOS8 코드를 제거할 수 있었으며, 몇년간 기술적인 빚을 갚을 수 있었다.

Buffer iOS의 6.0버전에서 증진시키고 싶은 부분중 하나는 바로 테이블이다. 특히 나중에는 레이아웃 부분을 더 유연하게 하고 싶었다. 우리는 2011년부터 UITableView를 사용하고 있었다. 이것은 컨텐트의 목록에는 좋지만, 나중에 v6에는 컨텐트를 그리드(grid)형식으로 보여주고 싶었다.

목표 : 부드러운 스크롤링을 위해 초당 60프레임을 지원
우리는 항상 부드러운 스크롤을 위해 마법처럼 초당 60프레임을 달성하려고 노력해가며 테이블을 업데이트 했다. 몇년간 아이폰이 더욱 강력해졌음에도 이것은 매우 어려운 목표치였고, 스마트폰이 싱글 코어 디바이스일때 설계된것같은 UIKit은 아직또 메인스레드에 묶여있다.

사용자가 프레임이 떨어지는 것을 알아차리지 않으려면 모든 레이아웃과 코드 랜더링이 16ms 안에 실행되야하는데, UIKit은 이것을 개발자에게 떠넘겼다. 시스템이 오버헤드하면 가끔 10ms 정도 휘청거린다.

우리의 UI는 매우 튼튼하고 다채로우므로 이 과제는 매년 포퍼먼스 유지보수를하게 만들었다. 이 기술적인 과제는 사용자가 "로딩" 화면을 보지 않으면서 스크롤 하는동안 어떻게 부드럽게 업데이트를 서버에서 검색하는지와 연관이 있다.

우리의 셀(cell)들은 사용자가 만든 소셜미디어 업데이트의 다양한 높이의 UI를 지원하는데, 이것이 꽤 복잡하다. 그래서 올해 애플이 UICollectionView의 퍼포먼스를 올렸다는 발표를 들었을 때 우리는 매우 흥미로웠다. 테이블 스크롤링을 하면서 처리할게 많을때 우리의 기대치는 60프레임을 유지하는 것이었고, 적어도 55프레임 이상 유지하길 바랬다.

우리는 먼저 앱에서 UICollectionView를 어떻게 이용할지부터 찾아보았지만, 현재의 UITableViews와 비슷한 레이아웃을 만들어내는데 몇 가지 장애물이 있었다. 컬랙션 셀의 오토사이징에대한 새로운 추가 사항과 함께 몇 가지 문제가있었다. 우리는 iOS가 높이를 계산하고나면 스크롤이 점프해서 왔다갔다하는 모습을 보고 있었다. (v5.0에서 가능하면 미리 높이를 계산해두는 방법을 찾았었다) 더 나아가 오토 레이아웃의 편리함은 무거운 처리의 비용으로 돌아오고, 이것은 메인스레드에서 일어났다.

전략: 페이스북의 AsyncDisplayKit을 사용하기
얼마전에 아직 보진 못했지만 페이스북이 AsyncDisplayKit(ASDK)를 만들었다는 소식을 접했다. 페이스북은 그들의 제스처와 애니메이션 특징의 앱(Paper)를 위해 2014년 10월부터 ASDK를 커스텀하여 만들고 있었다. 그들은 UIView의  스레드-세이프 추상화를 통해 메인스레드에서 뷰를 랜더링하는데, 초당 60프레임이 유지되도록하여 앱이 부드럽게 동작하게 만들었다. 추가로 ASDK는 "Intelligent Preloading"이라 불리는 강력한 API를 가지는데, 이것은 서버로부터 응답이 필요한 컨텐트 "목록"을 사용자가 스크롤하는 곳에 네트워크 호출과 불러오기를 효율적인 방법으로 다룬다.


우리는 이번 기회에 이것을 확인하여 사용해보자고 마음을 먹었고, 몇 시간 안에 텍스트만 갱신하면서 스크롤이 점프해서 왔다갔다 하지 않는 v6.0을 만들었다.

AsyncDisplayKit이 셀 랜더링을 처리해줌으로서 heightForRowAtIndexPath:나 estimatedHeightForRowAtIndexPath: 구현 없이 정확한 높이 값을 받아냈다. 이 컨셉 증명을 통해 아이폰5C에서 부드러운 60프레임을 확인할 수 있었다(아이폰6+에서는 이미 더 좋은 퍼포먼스를 냈다)

AsyncDisplayKit은 UI를 만들기위해 "Nodes"를 사용하는데, 우리의 바뀐 셀이 ASCellNode가 되도록 다시 만들어야했다. 또한 다른 요소를 새로운 클래스로 추상화하여 셀의 구조를 정리하는데 시간을 썼으며, 2800줄이나 되던것을 945줄까지 줄이게 되었다. ASCellNode는 또한 컬랙션 뷰나 테이블 뷰 둘다와 함께 양립해서 사용할 수 있다는 이점도 얻을 수 있었는데, 이것이 미래의 기능을위해 리팩토링 한 후에 얻은 레이아웃 유연성이다.

ASDK의 레이아웃은 CSS Flex Box 모델을 기반으로 하는데, 이것은 현재 오토 레이아웃 코드의 시작점중 일부이기도 하다. 그러나 Inset, Overlay, Center, Stack등을 사용하여 레이아웃 기능을 꽤 쉽게 사용할 수 있다. 우리는 빠르게 바뀐 셀을 레이아웃에 구성할 수 있었으며, 아이패드에서 읽을 수 있는 레이아웃 가이드로 방향 전환과 고정된 폭 지원까지 해줌으로서 오토 레이아웃보다 더 깔끔하게 보이게 되었다.

AsyncDisplayKit의 소개때부터 핀터레스트는 페이스북과 협력하여 AsyncDisplayKit v2.0 작업을 함께 하고 있었다. 우리는 항상 페이스북과 핀터레스트 iOS 앱 둘다의 퍼포먼스에 놀랐었다. 핀터레스트 앱에서 순식간에 핀을 통해 스와이핑할때는 정말로 인상적이고, 좀처럼 로딩화면에 도달하지 않는다. 우리가 보았던 그들의 포퍼먼스 증진을 큰 인상을 받아왔는데, Buffer iOS앱의 다른 영역에서 ASDK를 사용하여 탐험할 수 있음에 흥분을 가라앉힐 수 없었다.

아래 비디오는 개발하는 중에 찍었는데, 새로 코딩한 버전의 v5.6 큐를 보여준다. 여러분은 셀 높이를 계산하는 iOS가 스크롤이 점프해서 왔다갔다하는것이 적어지면서, 건더뛰는 프레임도 줄었다는 것을 알 수 있을 것이다.

다음으로 할것 + 여러분의 생각
다른 오픈 소스 애플리케이션을 ASDK 예제와 곧 공유하여 다른 사람들이 자체 애플리케이션에서 ASDK를 사용할 수 있게 도움이 되면 좋겠다. 첫 번째 버전은 Buffer iOS 앱에서 업데이트를 재정렬하는 데 사용되는 코드이다.

프로젝트에서 ASDK를 사용해볼 기회가 있다면, 우리는 여러분의 경험을 들어보고 싶다. 부드러운 스크롤링에대한 다른 팁이나 질문이 있다면 코멘트를 달아달라. 



WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

포트폴리오 페이지를 제작하고있다. 보통 포트폴리오 제일 하단에 footer를 만들어서 자신의 소셜 링크를 달아놓는다. 그 부분을 쉽고 깔끔하게 구현하는 코드를 기록해놓았다.

결과는 아래와 같다.

참고 :  http://fortawesome.github.io/Font-Awesome/examples/#
더 많은 아이콘 : 
http://bootstrapheroes.com/semantriademo/downloader/light-blue/ui_icons.html


WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,

준비할것

카카오톡 API 홈페이지 들어가서 준비를 한다.
- 나머지 프레임 워크도 추가한다.

#import <MessageUI/MessageUI.h>
#import <KakaoOpenSDK/KakaoOpenSDK.h>
#import <Social/Social.h>
#import <Accounts/Accounts.h>


MFMessageComposeViewControllerDelegate 델리게이트를 등록한다. 메시지 전송 팝업을 컨트롤 할때 사용한다.

@interface ViewController () <MFMessageComposeViewControllerDelegate> {

}
@end


아래 코드는 실제 동작하는 코드

- (void) shareWithIndex:(NSInteger)buttonIndex text:(NSString *)text image:(UIImage *)image imageURLString:(NSString *)imageurl/*카톡 이미지 공유에서 쓰임*/ url:(NSURL *)url { if (buttonIndex==0) { // 문자 메세지 [self shareMessageWithText:text image:image url:url]; } else if (buttonIndex==1) { // 카카오톡 if ([KOAppCall canOpenKakaoTalkAppLink]) { // 카카오톡 공유 [self kakaoWithText:text image:image imageURLString:imageurl url:url]; } else { // 카카오톡 설치 [self openInstallKakaoAlert]; } } else if (buttonIndex==2) { // 페이스북 [self shareWithServiceType:SLServiceTypeFacebook Text:text image:image url:url]; } else if (buttonIndex==3) { // 트위터 [self shareWithServiceType:SLServiceTypeTwitter Text:text image:image url:url]; } } #pragma mark - 메시지 - (void) shareMessageWithText:(NSString *)text image:(UIImage *)image url:(NSURL *)url { if(![MFMessageComposeViewController canSendText]) { UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:@"메시지 보내기 기능을 지원하지 않습니다." message:@" " delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [warningAlert show]; return; } else { MFMessageComposeViewController *controller = [[MFMessageComposeViewController alloc] init]; if([MFMessageComposeViewController canSendText]) { controller.body = [NSString stringWithFormat:@"%@\n\n%@", text, url]; // controller.recipients = recipients; controller.messageComposeDelegate = self; NSData *data = UIImageJPEGRepresentation(image, 0); [controller addAttachmentData:data typeIdentifier:@"image/jpg" filename:@"thumbnail"]; [self presentViewController:controller animated:YES completion:nil]; } } } #pragma mark - 메시지 전송 delegate - (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result { [controller dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - 카카오톡 - (void) kakaoWithText:(NSString *)text image:(UIImage *)image imageURLString:(NSString *)imageurl url:(NSURL *)url { // 카카오톡 KakaoTalkLinkAction *androidAppAction = [KakaoTalkLinkAction createAppAction:KakaoTalkLinkActionOSPlatformAndroid devicetype:KakaoTalkLinkActionDeviceTypePhone marketparam:nil execparam:@{@"kakaoFromData":[NSString stringWithFormat:@"{seq:\"%@\", type:\"%@\"}", self.dataInfo[@"contentsSeq"], self.dataInfo[@"contentsType"]]}]; KakaoTalkLinkAction *iphoneAppAction = [KakaoTalkLinkAction createAppAction:KakaoTalkLinkActionOSPlatformIOS devicetype:KakaoTalkLinkActionDeviceTypePhone marketparam:nil execparam:@{@"kakaoFromData":[NSString stringWithFormat:@"{seq:\"%@\", type:\"%@\"}", self.dataInfo[@"contentsSeq"], self.dataInfo[@"contentsType"]]}]; NSString *buttonTitle = @"앱으로 이동"; NSMutableArray *linkArray = [NSMutableArray array]; KakaoTalkLinkObject *button = [KakaoTalkLinkObject createAppButton:buttonTitle actions:@[androidAppAction, iphoneAppAction]]; [linkArray addObject:button]; /*[NSString stringWithFormat:@"%@ (%@)",[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"], LOC(@"msg_invite_kakao", @"경영전문대학원 MBA 모바일 주소록 앱")]*/ if (text) { KakaoTalkLinkObject *label; label = [KakaoTalkLinkObject createLabel:text]; [linkArray addObject:text]; } if (imageurl && image) { KakaoTalkLinkObject *kimage = [KakaoTalkLinkObject createImage:imageurl/*self.dataInfo[@"thumbnail1"]*/ width:image.size.width height:image.size.height]; [linkArray addObject:kimage]; } [KOAppCall openKakaoTalkAppLink:linkArray]; } - (void) openInstallKakaoAlert { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"카카오톡이 설치되어 있지 않습니다." message:@"카카오톡을 설치하겠습니까?"// @"Do you want to install the KakaoTalk?" delegate:self cancelButtonTitle:@"취소" otherButtonTitles:@"확인", nil]; alert.tag = 141; [alert show]; } #pragma mark - Alert View Delegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (alertView.tag==141) { if (buttonIndex==[alertView cancelButtonIndex]) { // cancel } else { // 카카오톡 링크로 이동 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://itunes.apple.com/kr/app/id362057947"]]; } } } #pragma mark - 페이스북 트위터 - (void) shareWithServiceType:(NSString *)serviceType Text:(NSString *)text image:(UIImage *)image url:(NSURL *)url { if ([SLComposeViewController isAvailableForServiceType:serviceType]) { SLComposeViewController *mySLComposerSheet = [SLComposeViewController composeViewControllerForServiceType:serviceType]; if (text) [mySLComposerSheet setInitialText:text]; if (image) [mySLComposerSheet addImage:image]; if (url) [mySLComposerSheet addURL:url]; [mySLComposerSheet setCompletionHandler:^(SLComposeViewControllerResult result) { switch (result) { case SLComposeViewControllerResultCancelled: NSLog(@"Post Canceled"); break; case SLComposeViewControllerResultDone: NSLog(@"Post Sucessful"); break; default: break; } }]; [self presentViewController:mySLComposerSheet animated:YES completion:nil]; } else { [[[UIAlertView alloc] initWithTitle:@"실패" message:@" " delegate:nil cancelButtonTitle:@"확인" otherButtonTitles:nil] show]; } }


페이스북, 트윗 등등 다른 기본 소셜 공유기능을 사용하려면

SOCIAL_EXTERN NSString *const SLServiceTypeTwitter NS_AVAILABLE(10_8, 6_0);
SOCIAL_EXTERN NSString *const SLServiceTypeFacebook NS_AVAILABLE(10_8, 6_0);
SOCIAL_EXTERN NSString *const SLServiceTypeSinaWeibo NS_AVAILABLE(10_8, 6_0);
SOCIAL_EXTERN NSString *const SLServiceTypeTencentWeibo NS_AVAILABLE(10_9, 7_0);
SOCIAL_EXTERN NSString *const SLServiceTypeLinkedIn NS_AVAILABLE(10_9, NA);

상수 스트링을 사용하면 된다. 이 포스팅에서는 SLServiceTypeFacebook, SLServiceTypeTwitter 만 사용했다.


WRITTEN BY
tucan.dev
개인 iOS 개발, tucan9389

,