Fall in IT.

AngularJS 개념 및 기초잡기 본문

프레임워크/Angular Framework

AngularJS 개념 및 기초잡기

D.Y 2016. 8. 23. 00:22

안녕하세요.


오늘은 AngularJS 1의 기초개념에 대해서 알아보겠습니다.



AngularJS란?

  • AngularJS는 구글에서 만든 자바스크립트 프레임워크로, 2009년에 발표되었습니다.
  • AngularJS는 자바스크립트로 만든 client 측 MVC/MVVM 프레임워크로 모던 단일 페이지 웹 애플리케이션 개발의 정수이다.


AngularJS 특징

  • 자바스크립트로 작성할 코드량을 줄여준다. 
    - Dom을 선택하고 조작하는 자바스크립트 코드를 작성하지 않아도 됩니다.
  • 양방향 데이터 바인딩이 가능합니다.
    - 모델의 데이터와 뷰 데이터가 양방향 데이터 바인딩이 되어, 모델이 바뀌면 뷰 데이터도 같이 변경 됩니다.
  • HTML, CSS, 로직 등의 개발 영역을 명확하게 분리해줍니다.
    - 기존 자바스크립트에서는 Dom 조작과 이벤트 처리를 위해 HTML을 잘 알고 있어야 했으나, AngularJS는 뷰 코드와 로직 코드가 명확히 분리됩니다.

주요개념

  • Model(모델)
    - 단순 자바스크립트 객체로 된 데이터
    보통 JSON으로 표현되는 애플리케이션의 특정한 데이터 구조를 말합니다.
    json 데이터를 jQuery의 $.ajax 메서드를 래핑한 Angular의 $http 를 통해 XHR(XMLHttp Request)로 서버에서 가져오거나 페이지를 로딩할 때 코드에서 직접 (데이터베이스에서) 읽어오도록 할 수 있다. 그리고 모델을 변경한 다음 다시 반영할 수도 있습니다.
  • View(뷰)
    - 템플릿과 모델이 합쳐져서 보여지는 화면 (Dom구조로 되어있습니다.)
    MVC 프레임워크를 사용한다면 뷰를 갱신할 모델 데이터를 내려받은 뒤 HTML에서 해당 데이터를 보여줄 것이다.
  • Controller(컨트롤러)
    - 자바스크립트로된 로직 영역
    컨트롤러는 서버에서 직접 뷰로 접근하는 일종의 중간 통로로서 필요할 때마다 서버와 클라이언트 통신으로 데이터를 변경한다.
  • Scope(스코프)
    - 뷰와 컨트롤러 사이에서 데이터를 연결해주는 역할
    - 모델과 뷰를 감시하고, 컨트롤러에 이벤트를 보내는 역할
  • Directive(디렉티브)
    - html을 확장하는 AngularJS의 지시어
    - ex) ng-app, ng-controller, ng-click 등..
  • Data Binding(데이터바인딩)
    - 모델과 뷰의 데이터를 실시간으로 연동
  • Module(모듈)
    - 모든 자바스크립트 기능들이 ng-app="모듈명" 을 시작으로 모듈 단위로 관리됩니다.
    - 컨트롤러, 서비스, 필터 등을 포함하며, 응용프로그램의 서로 다른 기능을 구성하는 컨테이너 입니다.
  • Service(서비스)
    - 특정 기능을 담당하는 객체
    - 싱글톤 객체로 인스턴스가 하나만 존재합니다.


AngularJS 프로젝트 기초

  • 기본적으로 AngularJS공식홈페이지에서 AngularJS를 다운로드 받고, 프로젝트에 임포트 합니다.
    - CDN방식으로 추가하거나, bower, npm 등 어느 방법으로 다운받아도 상관없습니다.


  • Angular module을 생성하고, 해당 모듈을 사용할 수 있도록 연결해줍니다.
<div ng-app="myApp">
    <div ng-controller="MainCtrl">
        <!-- controller logic -->
    </div>
</div>

