Architecture에 대해 고민해보자 - 클라이언트 편

사실 기본적인 아키텍처는 짜여있는 것이나 다름없다. web frontend는 React framework를 기반으로 하고, backend는 Amazon lambda를 사용하고, 아이폰과 안드로이드 앱은 React Native를 사용할 것이기 때문이다. (그리고 이 모든 것들을 배우면서 진행해야 한다! 개꾸르..잼..)

오늘 고민해 볼 내용은 좀 더 세부적인 내용들이다. 특히 React framework를 사용하지만, 가장 중요하게는 데이터의 흐름을 어떻게 제어하고 관리할 것인지, 이를 위해 어떤 철학이나 패턴을 사용할 것이며, 그러기 위해 백엔드는 어떻게 연결할 것인지 등을 이야기해보고 싶다.

좀 다른 얘기지만, 아이폰 앱을 만들면서 가장 크게 고민이 되었던 것이 ugly fat ViewController 였다. 뷰컨트롤러에 다 때려넣다 보니 2~3천 라인이 우습게 넘어가는 뚱뚱함에 나중에는 로직 찾아보기도 어려운 못생김이 보기 싫어 이를 간결하게 할 패턴을 찾게 되었다.

특히 앱의 완성도를 쉽게 해치는 부분 중의 하나가 여러 뷰 간의 데이터 동기화에 따른 버그이다. 뷰에 필요한 데이터를 공급하는 모델에 변화가 생기면, 다른 뷰로 건너가면서 이를 전달해줘야 하는데 (미리) 생각지 않은 뷰 이동이나 미묘한 딜레이 등으로 데이터 동기화가 제대로 되지 않은 경우 엉뚱한 자료를 보여줄 수 있다. 어떨 때는 이런 에러가 생각외로 잡기 까다로운 경우가 있다. 마이너스의 손으로 버튼을 두다다다 눌러서 의도치 않은 뷰로 팍 넘어가는 경우나 개발자가 미처 생각하지도 못한 오래된 디바이스에서는 동기화가 반박자 늦거나 여러 뷰가 쌓여있는 스택에서 뷰가 바뀌면서 동기화가 제대로 안되는 경우도 발생한다.

사실 이 부분에서 Redux라는 개념을 처음 접하고 아이폰 앱 개발에 먼저 적용(ReSwift)을 했었다. Redux에서 내가 매력을 느꼈던 점은 세 가지 principles가 있는데,

  • Single source of truth
  • State is read-only
  • Changes are made with pure functions

앱의 모든 상태를 하나의 오브젝트 트리 안에 보관하므로 말 그대로 하나의 진실! 이것저것 참조할 필요도 없고, 서로 다른 곳에 있는 데이터의 동기화 등을 고민할 필요도 없다. 또한 상태를 변경하려면 액션을 통해 새로운 상태(state)를 만드는 식으로만 가능하므로 미묘한 타이밍으로 인해 상태변경의 순서가 바뀌어서 애먹을 일이 없다. (적어도 디버그는 가능하다! 몽땅 순서대로 logging되니까) 순수함수로 된 reducer안에서 action을 만든다는 원칙도 있다. 모든 상태 변화를 예측가능하게 만들겠다는 강력한 의지가 느껴지지 않습니까? 여러분?

실제로 ReSwift를 통해 Redux을 경험하면서 걱정되었던 점도 몇 가지 있다. 우선 아무래도 코딩 양이 많이 늘어나지 않느냐는 점과 하나의 오브젝트 트리 안에 모든 것을 때려 넣으면 혹시 속도가 느려지거나 하는 골치아픔이 있지 않을까 하는 걱정도 있었다. 실제 맛을 본 경험으로 말하자면, 분명 코드 양이 늘어나는 부분이 있지만, MVVM+P 패턴을 도입한 것에 비해서는 늘어난 부분이 훨씬 적었고 (MVC -> ReSwift+MVVM+Presenter 라는 괴랄한 패턴까지 도입했다...헉) 순수 로직 때려넣는 것으로는 별 문제없다는 결론이다.

