AngularJS, Hello World 출력하기

개요

AngularJS는 최근의 트렌드라 할 수 있는 SPA(Single Page Application) 개발을 지원하는 현재 가장 유명한 MVW(Model-View-Whatever) 프레임워크이다. 마지막 WWhatever인 것은 무엇이든 올 수 있다는 의미이다. 최근의 트렌드는 이 부분을 ViewModel 방식으로 구현하는 것이다.

사전지식

AngularJS를 접하기에 앞서 JavaScript 빌드 도구인 Grunt, Webpack에 대한 사전 이해가 필요하다. 아래 글을 참고한다.

package.json

프로젝트 루트에 package.json 파일을 아래와 같이 작성한다.

{
    "name": "helloworld-app",
    "version": "0.0.1",
    "devDependencies": {
        "grunt": "~1.0.1",
        "webpack": "~1.13.2",
        "webpack-dev-server": "~1.15.1",
        "grunt-webpack": "~1.0.14",
        "grunt-contrib-uglify": "~2.0.0",
        "grunt-contrib-copy": "~1.0.0",
        "grunt-contrib-connect": "~1.0.2",
        "jquery": "~3.1.0",
        "angular": "~1.5.8",
        "angular-ui-router": "~0.3.1"
    }
}
  • grunt 모듈은 Grunt 프로젝트 빌드 환경을 구성하기 위해 필요하다.
  • webpack, webpack-dev-server, grunt-webpackWebpack을 이용한 모듈 기반의 JavaScript 작성을 위해 필요하다.
  • angular, angular-ui-router는 프로젝트 소스 코드에서 사용할 라이브러리이다. <script src="..."></script> 태그를 사용하지 않고 Webpack 기반의 스코프 범위를 갖는 동적 로드를 할 것이다.

앞서 정의한 모듈을 설치한다. 프로젝트 루트에 node_modules 디렉토리가 생성된다.

$ npm install

Gruntfile.js

프로젝트 루트에 Gruntfile.js를 아래와 같이 작성한다.

