<< Vim에 대해 점진적으로 학습하기 | Home | 빌드시스템 Pants에 대해 >>

AngularJS 사용하면서 정리한 것들

작년부터 AngularJS 도입해 사용하면서 도움이 될만한게 뭐있나 고민하다가 초기에 알아두면 좋을법한 내용을 정리해 보려고 합니다. MVW에 대한 고민하면서 코딩을 했는지, 내가 AngularJS를 문제없게 코딩했는지 등에 대한 불안을 어느정도 회피해 줄 수 있는 생각들을 공유합니다.

AngularJS 기반 MVW 이해하기



MV* 패턴 적용의 목적은 프레젠테이션과 도메인을 격리시키는 것이다. 분리의 장점은 아래와 같다.
  • 프리젠테이션 로직과 도메인 로직이 분리되면 이해도가 올라간다.
  • 같은 베이스 프로그램을 중복코드 없이 여러 프리젠테이션에 대응할 수 있다.
  • 사용자 인터페이스는 테스트가 어렵기 때문에 그것을 분리하는 것은 테스트 가능한 로직을 유지할 수 있다.
  • 스크립트에 대한 API나 서비스로 구체화하기 위한 API를 쉽게 추가 할 수 있다.(다른 프리젠테이션 영역에서도 볼 수 있는)
  • 프레젠테이션 부분의 코드는 도메인 부분의 코드와 다른 지식과 기술이 필요하다.
1. Model 역할
데이터 관리, 서버측과 통신 등의 역할을 하며 서비스와 데이터로 나뉘어 지는데 데이터와 관련된 일반적인 작업은 Service, Factory에 위임하는 것이 코드가 깔끔해진다.
  • Service : JavaScript 클래스. 런타임은 new로 인스턴스가 생성된 싱글톤으로 취급된다. 다른 개발에서 만든 JavaScript 클래스 등을 전용할 수 있기 때문에 유연성, 사용 용이성이 좋다.
  • Factory : 실체로는 function(함수 참조). 특성으로는 내부 변수를 사용하면 함수 범위에 숨겨지며 private처럼 취급 된다.(Controller에서 참조 불가).
2. View 역할
레이아웃(HTML)과 프리젠테이션 로직으로 나눌 수 있는데 내부 로직이 복잡해지면 Directive나 Filter를 사용하면 된다. 그런데 코드는 깔끔해지나 제3자가 볼 때 이해도가 확실히 떨어진다는 단점을 가지고 있다.

3. Controller 역할
주로 이벤트 핸들링과 Data Binding에 집중하는 역할을 한다.
  • 이벤트 핸들링(Event -> Model): 이벤트를 Model에 전달해 값 변경 등을 실현한다. 이것은 ng-model에서 직접 실현하거나 ng-click 등으로부터 Model의 function을 부르는 형태로 구현될 수 있어 다양한 방법들이 존재한다.
  • Data Binding(Model -> View): 모델의 변경을 View에 반영한다. 이것은 Angular.js는 의식할 필요가 없다 ($watch 통해 Model의 변경이 감시되고 변경되면 자동으로 업데이트 됨).
다음부터는 AngularJS를 잘 이해하지 못하고 코딩을 하면 돌아는 가는데 문제가 될 여자가 많은 코드를 생산할 수 있어서 이를 도구를 통해 미연에 조금이나마 방지할 수 있는 방법들을 설명한다.

AngularJS Hint 사용하기

AngularJS를 사용하여 개발한 응용 프로그램에 통합 실행하면 오류가 발생 주거나 모범 사례를 준수하는지 여부를 확인하여 준다. 그리고 현재의 angular-hint 프로젝트 상태는 WIP(Work In Progress)라고 써 있는 것으로 보아 아직 개발중인 것 같다.

1. angular-hint 사용 방법
angular-hint를 사용하려면 github에서 소스를 가져 와서 빌드하거나 npm을 통해 가능하다.
$ npm install angular-hint