그래서 결론은 React에 상태관리는 Redux를 사용한다는 것이다. Mobx 등도 있지만 난 Redux가 좋으니까 (그리고 좀 다른 형태긴 하지만 한 번 써봤으니까..) 이걸로 꽝꽝. 그럼 또 결정해야 할 것은 Navigation Library이다.

React Native쪽은 Navigation 관련해서는 자체적으로 제공하고 있지 않다. (예를 들어 아이폰식으로 말하면 UI Navigation Bar 라든가 UI Tab Bar 같은거) 따라서 직접 구현(!) 하던가 서드파티를 이용해야 하는데, 최근 React Native쪽 Navigation Library의 대세는 React Navigation과 React Native Navigation이라고 해서 둘 다 차이점을 찾아보았다.

가장 큰 차이점은 React Navigation(이하 RN)은 안의 뷰들을 JS로 짰다는 점이고, React Native Navigation(이하 RNN)은 안의 뷰들은 네이티브 모듈이고, 그것을 브릿지로 연결해서 사용한다는 점이다. 일단 퍼포먼스 면에서 네이티브 모듈을 사용하는 RNN이 우월할 것이라는 짐작은 쉽게 가능하다. 다만 RN은 네이티브만큼의 매끄러움은 없을지라도(퍼포먼스 든 UI적인 면이든) 통일된 디자인으로 안드로이드와 아이폰을 동시에 제공하는 것에는 유리할 것이다.

스택오버플로를 좀 뒤적거려 본 결과, 느낀 바는 일단 둘 다 아직 초기 스테이지에 있는 느낌이다. 둘 다 최근에 v2를 개발하였거나, 개발하는 중이고, 대체로 RNN에 대해서 성능 면에서나 완성도 측면에서 더 우호적인 평가를 많이 접하는 반면, RN에 대해서는 완성도 측면에서 기본 기능을 수행하는데도 에러가 있다는 등 안좋은 얘기가 일색이다. 다만, 이러한 얘기들은 대체로 v1 버전의 작년 포스팅에서 언급된 내용들이고, 올해 RN은 많은 점을 개선한 v2가 나온 것으로 보인다.

결론적으로 나는 RN을 사용하려고 한다. 세 가지 이유가 있는데 하나는 퍼포먼스에서 약간의 손해를 보더라도 아이폰과 안드로이드 모두 완전히 동일한 UI를 제공하고 싶다는 것이고, (그리고 실제로 퍼포먼스 손해는 뷰가 에지간히 복잡하지 않으면 크게 문제가 되지는 않을 것으로 보인다) 두번째는 RN이 Native Module을 쓰지 않고 직접 그것을 Javascript로 흉내내어 구현한다는 점이 오히려 내게는 매력적으로 보였기 때문이다. 과연 어느 정도까지 가능할까 탐구해보고 싶다고 할까? 마지막으로는 React Community가 공식적으로 지원하는 것이기 때문에 어쨌든 다소간의 문제는 있더라도 빠른 대응 등으로 크게 문제가 되지는 않을 것으로 여겨진다는 점이다.

그리고 혹시 Native Language가 필요한 경우에 대비해 무엇을 쓸 것인지도 결정해야 한다. (지금 당장 봐서는 필요 없을 것으로 보이지만…) iOS쪽에선 지금 이 시기에서는 당연하게도 Swift가 되겠지만(Objective-C가 그렇게 빨리 사라질 줄은 몰랐다), 마찬가지로 안드로이드쪽 Native Language도 생각해봐야 할 것이다. 검색의 용이성 등을 고려하면 Java가 되어야겠지만, 이것은 공부하기 위한 프로젝트이기도 하므로, 난 Kotlin을 고르겠다.

이제 클라이언트쪽은 대충 정리가 된 것 같고, 서버에 대해서도 공부 & 고민 해봐야겠다.