타일은 2015년 개발을 시작했다. 하필이면 프론트엔드의 격변기에 프론트엔드 개발이 위주인 프로젝트를 시작한 것이었다. 살려줘... 처음엔 Meteor + React 조합으로 시작했다가, 백엔드에서의 RDMS 회귀 유혹을 참지 못하고 Meteor를 걷어내고 Laravel(Mysql)+ React 조합으로 백엔드 변경을 한 번 했다. 이후 부분적으로 Vue와 React + Mobx 조합을 시험하다가, 최근 메이저 업데이트의 스택으로 React + Mobx + Typescript로 결정하게 되었다. 안녕 Vue... 이 글은 각 기술들을 사용해보면서 겪었던 점에 대한 후기이다.
Meteor
지난 몇 년간 전통적인 관점으로 백엔드는 DB 및 API, 세션, 권한, 라우팅, SEO를 책임지도록 해왔고, 프론트엔드는 사용자에게 환경을 제공하는 역할의 개발을 해왔었다. 그런데 당시 지인이 Meteor를 공부한다기에 대체 이게 뭔가하고 봤다. Universal하게 접근하려는 니즈는 있었지만 어디까지나 View 정도에 한정된 것이었지, 모든 백엔드와 프론트가 같은 라우트, 모델을 공유하는 그런 전방위적인 통합은 아니었다. 근데 Meteor가 바로 그걸 해준다. 쩐다는 말밖에...!
뭐 어떻게 했는지는 모르겠지만 코드 하나로 백엔드와 프론트를 같이 처리할 수 있다는 너무나 획기적이었다! 그래서 당시에 앞으로 할 이 프로젝트(타일)은 이걸로 하겠다고 생각하고 열심히대충 공부했다. React와 함께 개발했지만, 결국은 이 기술을 포기했다. 가장 중요한 포기 사유는 실력부족과 기존부터 들여온 습관에 대한 문제였던 듯싶다.
- MongoDB를 메인 DB로 쓰는 게 싫었다. JSON Document 구조라는게 화면 표현과 구조화에서는 유리하지만, 빠른 스키밍 및 탐색에는 쥐약인 점이 있었고, 한국에서 속도감있는 MongoDB PaaS를 찾지 못했었다. MySQL 라이브러리도 있었지만 PHP에서의 오랫동안 구워진 그것들처럼 ORM이 만족스럽지 않았다.
- 끊임없이 아싸와 비주류를 추구하는 성향이지만, 개발에서마저도 비주류이고 싶지는 않았다. Meteor는 Blaze라는 프론트 기술을 공식으로 걸고 있었고, 모든 레퍼런스 들이 그 조합을 기준으로 하고 있었다. Meteor + MySQL + React 등의 조합도 구성할 수 있고 쓸려면 쓸 수 있었지만 계속하면 할수록 뭔가 불안하다는 느낌이 계속 있었다. Meteor 자체도 MEAN 계열에 밀려 비주류인데, 그 비주류안에서도 비주류인 셈이다.
- 백엔드와 프론트를 하나로 통일한 것은 정말 충격 그 자체였다. 코드 자체도 엄청 줄었다. 그런데 문제는 구조 설계 시 전통의 관점과 적응되지 않는 점들이 많았다. 모든 것이 반대로 느껴졌다. 기존에는 어느 부분이 백엔드와 프론트가 실시간으로 연결되어야하는가의 고민이었다면 Meteor에서는 반대로 어느 부분이 달라야하는가를 고민해야했고, 싱크되어서 편리해진만큼 백엔드와 프론트를 구분하기 위하여 들어가는 작업도 의외로 상당히 많다는 것을 깨달았다. 개발의 문제를 백엔드의 문제/프론트엔드의 문제로 양분해서 역할과 책임을 확실히 구분짓고 선택과 집중을 하는 입장에서 이러한 개발방식은 잘 적응이 되지 않았다.
- 이건 서비스의 특성 문제도 있었는데 타일의 핵심적인 부분에서 실시간으로 데이터를 백엔드와 싱크셔야할 필요성이 있지는 않았다. 이거 배우면서 만들어 본 실시간 타자 대결 서비스에는 꽤 만족스러웠다. 향후에 토이프로젝트나 실시간 데이터 싱크가 중요한 서비스에서는 다시 미티어를 공부해 볼 생각이다.
React
좀 먼 얘기이긴 하지만 이거 접하기 전엔 기존 서비스 개발시 다 jquery를 사용했었다. 사실 타일까지도 jquery를 완전히 걷어내지 못하고 같이 쓰고있다(DOM조작의 유혹). 당시에도 React가 한창 뜨고있을 때라, 존재를 알고는 있었는데, 실무는 급하고 배움의 길은 멀다는 핑계로 타일의 프로토타입도 jquery로 시작했었다.
그런데 프론트에서의 동작이라는게 결국 DB에서 어떤 형태(모델)을 불러와서 화면에 구현(렌더링)하고, 화면상에서의 이용자 피드백(인터렉션)을 다시 그 모델에 반영하고, 그 모델을 DB에 다시 저장하는 과정으로 요약할 수 있는데.. 이거 jquery로 다 하려니까 할 수는 있겠지만 특정 동작의 발현에 대한 책임이 누구인지 명확하지 않았고 따라서 유지보수는 과연 누가 할 수 있을까 걱정이 되기 시작했다. 자기가 싼 똥은 자기가 치워야 되니까...
Meteor를 선택하는 거의 동시에 React도 같이 시도를 해보았다. Meteor에는 Blaze라는 기술이 공식이었고 분리된 프로젝트라고 해도 결국 Meteor하고만 잘 놀 것 같은 느낌에, 독립적인 프론트 기술인 React로 집중을 하였다. 결과적으로 React는 너무나 만족스럽고 앞으로도 계속 사용할 놈이라고 생각하고 있다.
- 기존에는 DOM을 직접 조작하고, 필요한 데이터가 생기면
아무렇게나자바스크립트 객체로 만들거나 DOM에 data-attribute나 class를 물리는 방식으로 구현했었다. React 이후로 공식 패턴대로 모든 것을 상태 모델과 렌더링 구조, 그리고 액션(메써드) 구분하게 되었는데, 이것은 개발 시 집중하는 포인트를 명확하게 구분시켜주는데 도움이 되었다. 아무리 복잡한 인터페이스도 구조를 잘 짜면 결국 단순화된 컴포넌트로 쪼개지고, 각 컴포넌트들을 잘 조합하면 이룰 수 있다. - 다만 React 프로젝트를 여러 번 구성하면서도 아직까지 어떤 폴더 구조가 최적인지는 감을 잡지 못했다. 프로젝트마다 다르다고 생각이 들면서도, 그래도 뭔가 손이 가는.. 마음에 딱 들어맞는 구조가 없을까 싶은데 아직 답을 못 찾았다. 그나마 자주 쓰는 방법이 각 페이지별(혹은 기능별)로 컨테이너 컴포넌트들이 있는 폴더와 재사용할 수 있는 프리젠테이셔널 컴포넌트 폴더로 크게 양분하는 것이다. 상태관리를 Mobx로 하게 됨에 따라 상태와 리듀서(혹은 액션) 역할을 하는 것을 클래스 단위로 구분하고, 그것들을 모아둔 모델 폴더까지 포함하여 세 폴더로 구분하는 것을 지향하고 있다.
- 함수형 프로그래밍에 큰 관심은 없었는데, React 패턴을 다루게 되면서 컴포넌트를 순수 함수형으로 계속 지향하다 보니, 함수형에 관해서 관심이 많이 늘었다.
Mobx
React 형식의 선언형으로 디자인을 하게 되면, 그 상태 모델이 필요하다. 이 상태 모델을 화면에 어떻게 렌더링하고, 사용자의 인터렉션에 반응하여 이 상태 모델을 어떻게 바꿀 것인가를 고민하는 것이다. 기본적으로 리액트는 자체적으로 state라는 상태를 가지고 있고, 필요하면 다른 컴포넌트로 내려주거나 역으로 올려 받을 수도 있다. 문제는 이렇게 컴포넌트 간 상태를 주고받는 것이 쉽지 않다. 손이 많이 가고 짜증난다.
React 생태계에서는 상태관리 도구로 Redux(Flux 패턴 라이브러리)를 사실상 공식으로 채택하고 있고, 이것을 기준으로 또 수많은 라이브러리들이 생성되어 있다. 그런데 Flux 패턴을 처음 접했을 때, 이런 녀석에 대한 필요성은 공감하였으나.... 액션이라는 놈을 만들고, 액션을 만들어주는 액션 생성함수를 만들고, 또 그놈을 건네줄 때 디스패처라는 놈을 쓰고, 이런 놈들을 처리하는 리듀서를 만들고, 그놈들을 또 결합하고 연결하고, 컴포넌트에 MapToProps는 MapToDispatch니 또 만들고, 프로바이드하고 인젝트하고.. 이런 것들이 배보다 배꼽이 더 커보였다(나중엔 이 구조가 좋다는 걸 깨달았다)
Mobx는 상태를 그냥 자바스크립트 객체처럼 만들고, 바꿀 일이 있을 때 대입 연산자로 그냥 값을 바꾸어주면, 컴포넌트가 그 상태를 감지하고 있다가 자동으로 변경된다. 그러면서도 여러 컴포넌트에서도 별도의 과정 없이 바로 적용이 되었다. Meteor를 처음 접할 때처럼 신선한 충격이었다.
하지만 Mobx의 이러한 점은 사실상 프로젝트가 복잡해지고 협업을 하게 되면 난관에 빠질 수 있는 함정이 있다. 바로 단일 책임의 원칙이 깨진다는 것이다. 어디서든 누구나 바꿀 수 있고 그 변화를 추적하기가 어려워서, 진정한 단뱡항 데이터 흐름이라고 말하기가 애매한 부분이 있었다.
그래서 이러한 문제를 해결하기 위하여 최근에는 커뮤니티를 중심으로 Mobx State Tree(이하 MST)라는 놈이 나온 것으로 아는데, 얘는 각각의 클래스 내에서 자신이 관할하는 상태와 그것을 변경하는 액션과 리듀서를 모아놓은 형상이고, 자료의 변화부분을 자동으로 감지하고 로깅까지 해준다.
MST에서 가장 마음에 들었던 것은 모델별 빡빡한 타입 정의였는데, 이것은 Typescript와 병행을 하면 되는 부분이므로, 우리는 다음 프로젝트에서 그냥 Mobx + Typescript 조합으로 가기로 했다. 대신 MST의 관점을 가져와서 모델별 클래스를 구성하고, 이것을 루트 Store에서 합친 이후에, Provider/Inject하는 구조로 가기로 하였다. 결국 체계를 잡다보면 Redux화 된다?
Typescript
가장 최근에 배우고 있고, 또 도입하고 있는 기술이다. 우리가 주력으로 하는 PHP나 자바스크립트 모두 느슨한 타입을 지향하고 있는데, 프로그래밍할 때 변수라는 놈이 숫자건 문자열이건 크게 개의치 않는 관대한 놈이라는 것이다. 관대해서 일단 뭔가 만들기에는 최적이다. 하지만 프로그램이 복잡해지기 시작하거나 유지보수를 하게 되면 어떤 문제의 원인을 찾는 것이 다소 어려운 부분이 있다. 심지어 뭐가 문제인지도 모를 수도..
Typescript는 이러한 문제를 해결하기 위하여 개발시에 빡빡한 변수의 타입을 지정하도록 강제하고, 대신 운영되는 코드에서는 다시 원래대로.. 즉, 개발자를 빡세게 감시하는 그런 녀석이다. 그래서 최초에 이 기술을 채택할 때에는 왜 사서 고생하나 하는 생각이 들었다.
이 Typescript를 영화에 비유하자면 위플래시다. 내가 드러머고 Typescript가 교수다. 컴퓨터 앞에 앉아서 열심히 코딩하고 있으면 그 빡빡이 교수가 실시간으로 내 귀에 소리를 빽빽 지르며 ”그따위로 코딩하냐!! 어디서 배워먹은 코딩 습관이냐!!!! 더 정확하게!! 그거 말고! 저거 말고!' 이러면서 간섭하고 소리를 지른다.
하여튼 그렇게 빡빡한 교수를 옆에 세워두고 코딩하는 일과 같은 건데, 꾹꾹 참고 쓰다 보면 어느새 잔소리가 조금씩 줄기 시작하며, 교수에서 친구가 되어가는 느낌이 들어간다. 영화에서도 마지막에 막 서로 웃으면서 합을 맞추지 않나. 실제로 어느 정도 적응하고 나서는 기존에 하던 토이 프로젝트들도 Typescript로 적용을 하고 있다.
Typescript의 단점은 최초 시작 시 빌드 설정, 모듈 관리 등 손이 많이 가고, 소스가 지나치게 복잡해 보인다는 것이다. 그런데 이것들이 실수를 방지하고 보다 더 정확한 코딩을 위한 것이라고 생각하면 그 정도는 참아줄만한 정도라고 생각한다. 사실 평상시엔 귀찮다고 자주 느끼다가.. 어떠한 이유로 중간에 구조 변경을 하게 될 때 진정한 가치를 느끼게 된다. 뭐부터 수정을 하든 수정해야 될 모든 연관 부분들을 한 번에 알려주기 때문이다.
---
다음 포스팅에선 우리팀이 구성한 React + Mobx + Typescript + Storybook 구조에 대해서 논의할 예정이다. 근데 계속 바뀌고 있다.