AngularJS 개발자의 ReactJS 마이그레이션

프레임워크 의존성에서 벗어나기 위한 리엑트의 선택

(본 글은 2017년 7월 22일 브런치 글을 옮겨온 것입니다.)

최근 글이 매우 뜸했다. 것도 그런 것이, 학교를 마치고 나서 본격적으로 제품을 만들기 위해 가장 하고싶었던 일이 다름아닌 작년에 포기했었던 리엑트를 올해 꼭 공부해보고, 실제 적용해보고 이를 기반으로 개발환경이나 머릿속에 잡혀있는 Single-page Application에 대한 이해를 다시금 정비하고 싶었다. 그래서 근 두달 정도 공부하며 마이그레이션을 진행했다.

https://brunch.co.kr/@matthew-chang/14

사실 리엑트로 가고싶은 이유는 다양했다. 일단 개발자라면 최신 기술이 끌리지 않을 수가 없다. 나 또한, 본래 프론트야 jQuery에 기반한 단순한 Bootstrapping정도나 했지, 본래 백엔드 개발자로써 프론트앤드 개발에는 아주 크게는 관심이 없었다. 실무에서도 그랬지만, 왠만한 기능들은 대부분 jQuery를 이용하면 얼마던지 가능했으니 말이다.

그러다 2013년 쯤인가, backbone.js를 알고 나서 뭔가 프론트앤드 생태계가 변화하고 있다는 것을 살짝 알아채렸다. 그러다 2014년 접한 AngularJS는 신세계였고, 내게는 단순히 동적, 혹은 몇몇 페이지로 구성된 프론트앤드는 디자인 껍데기에 온갖 액션과 Ajax를 통한 데이터 노가다를 붙혀넣는, 그 이상 그 이하도 아니었는데 앵귤러의 MVVC구조는 내게는 혁신적이었다. 아, 프론트앤드도 백엔드 못지않게 그 구조로써의 의미를 할 수 있겠구나. 그런 생각이 들었다.

그렇게 앵귤러를 공부하고, 회사 ERP프로젝트를 약 2년간 앵귤러랑 본래 주전공(?)인 스프링 써대다가 열심히 만들면서 앵귤러에 대한 감을 익히고 제작년 미국에 와서 스타트업을 하겠다고 마음먹고 나서는 본격 앵귤러 사용에 나섰다. 그러다가 몇몇 이곳에서 프론트앤드 개발자 분들을 만나고 나서, 리엑트나 앵귤러 2.0이 본격 쓰인다는 말에 마음이 살짝 흔들렸으나, 계속해서 앵귤러 1.5를 고집, 그렇게 어느정도 내 스타트업 아이템인 ‘유라임’ 의 프로토타입을 만들 수 있었다.

앵귤러를 사용하면서 느낀 불편함

사실 앵귤러는 나같은 백엔드 출신의 개발자에게는 정말 좋은 프레임워크이다. 왜냐면 dirty checking이나 2-way binding등을 통해 뭐 원하는 DOM이면 죄다 앵귤러 디렉티브 붙히면 만들 수 있으니 말이다. 흡사 JSTL의 그것과도 비슷하다. 2009년쯤 내가 SI에서 일할때는 백엔드/프론트앤드 구분 자체가 없어서 디자이너가 PSD만들면 코더가 HTML/JS를 간단히 만들고 이를 가지고 전부 JSP로 포팅했다. 물론 나는 스프링을 썼고 템플릿 언어로 Velocity를 썼으니 HTML/JS를 가지고 또 테그 라이브러리 비슷한 것들을 로딩해다가 ‘동적’으로 만들었다. 당시 스프링 2.5를 썼고, Ajax라는 개념이 막 유행하던 시절이라 Ajax와 일반 form request를 적절히 사용했던 것 같다. 어쨌든 중요한 것은, 당시에는 아직까지 딱히 프론트/백엔드를 구분할 필요가 없었고 때문에 테그 라이브러리를 쓰던 내게는 앵귤러의 디렉티브는 친숙하게 느껴졌다. 그러다 아이폰 유행하고, 모바일 앱 개발 유행을 추세로 REST API를 토대로 백엔드와 프론트과 완벽하게 분리되는 시점에 다다렀다.