node_modules/angular-hint/dist/hint.js라는 파일을 찾아서 해당 프로젝트에 카피해서 사용하면 된다. 사용 방법은 아래와 같다.
angular.js 뒤에 hint.js를 읽어들이게 하고
<script type="text/javascript" src="resources/angularjs/hint.js"></script>
ng-app을 지정하는 부분에 ng-hint를 추가하면 된다.
<html lang="ko" data-ng-app="App" ng-hint>

2. angular-hint 표시되는 메세지 정보
1번에서 설명한대로 한 다음 WAS를 구동해서 console 로그를 보면 오류 등의 정보를 확인할 수 있다.


여기서 알려주는 내용은 Modules, Controllers, Directives, DOM, Events, Interpolation의 6 종류의 카테고리로 제공하고 메세지 수준은 오류, 경고 및 제안 등 세가지로 분류되어 표시된다.

AngularJS 성능 관련

AngularJS는 Batarang이란 편리한 도구(Chrome Developer Tools 확장 기능)가 존재한다. 이 도구는 AngularJS의 디버깅이나 성능 메트릭스 등에 참고할만한 정보를 보여줘 개발시에 참고하면 좋다. 그리고 시작은 AngularJS 탭의 Enable 체크만 하면 된다.


AngularJS는 양방향 데이터 바인딩의 구조가 매우 유용하지만 성능에 영향을 줄 수 있는 이슈가 나올 수 있어 성능상의 병목을 짚어보는 것은 중요하지만 먼저 이 구조를 이해하는게 더 중요할 수 있다.

1. 데이터 바인딩 구조 이해
데이터 바인딩 메커니즘의 실현 방법에는 여러 가지 방법이 있지만, 여기서는 두가지정도 언급하고 만다. 첫번째의 경우는 Knockout.js의 경우로 데이터의 값이 변경될 때 이벤트 리스너에서 View에 통지하는 방식을 취하고 있고, 두번째인 AngularJS는 dirty-checking라는 방식으로 구현되어 있는데, 이것은 바인딩 되는 모든 변수에 대해 특정 시간에 이전 값과 현재 값을 비교하고 값에 변화가 있으면 DOM을 갱신하는 구조이다.

이를 순차적으로 기술해 보면
- HTML을 분석하여 지시문(directive)을 컴파일할 때 바인딩되는 변수를 $scope.$watch()로 등록한다.
- $rootScope 자식들의 scope를 차례로 검색해, watch에 등록된 모든 변수의 변경 검사를 수행한다. 이 과정을 $digest 루프라고 부르고, 아래와 같은 타이밍에서 실행된다.(정기적인 폴링은하지 않습니다)
  • $scope.$apply()를 부를 때.
  • DOM 이벤트 (onChange, onClick 등)가 발생한 후.
  • $http와 $resource에서 응답이 돌아왔을 경우.
  • $location에서 URL을 변경한 후.
  • $timeout이벤트가 발생한 후.
- 변경 검사가 완료되면 변경된 부분의 DOM을 다시 시작한다.

2. 개선 포인트는 어떻게
- 불필요한 $scope 변수나 $scope.$apply()를 하지 않는다.
$digest 루프에서는 watch하고 있는 변수의 수와 변수의 비교 처리 시간이 성능에 크게 영향을 준다. 그래서 watch 변수의 수는 가능한 적게, 객체의 비교 처리는 가능한 한 가볍게하는 것이 좋다. watch 변수의 수 기준은 2000개 이하, 비교 처리 시간의 기준은 25μsec라고 알려져 있다.(여기)
Batarang을 사용하면 $scope의 트리 구조를 시각화되므로 watch하고 있는 변수의 수를 확인할 수 있다.

- $watch에서 무겁고 느린 처리를 만들지 말자.
그러면 응용 프로그램 전체가 느려집니다. JavaScript가 단일 스레드인 특성상 $digest 루프는 단일 스레드에서 처리된다.