var myApp = angular.module('myApp', []); myApp.controller('MainCtrl', function ($scope) { // Controller magic });


Angular는 HTML과 통신하기 위해 {{ }} 와 같은 템플릿 형식의 문법을 사용합니다.
HTML에 데이터를 하드코딩하지 않아야지만(이상적으로는) Angular를 제대로 사용하는 것이다.
아래는 DOM에 간단한 문자열을 넣는 예제입니다.

<div ng-app="myApp">
    <div ng-controller="MainCtrl">
         {{ text }}
    </div>
</div>

var myApp = angular.module('myApp', []); myApp.controller('MainCtrl', function ($scope) { $scope.text = 'Hello, Angular fanatic.'; });


실행한 결과 


여기서 가장 중요한 개념은 특정 컨트롤러안에 모든 기능을 담는 $scope 라는 개념이다. $scope 는 DOM의 현재 요소/영역을 참조하며(this 와는 다르다), 요소안의 데이터와 로직을 관찰하는 기능을 가지고 있다.


정적 데이터바인딩 예제



컨트롤러는 JSON 데이터로 서버와 통신하는 함수(이벤트 함수도!)와 데이터 만을 다룬다는 걸 기억하는 게 중요하다. DOM 조작을 컨트롤러에서 해선 안된다. DOM 조작은 디렉티브로 하면 된다.


디렉티브

디렉티브(Directives from existing scripts/plugins 포스트를 참고)의 가장 간단한 형태는 애플리케이션이 필요한 곳에 여러 번 사용할 수 있는 작은 HTML 조각 형태다. 디렉티브를 사용하면 애플리케이션에 별다른 노력없이도 쉽게 DOM을 주입하거나 사용자 정의 DOM의 상호작용을 적용할 수 있다. 디렉티브는 간단하지 않을 뿐더러 러닝커브가 생각보다 꽤 높긴 하지만 다음 절부터 읽어보면 분명 도움이 될 것이다.

AngularJS에서는 이미 ngBind, ngModel등 built-in된 다양한 디렉티브를 다루고 있으며, 사용자가 AngularJS의 controller나 service를 새롭게 생성하는 것처럼 사용자정의 디렉티브를 마음껏 생성하고 확장 할 수 있습니다.  AngularJS는 템플릿으로 HTML을 지원하기 때문에 사용자정의 디렉티브를 HTML에서 매우 쉽게 적용할 수 있다는 매력을 가지고  있습니다.


서비스

서비스는 종종 헷갈리는 부분이다. 경험에 비춰보면 서비스는 기능적인  차이점을 제공하지 않으면서도 뭔가 더 좋아보이는 디자인 패턴이다. Angular 소스를 분석해보니 Angular는 같은 컴파일러를 사용하면서 많은 기능을 제공하는듯 하다. 분석해보니 서비스는 싱글톤 으로 사용해야하고 객체 리터럴이나 좀 더 복잡한 유즈 케이스처럼 더 복잡한 기능은 팩토리를 사용해야 한다.

다음 예제는 2개의 숫자를 곱하는 서비스이다.

myApp.service('Math', function () {
  this.multiply = function (x, y) {
    return x * y;
  };
});

서비스(혹은 팩토리)를 생성할때는 의존성 주입을 사용해서 Angular에게 새로 만든 서비스의 존재를 알려줘야 한다. 알려주지 않으면 컴파일 에러가 발생하거나 컨트롤러가 동작하지 않을 것이다. 컨트롤러 선언부분에 function ($scope) 를 봤을텐데 이게 바로 간단한 의존성 주입 방법이다. function ($scope) 앞에 있는 [‘$scope’] 도 봤겠지만 이건 나중에 설명하겠다. 다음 예제는 의존성 주입을 통해 Angular에게 서비스가 필요하다고 알려주는 방법이다.

// Math를 주입한다
myApp.controller('MainCtrl', ['$scope', 'Math', function ($scope, Math) {
    var a = 12;
    var b = 24;

    // 결과는 288
    var result = Math.multiply(a, b);
}]);


팩토리

팩토리로 서비스를 만드는 건 이제 간단하다. 객체 리터럴을 팩토리안에서 생성하거나 다음처럼 몇 가지 메서드를 추가하면 된다.

myApp.factory('Server', ['$http', function ($http) {
  return {
    get: function(url) {
      return $http.get(url);
    },
    post: function(url) {
      return $http.post(url);
    },
  };
}]);

Angular의 XHR을 래핑한 코드를 작성해봤다. 컨트롤러에 의존성을 주입한 다음 이렇게 간단히 사용하면 된다.

myApp.controller('MainCtrl', ['$scope', 'Server', function ($scope, Server) {
    var jsonGet = 'http://myserver/getURL';
    var jsonPost = 'http://myserver/postURL';
    Server.get(jsonGet);
    Server.post(jsonPost);
}]);

필터

필터는 배열의 데이터와 함께 사용하며 루프 밖에서도 사용 할 수 있다. 데이터를 순회하면서 특정 조건에 만족하는 데이터만 추리고 싶을 때 필터를 사용하면 된다. 예를 들어 <input>에 입력된 값으로 사용자를 추리고 싶을 때처럼 말이다. 필터를 사용하는 방법은 컨트롤러 안에 선언하거나 메서드로 정의해서 사용해도 된다. 다음은 필터를 전역으로 선언한 방법이다.



양방향 데이터 바인딩

양방향 데이터 바인딩이라는 말을 처음 들었을 때는 무슨 말인지 제대로 이해하지 못했다. 양방향 데이터 바인딩을 한 문장으로 표현하자면 완전히 동기화된 데이터 정도가 가장 좋겠다. 즉 모델 을 갱신하면  에 반영되고,  를 갱신하면 모델 에 반영되는 형태를 말한다. 이는 별다른 작업 없이도 데이터가 동기화된다는 뜻이다. 예를 들어 <input> 하나에 ng-model 을 바인딩하고 값을 입력하기 시작하면 동시에 모델이 생성(기존에 존재하면 갱신)된다.

<input>을 하나 생성해서 ‘myModel’이라는 모델을 연결해보자. 그리고 이중괄호 문법으로 모델을 정의하면 뷰와 즉시 연동될 것이다


XHR/Ajax/$http 호출과 JSON 바인딩

 $http 메서드는 Angular가 서버 데이터에 접근하는 기능을 멋지게 래핑한 메서드로 눈감고 사용할 수 있을 정도로 쉽다. 다음은 ‘GET’ 요청을 보내고 서버에서 데이터를 받아오는 간단한 예제다. 문법이 jQuery와 꽤 비슷해서 금방 이해할 수 있을 것이다

myApp.controller('MainCtrl', ['$scope', function ($scope) {
  $http({
    method: 'GET',
    url: '//localhost:9000/someUrl'
  })
  .success(function (data, status, headers, config) {
    // 성공! 데이터를 가져왔어
  })
  .error(function (data, status, headers, config) {
    // 이런. 뭔가 잘못되었음! :(
  });
}]);


표현식


동적 뷰와 라우팅

단일 페이지 웹 애플리케이션(혹은 웹사이트!)에는 헤더, 푸터, 사이드바, 본문이 있고 URL에 따라 내용이 표시되는 게 보통이다.

Angular를 사용하면 동적 뷰 를 통해 이를 쉽게 설정할 수 있다.

myApp.config(['$routeProvider', function ($routeProvider) {

  /**
   * $routeProvider
   */
  $routeProvider
  .when('/', {
    templateUrl: 'views/main.html'
  })
  .otherwise({
    redirectTo: '/'
  });

}]);

URL이 ‘/’ (사이트의 루트) ‘이면’ main.html 가 주입된다는 것을 알 수 있다. 초기 뷰로 index.html 대신 main.html을 설정하는 게 좋은데 왜냐하면 index.html 페이지를 이미 단일 페이지 셋업에 사용했기 때문이다. 그리고 다른 URL에 대해서 뷰를 추가하는 것도 무척 쉽다.

myApp.config(['$routeProvider', function ($routeProvider) {

  /**
   * $routeProvider
   */
  $routeProvider
  .when('/', {
    templateUrl: 'views/main.html'
  })
  .when('/emails', {
    templateUrl: 'views/emails.html'
  })
  .otherwise({
    redirectTo: '/'
  });

}]);

이로서 이메일 목록을 보여주는 emails.html 을 간단하게 추가했다. 결국 매우 복잡한 애플리케이션을 아주 적은 노력으로 만들 수 있다.


참조


Comments