작년 말에 어찌하다가 중간에 지원사격하러 들어간 프로젝트가 있다. 이 프로젝트는 backbone.js를 사용하고 있었는데 그점이 끌려서 내가 “지원”하겠다고 “자원”했다. 그리고 아직도 끝나지 않은 그 프로젝트는 내 주업무가 되었다.
이전에 backbone.js를 잠깐 공부했던 적도 있었던지라 “대충 그냥 삽 좀 푸면 금방 익숙해지겠지”라는 생각으로 무작정 뛰어들어서 닥치고 코딩부터 했다. 얼마 안 가서 이 삽질이 그냥 대충 삽질이 아니라는 사실을 깨달았다. 사이드 이펙트의 향연 앞에 야근 도장을 찍었던 날이 며칠이었던가(참고로 우리 실이나 팀은 야근을 하지 않는 것을 권장한다. 그래서 야근할 때 눈치봐야 된다. 깨알같은 회사 자랑).
물론 핑계거리가 아예 없는 것은 아니다. 이정도 규모의 JavaScript 애플리케이션 개발은 처음이었던 데다가, 구조 자체도 다소 복잡했다. 하지만 삽질의 가장 큰 원인은 backbone.js에 대한 나의 이해 부족이라는 사실을 인정해야 할 것 같다. 거기에다가 의존성 관리 해보겠다고 무턱대고 잘 알지도 못하는 require.js를 가져다 얹어놨으니 삽질은 필연적 운명의 데스티니였다고나 할까.
인터넷에 떠도는 backbone.js에 대한 평가를 살펴보면 학습곡선에 대한 이야기가 많이 나온다. 영어로는 Learning curve라고 하던데, 한 마디로 배우기 좀 어렵다는 소리다. 그걸 몰랐던 것도 아니면서 지금 생각해보면 무슨 자신감이었나 싶다.
어쨌든 지금도 계속하고 있는 삽질 속에서도 꼰대 정신을 발휘해서 backbone.js에 대한 주관적인 나의 생각을 정리해볼까 한다.
높은 유연성은 때로는 독
프레임워크로서 backbone.js는 다양한 편리를 제공하지만 규칙을 엄격하게 강제하지 않는다. 그리고 어떠한 JavaScript 라이브러리도 쉽게 적용하여 확장할 수 있다. 따라서 똑같이 backbone.js를 이용해서 애플리케이션 개발을 진행해도 다양한 개발 패턴이 나올 수 있다.
그런데 이러한 장점은 때론 독이 되기도 한다. 프로젝트 참여자 간에 구현 방식에 대한 사전 합의가 없으면 일관성 없는 코드가 만들어질 가능성이 높기 때문이다. 일관성 없는 코드가 미치는 영향은 프로젝트 규모가 커질수록 치명적이지만, 혼자하는 프로젝트라고 해서 안심할 수 있는 것은 아니다. 규모에 따라 정도의 차이가 있을 뿐 일관성 없는 코드가 유지보수를 어렵게 만드는 요인이라는 것은 분명한 사실이니까.
backbone.js를 어떤 방향으로 사용할 것인지에 대한 명확한 기준이나 규칙을 사전에 정리해두면 좋을 것 같다.
아는 만큼 편리하다
backbone.js는 아는 만큼 편리하다. 프레임워크로서 편리한 기능을 많이 제공하지만 강제하지 않기 때문에 아는 만큼 활용할 수 있다.
예를 들어, Event Binding이나 REST API 추상화 메소드를 backbone.js가 제공하고 있다는 사실을 모르고 직접 다 구현했다고 생각해보자. 이것은 귤의 껍데기를 까놓고 알맹이를 버리는 것과 같다(내가 바로 그사람…).
그래서 backbone.js를 제대로 활용하려면 초기에 어느 정도 학습비용을 투자하는 것이 좋다. 선행학습은 삽질 예방을 위해서도 중요하다. backbone.js가 내부적으로 자동처리해주는 것들이 있는데 이걸 잘 모르면 오류가 났을 때 원인을 찾지 못해서 헤메는 경우가 종종 있다.
서두에 이야기했듯이, 내가 바로 무턱대고 backbone.js로 개발해보겠다고 덤볐다가 삽질로 눈물의 나날을 보낸 그 사람이다. backbone.js의 동작 방식을 제대로 이해하지 못하고 코드를 짜는 바람에 내 코드로 인해 생긴 사이드 이펙트를 미리 예측하기 어려웠고, 버그가 발생해도 원인을 찾지 못하고 헤메느라 많은 시간을 허비해야했다.
삽질을 직접 몸으로 겪으면서 배워나가는 것도 나쁘지 않은 배움의 방법이라고 생각은 하지만, 100세 시대 정신건강을 위해 어느 정도 선행학습 후에 backbone.js을 사용하는 게 좋지 않을까?
MVC? MVP? MVVM?
backbone.js는 특정 GUI Archictecture를 지향하지 않는다. 그리고 유연하다. 사용하기에 따라 MVC가 될 수도 있고, MVP가 될 수도 있다. 그러다보니 backbone.js를 바라보는 사람들의 관점에 조금씩 차이가 있다. 지금 당장 구글링을 조금만 해봐도 누군는 backbone.js의 view를 MV*의View라고 하며, 또 다른 누군는 Presenter라고 이야기한다. Controller라고 이야기하는 사람도 있다. 우리 동네 MVC와 옆 동네 MVC가 다른 경우는 backbone이 아니더라도 아주 비일비재하다.
개인적으로는 backbone.js를 어떤 특정 틀에 가둬서 바라보기보다는, 문제를 어떤 방식으로 해결할 것인지 정책적으로 고민하는 게 더 낫다고 생각한다. 중요한 것은 명칭이 아니라 “각 컴포넌트가 어떤 역할을 담당하고 있는가”이기 때문이다.
협업시에는 프로젝트 구성원 간의 커뮤니케이션을 쉽게 하기 위해 특정 패턴 이름을 끌어다 쓰고 싶은 경우가 있다. 그럴 때는 다같이 모인 자리에서 사용하고자 하는 패턴을 명확하게 정의해서 구성원 간의 이해도를 동일하게 맞춰주는 것이 좋다. 안 그러면 개발하다 멱살 잡는 일이 생길 수도…?
참고로 angular.js는 MV* 논쟁에 지쳐서 아예 자신들을 M-V-Whatever라고 불러달라고 하더라.
소 잡는 칼
backbone.js는 SPA 구현을 목적으로 나온 프레임워크라 무거운 축에 속한다. 그래서 작고 단순한 프로젝트에 backbone.js를 적용하는 것은 적합하지 않을 수 있다. 소 잡는 칼로 닭을 잡는 격이라고나 할까?
너무너무 식상하지만 “은색 탄환”은 없다. 결국 backbone.js도 하나의 도구일 뿐이다. 어디에든 쓰일 수 있는 도구는 없다. 심지어 어린시절 우리를 흥분시켰던 맥가이버 만능칼도 결국 만능이 아니었다.
backbone.js를 선택하기 전에 지금 직면하고 있는 문제를 backbone.js가 해결해 줄 수 있는지 다시 한 번 생각해보자.
디버깅을 어렵게 만드는 요소
backbone.js를 사용하면서 가장 짜증나는 부분이 바로 디버깅이었는데, 그 중에 하나가 어떤 객체를 인스턴스화 했는지 알기가 어렵다는 점이다.
아래와 같은 코드가 있을 때,
var User = backbone.js.Model.extend({ initialize: function() {} }); var oUser = new User();
oUser를 콘솔에 찍어보면,
화면에 이렇게 나온다.
객체 타입이 무엇인지 확인하려면 어떤 프로퍼티를 가지고 있는지 일일이 확인해야 한다. 관리하는 객체가 많지 않을 때는 큰 문제가 되지 않지만, 애플리케이션 규모가 커서 많은 객체를 관리해야 할 때가 되면 아주 골치 아픈 상황이 생긴다.
예를 들어, 레거시 코드에 대한 유닛 테스트를 작성하기 위해서 대상 메소드에 전달인자로 넘길 객체의 타입이 무엇인지 확인하고 싶은 경우가 그렇다.
var User = backbone.js.Model.extend({}); var ContactUser = backbone.js.Model.extend({}); var InvitedUser = backbone.js.Model.extend({}); var AdminUser = backbone.js.Model.extend({}); // .... var ChatHandler = function(){ // .... }; ChatHandler.prototype = { addUser : function(user){ // .... } }
위와 같은 코드가 있을 때, addUser에 어떤 User 객체를 전달해야할까? 물론 위의 코드는 좀 과장한 측면이 없잖아 있지만, JavaScript로 개발을 하다보면 언제든지 이와 비슷한 상황을 마주칠 수 있다.
처음부터 본인이 다 개발한 프로젝트라면 문제가 좀 덜 할 수도 있다. 하지만 나는 중간에 들어갔기 때문에 멘붕 상태에서 허공에 삽질을 오랜 시간 해야만 했다.
이 문제는 객체의 이름을 별도 프로퍼티에 명시해 주거나,
var User = backbone.js.Model.extend({ objectName : "User" }); var ContactUser = backbone.js.Model.extend({ objectName : "ContactUser" }); var InvitedUser = backbone.js.Model.extend({ objectName : "InviteUser" }); var AdminUser = backbone.js.Model.extend({ objectName : "AdminUser" });
타입 주석을 이용해서 해결할 수 있다.
ChatHandler.prototype = { /** * @param {InvitedUser} user */ addUser : function(user){ // .... } }
개인적으로는 두 번째 방법을 선호한다.
그 이유는 단순 확인용으로 불필요한 프로퍼티를 만들지 않아도 되고, 굳이 콘솔에 프로퍼티를 출력해보지 않아도 코드 레벨에서 타입 확인이 가능하기 때문이다. 물론 주석을 관리해줘야 하는 불편함이 생기지만 느슨한 타입 언어인 JavaScript의 특성이나, 애플리케이션 복잡도 통제, 협업 측면에서 생각해보면 이 정도 비용은 충분히 감수할만하다(반대 의견 많다 ㅠ-ㅠ).
이 문제를 변수 네이밍으로 해결할 수도 있는데 개인적으로 선호하지 않는다. 그 이유는 객체 타입을 변수명에 반영하다보면 불필요하게 변수명이 길어져서 오히려 코드 가독성을 떨어뜨리는 경우가 생기기 때문이다.
backbone.js가 지향하는 이벤트 바인딩 방식도 디버깅을 어렵게 만든다. 변경을 감지하는 포인트가 여러 객체에 분산되어 있을 경우 변경의 원인이 어느 지점인지 파악하기가 힘들다. 이는 backbone.js 자체의 문제라기보다는 pub/sub류의 패턴이 가지고 있는 한계인 것 같다.
이벤트 바인딩을 사용할 때는 이 방법이 최선인지 신중하게 생각해보고, 사용하기로 결정했다면 최대한 흐름을 단순하게 만들 것을 권장하고 싶다.
Good underscore
backbone.js하면 underscore가 바로 떠오른다. backbone.js이 제공하는 모든 객체는 underscore를 상속한다. 이를 통해 함수형 프로그래밍의 장점을 살릴 수 있다. 객체에 따라서 상속하는 기능의 범주는 조금씩 차이는 있지만 역할을 수행하는 데 필요한 기능은 모두 지원한다.
특히 underscore가 제공하는 LINQ(Language INtergrated Query) 메소드를 이용해서 Collection 객체를 다룰 수 있다는 점은 아주 매력적이다. 물론 나는 처음에 이 사실을 몰라서 직접 다 구현했다가 나중에 이 사실을 알고 이전 코드를 싹 걷어내는 삽질을 했다. 이 작업을 하면서 삽질도 계속 하다보니 대처하는 속도가 조금씩 빨라진다는 것을 깨달았다.
그래서 결론은…
처음에 글을 쓰기 시작했을 때는 하고 싶은 이야기가 아주 많을 줄 알았는데, 쓰다보니 별 내용이 없다. 평소에 생각날 때 메모를 좀 해둘 걸 하는 아쉬움이 있다. 그나마 위에서 이야기한 것도 대부분 backbone.js 자체의 문제라기보다는 일반적으로 소프트웨어 개발할 때 누구나 한 번쯤 경험하고 고민해 봤을 법한 내용인 것 같다.
어쨌든 하고 싶었던 이야기를 한 줄 요약해서,
“backbone.js는 잘 쓰면 괜찮은 녀석인데, 잘 쓰기가 쉽지 않으니까 사전에 공부 좀 하고 쓰면 좋을 것 같다.”
정도로 정리하고,
backbone을 경험해 본 다른 분들이 더 많은 경험과 지식을 공유해주실거라 믿으며 이 글을 마친다.
0개의 댓글