- 예기치 않는 동작을 방지하도록 될 수 있으면 angularjs 모듈들을 사용하자.
  • setTimeout 대신 $timeout을 사용.(angular의 digest와 apply주기에 따라 setTimeout에서 수행 한 작업이 angular에 반영되지 않는 경우가 있기 때문에, 아래도 비슷한 이유이다.)
  • window 대신 $window를 사용.
  • document 대신 $document를 사용.
  • $.ajax 대신 $http를 사용.

- 다른 콘트롤러와 통신할 때 주의하자.
$emit, $broadcast, $watch를 사용하게 되는데, 일반적으로 $watch는 $emit(자신을 포함하여, 자신의 윗쪽 scope로 이벤트를 전달)이나 $broadcast(자신을 포함하여, 자신의 아랫쪽 scope로 이벤트를 전달) 같은 event 방식에 비해서 성능 소모가 많다고 알려져 있다.(여기)
$rootScope.$broadcast의 경우, AngularJS가 관리하는 모든 scope에 방문해야 하기 때문에 성능 문제가 있을 수 있다. 기본적으로 통신은 줄이데 공유 데이터는 서비스에서 처리하도록 하자.

- ngShow/ngHide/ngIf를 적정한데 사용하자.
ngShow/ngHide는 CSS를 통해 표시를 전환하고 ngIf는 DOM의 추가나 삭제를 할 수 있게 되어 있다. 그래서 ngShow/ngHide는 ngRepeat와 같이 쓸경우 리스트가 많게 되면 초기 로드가 느릴수 있고 화면 전환(보이고/안보이고)은 빠르게 되고 반대로 ngIf는 초기 로드는 빠르지만 화면 전환이 느린 특성이 있으니 잘 활용하자.

- watchers 수를 줄이자.
양방향 데이터 바인딩이 angularjs를 선택하는 요인이자 싫어하게 되는 양날의 검이다. 이는 양방향 바인딩이 가능한 배경은 바로 dirty-checking 때문이다. 어플리케이션을 개발하다보면 dirty-checking 하는 대상이 늘어나게 되는데 이는 성능을 나쁘게하는 요인이 된다. 그래서 해결책으로 양방향 데이터 바인딩이 필요없는 변수들은 1.3버전의 One-time bindings(::)을 사용하거나, 오픈 소스인 bindonce를 사용하면 watch 수를 줄여줘 ng-repeat 등을 사용시 성능 효과를 볼 수 있다.
bindonce를 사용하지 않는 경우사용한 경우를 테스트 해보라.

- angular 콘트롤러를 Lazy load하자.
콘트롤러에서 초기 부하가 많은 어플리케이션의 경우 Lazy load 기능을 넣어 동적으로 로딩을 한다면 어플리케이션 로딩 속도를 향상시켜줄 수 있다. angularjs-lazy-loading-with-requirejs를 참고해 사용해 보자.

- 기타
  • ng-cloak : 페이지 로딩 중 등에서, angular 처리되기 전에 마크업이 보여 버리는 것을 방지한다.
  • 변수나 메서드 이름에 $접두사를 사용하지 않는다. 이 접두사는 AngularJS에서 예약되어 있다.
  • $scope를 더럽 히지 말자. 템플릿에서 사용하는 메소드나 변수만 추가하자.
  • 전역 변수를 사용하지 말자. 의존성 주입을 사용하여 모든 종속성을 해결하자.
  • 콜백 대신 promise($q)를 사용한다. 그러면 코드는 더 우아하고 깨끗한되고 콜백 지옥에서 벗어나게 해준다.
  • 가능할 땐 $http 대신 $resource를 사용. 높은 수준의 추상화는 번잡한 작업으로부터 해방시켜 준다.
  • 될수 있으면 angularjs 내장 함수들을 사용하자. angular.element, angular.forEach 등.

[참조 사이트]



Add a comment Send a TrackBack