사실 시점은 나도 잘 모른다. 프론트앤드 개발을 시작한지는 이제 겨우 4년 정도 되기 때문이다. 그래도 앵귤러는 조금만 공부한다면 쉽게 프론트앤드에서도 굳이 서버를 통하지 않더라도 구현할 수 있는 많은 멋진 기능들을 ‘공짜로’ 가져다 쓸 수 있었다. 정말 지금와서도 보면 앵귤러의 그 수 많은 디렉티브들, 물론 대부분 처음부터 구현한 것보다는 jQuery시절 혹은 기존에 있던 라이브러리들을 디렉티브만 입힌 것이 많긴 하지만 그래도 그 뭔가 복잡한 순수 자바스크립트로만 짜여진 것을 해석하는 것보다는 훨씬 사용하기 쉬웠다.

문제는 시스템 자체가 갈수록 느려짐에 있었다. 사실 내가 뭐 그리 대단한 앱을 만드는 것도 아니고, 유라임이라는 자체는 사용자 데이터를 잘 입력받고 잘 표현하는 빅데이터 분석 프로그램이다. 클라이언트 단에서는 아주 큰 연산 자체가 요구되지 않는다. 그럼에도 불구하고 단순히 내가 송/수신 받는 데이터가 많다고, 혹은 서버에서 전송받은 데이터를 표현하는 데에 있어서 ‘편의상’ 죄다 바인딩을, 혹은 Angular.forEach로 이리저리 데이터 노가다를 하다 보니 이건 뭐 페이지 하나만 봐도 엄청난 데이터가 보이지 않게 이리저리 숨어있는 경우가 되었다. 

게다가 자원 재활용이 안된다. 딱히 top-down구조가 아니다 보니 뭐 매번 $rootScope에만 전역 데이터를 죄다 넣다 보니 자원 관리도 안된다. 당최 Scope라는 자체도 매번 헷갈린다. 재활용이 안되니 한번 서버를 통해 불러온 리스트를 다른 페이지에서는 동일하게 또 불러와야 하고, 아무리 뭐 controller/service로 모듈화 한다 한들 개개 컨트롤러에서는 필요한 데이터를 부르고, 또 다시 가공하는 절차를 밟아야 한다. 

마지막으로 콜백 무제한에 대한 문제다. 예전에 콜백을 이해했을때는 아 정말 콜백이라는걸 누가 만들었는지 몰라도 획기적이다 라고 생각했는데, 이제는 콜백이 들어오는 순간 거기에 들어가는 데이터는 진짜인가? 라는 생각부터 든다. deep copy가 되었는지도 모르겠고, 콜백이 호출되는 시점이 진정 모든 Ajax요청이 끝나는 것인지도 모르겠다는 생각이다. 물론 이후에는 $promise를 사용해서 조금 나아졌긴 했지만 말이다.

가장 큰 나의 앵귤러 사용의 문제점

내가 가장 크게 간과한 앵귤러 사용의 문제점은, 앵귤러도 결국 자바스크립트를 이용한 프레임워크다. 즉, 어딘가에는 앵귤러 본체의 구현체가 있고 이에 상응되는 라이브러리를 가져다가 마치 하나의 ‘앱’처럼 썼다는 말이다. 그런데 여기서 중요한건, 아무리 내가 난다 긴다 하는 개발자 이더라도 앵귤러의 코어, 혹은 여타 라이브러리까지 건들 실력은 되지 않는다. 더 정확히 말하면 이를 분석할 시간도 없을 뿐더러 자바스크립트의 깊은 곳과 디자인 패턴을 알지 모르는 이상, 앵귤러의 철학을 안다 하더라도 소스를 분석할 수 없다.

이 말은 결국 나 스스로 자바스크립트 실력이 없다고 봐야한다. 실제로도 별로 없다. 나는 약 4년 전에 HTML5와 관련된 책을 썼었다. (풀스택 개발자를 위한 HTML5 웹앱 만들기, 아직도 팔리는게 신기할 다름.) 이 책에서 나는 자바스크립과 관련된 기본적인 것들을 소개한 적이 있는데, 이를 소개한게 부끄러울 정도로 지금의 자바스크립트에는 내가 모르는 너무나도 많은 기능들이 들어가 있다.

Javascript의 버전별 지원기능 (출처: 위키피디아)

그렇다고 각각 기능들이 쓸때없는 것도 아니다. 버전에 따른 시대의 프로그래밍 패러다임을 착실히 반영하고, 순수 함수형 언어로써의 그 기능을 하는 것 같다. 그런데 내가 처음 자바스크립트를 접했던 당시가 98년이니깐, 그때야 뭐 조건문 반복문 그리고 몇몇 함수만 알면 된다 생각했지, 이후 나온 뭐 iterator나 closure이런걸 알기나 했는가. 물론 백엔드 개발자니깐.. 자바 개발자니깐 이라는 핑계도 없잖아 있지만 4년 전 앵귤러를 시작으로 프론트 개발을 시작했을 때 내가 실질적인 ‘자바스크립트’ 라는 언어를 다시 기본부터 바라보고 했느냐고 물어봤을때는 아니라는 것이다.