module.exports = function (grunt) {

    grunt.initConfig({
        webpack: {
            dev: {
                entry: "./src/app.js",
                output: {
                    path: "dev",
                    filename: "app.js"
                }
            }
        },
        uglify: {
            dev: {
                files: {
                    'dev/app.js': ['dev/app.js'],
                }
            }
        },
        copy: {
            dev: {
                files: [
                    {
                        expand: true,
                        cwd: 'src',
                        src: ['**', '!*.js'],
                        dest: 'dev/'
                    }
                ]
            }
        },
        connect: {
            dev: {
                options: {
                    port: 8080,
                    base: 'dev',
                    keepalive: true,
                    debug: true,
                    open: true
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-webpack');
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-contrib-connect');
    grunt.registerTask('dev', ['webpack:dev', 'uglify:dev', 'copy:dev', 'connect:dev']);
}
  • 앞서 설치한 Webpack 모듈을 통해 /app.js를 시작점으로 모듈 단위로 복수개의 파일로 작성된 JavaScript간의 종속 관계를 분석하여 1개로 합쳐진 /js/app.js를 생성한다.

index.html

프로젝트 루트에 index.html을 아래와 같이 작성한다.

<!DOCTYPE html>
<html lang="ko" ng-app="helloworld-app">

<head>
    <meta charset="utf-8">
    <title>Hello, World</title>
    <script src="app.js"></script>
</head>

<body>
    <div ui-view></div>
</body>

</html>
  • html 엘러먼트의 애트리뷰트로 명시된 ng-appAngularJS의 디렉티브라고 부른다. 보통 html 또는 body 엘러먼트에 명시하는데 어떤 엘러먼트에 명시해도 상관 없다. ng-app이 명시된 엘러먼트 범위가 곧 AngularJS의 영향을 받는 제어 범위가 된다.
  • JavaScriptjs/app.js 1개 만을 로드한다. 현재 시점에는 존재하지 않는 파일이며 뒤에 설명할 Webpack 빌드 도구를 이용하여 생성할 것이다. angular, angular-ui-router, 개발자가 작성한 복수개의 JavaScript 파일이 포함된다.
  • ui-view 디렉티브가 명시된 엘러먼트는 요청 주소에 따라 일치하는 ViewAJAX로 동적 로드할 부분이다. 본 예제에서는 /#hello 요청이 들어올 경우 hello.html을 출력할 것이다.

hello.view.html

프로젝트 루트에 hello.view.html을 아래와 같이 작성한다.

<div>
  {{message}}
</div>
  • HTML의 모든 구성 엘러먼트를 갖지 않은 것은 위 내용이 앞서 설명한 ui-view 디렉티브가 명시된 영역에 AJAX 요청에 의해 동적으로 출력될 것이기 때문이다.
  • {{message}}AngularJS가 제공하는 템플릿으로 다음에 뒤에 설명할 ViewModel에 의해 값이 출력될 것이다. 이처럼 브라우저에 출력될 HTML 템플릿을 View라고 부른다.

hello.viewmodel.js

프로젝트 루트에 hello.viewmodel.js을 아래와 같이 작성한다.

var viewmodel = function ($scope) {
    $scope.message = 'Hello, World!';
}
module.exports = viewmodel;
  • $scope에 위와 같이 변수를 작성하면 곧 Model이 된다. message라는 문자열 값을 가지는 Model 변수를 작성한 것인데 앞서 View에서 작성한 {{message}}에 주입되어 화면에 출력될 것이다. 개발자는 직접 DOM을 조작할 필요 없이 Model 변수의 값만 조작하면 AngularJS에 의해 자동으로 DOM에도 반영된다.
  • 단순한 예제라서 설명하지 않았지만 $scope에 함수를 작성할 수도 있다.
  • module.exports는 이 파일이 CommonJS 모듈 임을 Webpack에게 알려주는 역할을 한다. 이렇게 모듈을 구분하여 작성하면 이 모듈을 필요로 하는 다른 소스 코드에서 사용함으로서 모듈의 재사용성을 효율적으로 높일 수 있다.

hello.route.js

프로젝트 루트에 hello.route.js을 아래와 같이 작성한다.

var route = function ($stateProvider) {

    var helloState = {
        name: 'hello',
        url: '/hello',
        templateUrl: 'hello.view.html',
        controller: 'helloViewModel'
    }
    $stateProvider.state(helloState);
}
module.exports = route;
  • 앞서 작성한 View, ViewModel을 연결시켜주는 라우팅 역할을 한다. Spring Web MVC에 익숙한 개발자라면 @Controller를 생각하면 이해하기 쉽다.
  • 위는 /#hello 주소 요청을 hello.view.html 뷰와 helloViewModel 뷰모델로 연결시켜주는 역할을 한다.

app.js

프로젝트 루트에 app.js을 아래와 같이 작성한다.

window.$ = window.jQuery = require('jquery');
var angular = require('angular');
var angularUiRouter = require('angular-ui-router');
var helloRoute = require('./hello.route');
var helloViewModel = require('./hello.viewmodel');
var app = angular.module('helloworld-app', [angularUiRouter]);

app.config(['$stateProvider', helloRoute]);
app.controller("helloViewModel", ['$scope', helloViewModel]);
  • AngularJS 앱의 시작점이 되는 소스 코드이다. AngularJS 라이브러리인 angular, angular-ui-router를 차례대로 로드하고 앞서 개발자가 작성한 모듈을 로드하여 등록한다.
    • 만약 jQuery를 사용한다면 위와 같이 jqueryangular에 앞서 로드되어야 한다. 또한 다른 모듈에서도 이용할 수 있도록 전역 범위를 갖는 window 객체에 저장해야 한다.
  • 본 파일을 시작으로 분산되어 있는 복수개의 모듈들은 종속 관계를 가진다. 뒤에 설명할 Webpack에 의해 1개의 파일로 합쳐질 것이다. 현재 시점에서 브라우저에서 앱을 실행하면 브라우저가 알아듣지 못해 오류가 발생한다.

빌드

이제 Grunt, Webpack을 이용하여 빌드할 차례이다. 아래와 같이 Grunt 작업을 실행한다.

$ grunt dev
  • 앞서 Gruntfile.js에 설정한대로 빌드 과정을 거쳐 분산된 여러 모듈이 1개의 app.js로 통합되고 테스트 가능한 웹 서버가 실행되는 것을 확인할 수 있을 것이다.

관련 글

저작자 표시 비영리 동일 조건 변경 허락
신고