이것이 나는 프레임워크 의존적인 개발의 가장 큰 취약점 중 하나라고 생각한다. 즉, 자바스크립트 본연의 기능조차도 잘 모르면서 프레임워크만 알면 마치 다 안다는 식으로 말이다. 한 예로 최근 나는 C++언어 (C++ 11) 에서 람다식을 지원한다는 것을 모르고 (그전에는 C++에 버전이 있는지 조차 몰랐다.) 친구에게 함수형 표현이 C++에 없을꺼라고 호언장담하고 호되게 부끄러움을 샀던 기억이 난다.

앵귤러를 사용하면서 발생한 불만들은 결국 나의 부족한 자바스크립트 실력으로 인해 발생한 것들이었다. 

결국 이 모든 앵귤러를 사용하면서 발생한 불만들은 결국 나의 실력으로 인해 발생한 것들이었다. 자바스크립트를 더 잘 알고 사용했다면 과연 위의 문제들이 있었을까? 내 실력 부족이라는 원인을 알았으니 이를 고치기로 마음 먹었다. 그래서 리엑트를 실제 프로덕에 사용하기로 했다. 이제 베타까지 남은 시간은 약 3개월 정도, 추가 개발해야 할 모듈은 크게 3개 정도가 있다. 하지만 지금처럼 자바스크립트에 대한 이해가 없으면 백날 백엔드가 난다 긴다 하더라도 제품이 안나올 것은 뻔히 보였다. 실제 프로덕트에 적용하면서, 기존의 앵귤러를 마이그레이션 하면서 자바스크립트와 ES6를 배워보기로 한 것이다.

리엑트를 사용하며 느낀 장점

그렇게 지금 약 3주정도 마이그레이션을 진행중인데, 결과적으로는 매우 만족한다. 특히 ES6의 문법들, 특히 destructor와 Spread, Rest, const, let, for of 를 많이 쓰고 있는데 코드가 생각보다 매우 간결하게 나오고 좋다. 특히 Array.map 이나 Array.forEach, filter등과 함수형 표현인 뭐 () => {} 를 적절히 쓰니깐 정말 간결하다. 기존에 한 모듈당 컨트롤러 JS파일만 약 1200줄이 나왔는데 약 200줄 내외로 확연히 줄었다.

또한 JSX자체가 컴파일이 되니 사소한 문법 오류가 적어진다. IDE의 도움을 받을 수도 있고, Top-down방식이라서 객체의 의존 관계가 명확하다. 즉, 부모-자식 관계가 매우 명확하다. 꼭 필요하게 자식에게 전달할 개체들만 전달하면 되고, 또한 반복적인 부분을 컴포넌트로 만들 수 있다. 이 부분은 물론 앵귤러에도 Template이 있지만, include를 하는 방식이 아닌 하나의 ‘객체’를 소스에서 사용한다는 의미에서 그 개념이 확연히 다르다. 클래스나 오브젝트를 생성해서 이를 사용하게 되는데 이것 만큼 ‘프로그래밍’ 다운 것이 있을까.

한편으로는 자원 관리로 Flux패턴을 기반으로 한 Redux를 사용중인데 이 또한 Action과 함께 하나의 페이지에서 유지해야 할 자원을 적절히 들고있음으로써 쓸때없이 가지고 있을 법한 자원들을 확연히 줄일 수 있었다. 그리고 액션들간의 적절한 관계 때문에 redux-devtool들을 적절히 사용하면 해당 페이지의 어떤 사용자의 액션을 위해 어떤 정의된 액션(Action Creator)이 주고갔는지 알 수 있다. 딱히 console.log 노가다를 할 필요가 없어서 매우 좋다.

라우팅도 확연히 달라졌다. 기존 앵귤러에서 쓰던 라우팅은 하나의 파일에서, config()에서 모두다 관리했고 여기서 전부 파라미터와 URI를 정의했다. 이번 프로젝트에서는, 아무래도 최신 버전을 고집하다 보니 리엑트 라우터도 v3을 사용했는데 이게 기존 버전과는 개념이 확연히 다르다. 하나의 파일에서 관리하는 중앙집중적인 라우팅(Static Routing) 이 아닌, 각 페이지마다 라우팅을 정의(Dynamic Routing) 할 수 있고, 라우터 자체가 하나의 리엑트 컴포넌트이다 보니 재정의도 가능하다. 사실 사용하기 나름이겠지만 막 그렇게 많은 라우팅이 필요하지 않는 이상, 주로 URI 패턴으로 이뤄진 환경에서는 리엑트 라우터 v3이 꽤나 매력적인 것 같다.

그리고 가장 중요하게, 기존에 나와있는 자바스크립트 라이브러리를 대부분 그대로 사용할 수 있다. 예컨데 Moment.js 라는 시간/날짜 관련 라이브러리는 앵귤러 시절에는 디렉티브를 따로 설치하고 사용해야 했다면 리엑트는 자체만 설치하고도 사용할 수 있다는 장점도 있다. 그 외에도 리엑트만을 위한 라이브러리가 이미 많이 나와있다.

참, 콜백 무제한 문제는 ES6의 Promise를 이용해서 깔끔하게 정리할 수 있었다.

리엑트를 사용하며 부딪친 문제점 몇 가지

일단 페이지 구성을 어떻게 해야할지 몰랐다. 리엑트에서는 라우터에서 abstract페이지가 존재하면 이를 껍데기로 한 부모 페이지 정의가 가능했는데, 리엑트에서는 부모 컴포넌트를 정의하고 이를 라우터를 상속받아서 새로 PrivateRoute 와 같은 것을 만들어 줘야했다. React-router-v3의 메뉴얼을 보고 해결했다.

그리고 부모-자식간의 re-rendering문제점. 차트를 주로 사용하기 때문에 re-draw가 필수인데 아무리 리덕스에서 axios (rest api 라이브러리) 로 새로운 데이터를 받고 mapStateToProps를 한들 랜더링이 새롭게 안되서 차트가 업데이트가 안되는 것이다. 결론은 state가 바뀌어야 자식도 바뀐다는 말에 단순히 {…this.state}를 자식들에게 넘겨줌으로써 해결.

마치며

결국 이 글의 내용은, ‘앵귤러가 뭐가 문제고 뭐가 안좋고 그래서 리엑트가 멋져보이니 갈아탔다’ 라는 내용이 아닌, ‘다시금 자바스크립트에 대한 기본을 배우기 위해 리엑트로 리셋을 시켰다‘ 라는 것이었다. 작년에 리엑트 포기를 선언했을때랑은 상황이 많이 다른 지금은, 개발에 있어서도 두 세배 이상을 더 투자할 수 있을 만큼 여유가 많이 생겼다. 아마 아직 내가 여유가 없었다면, 지금도 리엑트의 마이그레이션은 주저하고만 있었을 지도 모른다.

개발자는 정말 평생 공부해야 하는 직종인 것 같다. 예전에는 그게 사실 별로였다. 한국에 있을때는 워낙 바쁘게 살다보니 개발 공부할 시간이 전혀 없었다. 공부를 하다가도 일에 치이다 보면 약 반년간의 공부 공백이 생기고, 다시 공부를 시작하면 그 사이에 무수히 많은 기술들이 나와있다. 미국에 오면 좀 나아지겠지 하던 것들도 대학원을 병행하면서 또 다시 깨어졌다. 그리고 지금, 5년간의 학업을 마치고 나니 정말 크게 여유가 생겨서 개발 공부를 신나게(?) 하고 있다.

사실 신기술을 접목시킨다는 것이 정말 트랜디 한 것이 아니라 조목조목 따져보고, 조금 더 깊게 들어가서 이게 왜 좋은거고 어떤 부분이 좋은거고.. 이런걸 아는게 더 중요하지 않을까. 리엑트도 절대 정답은 아니다. 다 장단점이 있다. 문제는 이를 어떻게 인지하느냐는 우리들의 자세에 달려있지 않을까. 개발 공부에는 비록 끝이 없겠지만 이를 꾸준히 한다 해서 나쁠 것도 없을 것이다. 그리고 그런 내공이 점점 쌓여갔을 때가 진정한 개발자가 아닐까. 그렇게 행동을 하고 계신 몇몇 개발의 대가분들, 참으로 존경스럽고 그간의 개발 공부를 하지 않았던 나를 반성하게 된다. 여튼 리엑트 더 잘 써서 다음에는 실제 배포와 운영에서의 장/단점을 쓸 수 있기를 기대해본다.