'소프트웨어개발/JavaScript'에 해당되는 글 43건

  1. Grunt, AngularJS 프로파일 설정하기
  2. NPM을 이용하여 프론트엔드 라이브러리 적용하기
  3. AngularJS, 전역 범위($rootScope)에 이벤트 핸들러 등록하기
  4. AngularJS, Hello World 출력하기
  5. Grunt, Webpack을 적용한 빌드 단위 개발하기
  6. Grunt, JavaScript 프로젝트에 적용하기
  7. AngularJS, UI-Router(angular-ui-router) 적용하기
  8. JavaScript, 오브젝트의 종류
  9. Framework7, Hello World 출력하기
  10. Vue.js, Hello World 출력하기

Grunt, AngularJS 프로파일 설정하기

개요

Java 진영, 특히 Spring 기반의 개발자들은 배포 환경에 따라 설정을 달리 적용할 수 있는 Profile 개념에 익숙할 것이다. 프론트엔드 JavaScript 진영에서 Profile 단위의 개발은 필요하지만 적용 방법은 익숙하지 않을 것이다. 이번 글에서는 Grunt 기반의 AngularJS 프로젝트에서 Profile 개념을 적용하는 방법을 소개하고자 한다.

package.json

프로젝트 루트에 package.json에 아래 내용을 추가한다.

{
    ...
    "devDependencies": {
        "grunt": "1.0.1",
        ...
        "grunt-replace": "1.0.1", 
        ...
}
  • 위와 같이 기존 Grunt 기반 AngularJS 프로젝트에 grunt-replace 모듈을 추가하고 커맨드 창에서 npm update를 실행한다.
  • grunt-replace 모듈은 원본 파일에서 사용자가 정의한 특정 패턴의 문자열을 찾아내어 바꿔치기하는 기능을 제공하는 Grunt의 모듈이다. 이 모듈을 활용하여 AngularJSConstant에 정의된 문자열을 배포 환경에 따라 변경하는 작업을 작성할 것이다.

Gruntfile.js

프로젝트 루트에 Gruntfile.js에 아래 내용을 추가한다.

module.exports = function (grunt) {

    grunt.initConfig({
        ...
        replace: {
            dev: {
                options: {
                    patterns: [{
                        json: grunt.file.readJSON('./profile_dev.json')
                    }]
                },
                files: [{
                    expand: true,
                    flatten: true,
                    src: ['src/app.js'],
                    dest: 'build/'
        },
        ...
    });
    ...
    grunt.loadNpmTasks('grunt-replace');
    ...
    grunt.registerTask('dev', ['replace:dev']);
}
  • 개발 환경을 dev 키워드로 식별하고 개발 환경으로 빌드할 경우 /profile_dev.json을 읽어 들여 소스 코드에서 해당 내용을 변경할 것이다.
  • 위 설정은 /src/app.js의 키워드를 모두 변경하고 /build/app.js로 저장한다.

profile_dev.json

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

{
    "PROTOCOL": "http",
    "HOSTNAME": "127.0.0.1",
    "PORT": "8080"
}
  • 각 배포 환경에 해당하는 설정 정보를 JSON 파일로 정의힌다. 빌드 과정에서 환경에 따라 해당 파일의 내용이 주입될 것이다.

app.js

/src/app.js를 아래와 같이 작성한다.

var app = angular.module('app', []);
app.constant('SERVER_INFO', {
    PROTOCOL: '@@PROTOCOL',
    HOSTNAME: '@@HOSTNAME',
    PORT: @@PORT
});

app.controller('testController', ['SERVER_INFO', function(SERVER_INFO) {
    console.log(SERER_INFO);
}]);
  • 위 소스 코드는 개발 및 운영 환경에 따라 값이 달라질 상수 값들을 AngularJSConstant로 등록하여 ControllerService에 주입하는 예이다.
  • 상수 값들은 @@KEY의 형식으로 식별 패턴을 부여하여 앞서 적용한 grunt-replace 모듈에 의해 빌드 과정에서 원래의 값으로 변경될 수 있도록 한다.

참고 글

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

NPM을 이용하여 프론트엔드 라이브러리 적용하기

개요

대개 jQuery, Bootstrap과 같은 프론트엔드 영역의 라이브러리(또는 프레임워크)를 프로젝트에 적용시 해당 라이브러리를 직접 다운로드하거나 CDN을 활용하는 방식을 사용해왔다. 이러한 방법은 최근의 백엔드 영역의 빌드 자동화에 비하면 대단히 낙후된 방법으로 이를 극복하기 위해 서버 영역에서는 WebJars, 클라이언트 영역에서는 Bower 등이 등장하였다. 이번 글에서는 Node.js의 패키지 매니저인 NPM을 이용하여 프로젝트에 프론트엔드 라이브러리를 적용하는 방법을 소개하고자 한다.

package.json

NPM을 이용하여 프론트엔드 모듈을 설치하기 위해 프로젝트 루트에 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-copy": "~1.0.0",
        "jquery": "~3.1.0",
        "bootstrap": "~3.3.7",
    }
}
  • jquery, bootstrap 프론트엔드 모듈을 사용하기 위해 의존성 정보를 작성하였다. 작성 후 커맨드 창에서 npm install를 실행하면 프로젝트 루트의 node_modules 디렉토리에 앞서 정의한 모듈이 설치된다.

Gruntfile.js

앞서 설치한 프론트엔드 모듈을 개발환경 또는 배포환경에 적용하기 위해 프로젝트 루트에 Gruntfile.js을 아래와 같이 작성한다. (Gruntfile.js는 프로젝트 빌드 도구인 Grunt의 작업 설정 파일이다.)

module.exports = function (grunt) {

    grunt.initConfig({
        copy: {
            build: {
                files: [
                    {
                        expand: true,
                        cwd: 'node_modules',
                        src: ['bootstrap/dist/**'],
                        dest: 'build/assets'
                    }
                ]
            }
        },
        webpack: {
            build: {
                entry: './src/app.js',
                output: {
                    path: 'build',
                    filename: 'app.js'
                }
            }
        }
    });
    grunt.loadNpmTasks('grunt-contrib-copy');
    grunt.loadNpmTasks('grunt-webpack');
    grunt.registerTask('dev', ['copy:dev', 'webpack:dev']);
}
  • copy 작업은 grunt-contrib-copy를 이용하여 node_modules에 존재하는 Bootstrap의 배포 파일(CSS, 글꼴, 이미지 등)을 지정한 경로에 복사한다. src 옵션에 복수개의 복사 대상 경로를 지정할 수 있다.
  • webpack 작업은 grunt-webpack을 이용하여 모듈 개념을 도입하여 작성한 사용자의 JavaScript 파일을 컴파일한다.
  • 뒤에 설명할 소스 코드까지 작성을 완료하고 커맨드 창에서 grunt build 명령을 실행하면 위 작성한 작업들이 순서대로 실행된다.

index.html

애플리케이션의 시작점인 /src/index.html을 아래와 같이 작성한다.

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <title>Hello, World</title>
    <link rel="stylesheet" href="assets/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="assets/bootstrap/dist/css/bootstrap-theme.min.css">
    <script src="app.js"></script>
</head>
<body>
</body>
</html>
  • 앞서 작성한 grunt-contrib-copy 작업에 따라 개발환경에 복사된 BoostrapCSS 파일을 로드하도록 작성하였다.
  • CSS는 기존의 방식대로 로드하지만 JavaScriptNPM 모듈의 장점을 활용할 것이다.

app.js

Webpack으로 빌드할 /src/app.js를 아래와 같이 작성한다.

window.$ = window.jQuery = require('jquery');
require('bootstrap');
  • NPM을 이용한 프론트엔드 모듈 관리의 장점은 위와 같이 Webpack이 제공하는 require() 구문을 이용하여 설치된 모듈을 참조할 수 있어 재사용성이 증가한다는 것이다. 위 2개 문장 만으로 JavaScript에서 jQueryBootstrapAPI를 전역 범위에서 사용할 수 있다.
  • 사용자가 작성한 JavaScript 파일은 반드시 UTF-8 인코딩으로 저장해야 한다. Webpack은 빌드 대상이 되는 파일들을 모두 UTF-8 인코딩으로 간주하기 때문에 다른 인코딩을 사용할 경우 글자가 깨진채 빌드된다.
저작자 표시 비영리 동일 조건 변경 허락
신고

AngularJS, 전역 범위($rootScope)에 이벤트 핸들러 등록하기

var app = angular.module('someApp', ['ui.router']);
app.run(['$rootScope', '$state'], function ($rootScope, $state) {

  $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams, options) {
    if ( ... ) { // 인증이 요구되는 스테이트일 경우
      $state.transitionTo('user.login');
      event.preventDefault();
  });
}]);
  • AngularJSrun() 메써드에 전달한 함수는 앱의 최초 부트스트래핑 과정에서 실행된다. 마치 Javamain() 함수와 유사한 기능을 한다. run() 메써드의 실행 시점은 config()controller() 사이이다. 앱의 전체 스코프에 필요한 이벤트 핸들러 등을 작성할 때 유용하다.
  • AngularJS$rootScope로 불리우는 단 1개의 스코프와 자손에 해당하는 복수개의 $scope를 제공한다. $rootScope의 범위는 바로 ng-app 디렉티브가 명시된 범위와 일치한다.
  • $rootScope와 모든 자손 스코프에는 특정 이벤트에 대한 이벤트 핸들러 함수를 등록할 수 있다. 위의 경우 URL 라우팅의 스테이트 전환시 발생하는 $stateChangeStart에 대한 이벤트 핸들러를 등록한 예이다.

참고 글

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

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로 통합되고 테스트 가능한 웹 서버가 실행되는 것을 확인할 수 있을 것이다.

관련 글

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

Grunt, Webpack을 적용한 빌드 단위 개발하기

Webpack이란?

서버 사이드에서 Node.js로 개발해본 개발자라면 require 구문을 통한 모듈 단위의 개발에 익숙할 것이다. Webpack은 서버 사이드에서만 사용되던 모듈 단위 개발을 클라이언트 사이드에서도 가능하게 해주는 빌드 도구이다. 이번 글에서는 Grunt 빌드 환경에서 Webpack을 적용하는 방법을 설명하고자 한다.

모듈의 예

JavaScript에서 모듈을 작성하는 방식은 AMD, CommonJS 2가지로 구분된다. 다음은 CommonJS 방식으로 작성한 모듈의 예이다. 아래와 같이 hello 이름을 갖는 모듈을 작성하고 hello.js로 저장한다.

var hello = 'hello';
module.exports = hello;

앞서 생성한 hello 모듈을 사용하는 world.js를 작성한다.

var hello = require('./hello');
console.log(hello);

package.js 작성

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

{
  "name": "sample-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"
  }
}
  • GruntWebpack을 적용하기 위해서는 webpack, webpack-dev-server, grunt-webpack 모듈을 프로젝트에 설치해야 한다.

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

$ npm install

Gruntfile.js 작성

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

module.exports = function(grunt) {

  grunt.initConfig({
    webpack: {
      sample: {
        entry: "./src/js/app.js",
        output: {
          path: "build/js",
          filename: "app.js"
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-webpack');
}
  • /src/js/app.js를 프로젝트의 시작점으로 지정하여 사용된 모듈 간의 종속 관계를 분석하여 하나의 /build/js/app.js를 생성한다. 이를 통해 개발자는 Node.js에서와 동일한 방법으로 프론트엔드 영역에서도 모듈 개념을 적용할 수 있다.

Grunt 작업 실행

앞서 정의한 작업을 아래와 같이 실행한다. 후속 작업으로 uglify를 실행하면 운영 환경에 맞게 축약된 파일을 얻을 수 있다.

$ grunt webpack:sample

써드파티 라이브러리 모듈 적용

Webpack을 도입하면 NPM과의 조합을 통해 써드파티 라이브러리 또한 모듈 적용이 가능하다. 전통적으로 프론트엔드에서 써드파티 라이브러리를 로드하는 방법은 아래와 같다.

<script src="/assets/js/jquery.js"></script>

Webpack을 도입하면 HTML 상에 명시할 필요 없이 JavaScript 만으로 아래와 같은 모듈화가 가능하다. 우선 package.js에 사용할 써드파티 라이브러리를 명시해야 한다.

{
  ...
  "devDependencies": {
    ...
    "jquery":"~3.1.0"
  }
}

npm install을 실행하여 라이브러리 의존성 설치까지 완료하면 아래와 같이 Webpack이 제공하는 모듈 방식의 라이브러리 사용이 가능하다.

window.$ = window.jQuery = require('jquery');

모듈에 별명 지어주기

때때로 특정 모듈에 명시적으로 별명일 지어줄 필요가 있다. 이 경우 resolve.alias 설정을 해주면 된다. jquery 모듈을 예로 들면 아래와 같다.

webpack: {
    dev: {
        ...
        resolve: {
            alias: {
                'jq': require('path').join(__dirname, 'node_modules/jquery/dist/jquery.min.js')
            }
        }
    }
}

위 설정은 jquery 모듈에 jq라는 별명을 지어준다. JavaScript에서는 아래와 같이 모듈을 로드할 수 있다.

window.$ = window.jQuery = require('jq');

관련 글

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

Grunt, JavaScript 프로젝트에 적용하기

Grunt란?

GruntJavaScript 작업 자동화 도구이다. Java 진영의 Maven, Gradle과 같은 프로젝트 빌드 도구라고 생각하면 이해하기 쉽다. 최근 프론트엔드 영역의 개발 복잡성이 증가하면서 JavaScript 또한 빌드의 대상이 되고 있다. 이번 글에서는 Grunt를 이용하여 복수개의 JavaScript 소스 코드를 병합(Concat), 축약(Uglify)하는 방법을 소개하고자 한다.

Grunt 사용을 위한 준비사항

Grunt를 사용하려면 프로젝트 루트 디렉토리에 package.json, Gruntfile.js가 작성되어 있어야 한다. Java 진영의 Gradlebuild.gradle의 관계를 생각하면 이해하기 쉽다.

콘솔에서 grunt 명령어를 사용하려면 NPM을 이용한 설치가 필요하다.

$ npm install -g grunt-cli

이제 프로젝트 루트에서 package.json을 작성한다.

{
  "name": "sample-app",
  "version": "0.0.1",
  "devDependencies": {
    "grunt": "1.0.1",
    "grunt-contrib-concat": "1.0.1",
    "grunt-contrib-uglify": "2.0.0"
  }
}
  • 기본 종속성으로 grunt를 추가한다. 또한, 이번 글에서 소개할 grunt-contrib-concat, grunt-contrib-uglify를 추가한다.

NPM으로 앞서 정의한 package.json대로 해당 모듈들을 설치한다. 작업이 완료되면 프로젝트 루트 디렉토리에 node_modules 디렉토리가 생성된다.

npm install

Gruntfile.js 작성

마지막 관문이다. 프로젝트 루트 디렉토리에 Gruntfile.js을 아래와 같이 자동화할 작업을 작성한다.

module.exports = function(grunt) {

  grunt.initConfig({
    concat: {
      build: {
        files: {
          'dist/app.js', ['src/main/javascript/app.js', 'src/main/javascript/app2.js']
        }
      }
    },
    uglify: {
      build: {
        files: {
          'dist/app.min.js': ['dist/app.js']
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
}

Grunt 작업 실행

앞서 정의한 작업을 아래와 같이 실행한다. 첫번째는 소스 코드를 병합(Concat)하는 명령어이다.

$ grunt concat

두번째는 소스 코드를 축약(Uglify)하는 명령어이다.

$ grunt uglify

참고 글

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

AngularJS, UI-Router(angular-ui-router) 적용하기

URL 라우팅이란?

전통적인 웹 사이트는 사용자가 링크를 클릭할 경우 새로운 주소로 이동하며 페이지가 전환된다. SPA(Single Page Application)은 이름 그대로 처음 시작 페이지를 제외하고는 독립적으로 접근 가능한 물리적인 페이지가 존재하지 않는다. 링크를 클릭할 경우 필요한 부분만 AJAX로 로드되어 현재 페이지에 주입된다.

AngularJS에서의 URL 라우팅은?

AngularJSURL 라우팅 기능을 하는 angular-route.js라는 공식 모듈을 제공한다. 하지만 써드파티 모듈인 angular-ui-router.js가 보다 많은 기능을 제공하며 공식 모듈보다 표준처럼 널리 쓰이고 있다. 이번 글에서는 angular-ui-router.js의 사용법을 소개하고자 한다.

UI-Router 의존성 추가

<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.3.1/angular-ui-router.js"></script>
</head>
  • angular-ui-route.js는 반드시 angular.js의 다음에 로드되어야 한다.

링크 작성

<a ui-sref="about" href="#/about">ABOUT</a>
  • 본 링크 적용 후 브라우저에서 #/about 주소로 접속하면 about이라는 이름의 State로 연결되어 물리적인 페이지 이동 없이 해당 내용이 출력된다.

링크 이동시 페이지 주입 위치 작성

<div ui-view></div>
  • 앞서 URL 라우팅을 통해 물리적인 페이지 이동 없이 링크의 페이지 내용이 현재 DOM에 주입된다고 설명했다. 위와 같이 HTML의 특정 엘러먼트에 ui-view 애트리뷰트를 식별자로 부여하면 해당 부분에 링크된 페이지의 내용이 AJAX로 주입된다.
  • 당연한 말이지만 ui-view 영역은 ng-app이 명시된 엘러먼트 범위에 존재해야 한다.

스테이트 등록

var samepleApp = angular.module('sameple-app', ['ui.router']);

sampleApp.config(function($stateProvider) {

    var aboutState = {
        name: 'about',
        url: '/about',
        templateUrl: 'about.html'
    }
    $stateProvider.state(aboutState);
});
  • 앱 모듈은 URL 라우팅을 제공하는 ui.router 모듈에 종속성을 가지므로 앱 초기화 시점에 추가해준다.
  • 주소와 페이지를 연결하여 주는 URL 라우팅 정보의 각 단위를 State라고 부른다. 위와 같이 개별 State$stateProvider로 추가하여 준다.
  • 위의 경우 #/about 주소 요청이 들어올 경우 about.html의 내용을 앞서 설명한 ui-view 영역에 출력한다.

멀티 뷰 스테이트 등록

앞서 설명한 방법은 화면 레이아웃이 단 1개의 뷰를 가진 스테이트 상황을 가정했다. 하지만 상황에 따라 여러 개의 복잡한 뷰를 가진 스테이트를 작성해야할 때도 있다. 멀티 뷰 스테이트 작성의 예는 아래와 같다.

<!-- index.html -->
<body>
    <div ui-view></div>
</body>

<!-- article.html -->
<div>
    <div ui-view="header"></div>
    <div ui-view="body"></div>
    <div ui-view="footer"></div>
</div>

<!-- article-header.html -->
<div>Header</div>

<!-- article-body.html -->
<div>Body</div>

<!-- article-footer.html -->
<div>Footer</div>
// app.js
var samepleApp = angular.module('sameple-app', ['ui.router']);
sampleApp.config(['$stateProvider', function($stateProvider) {

    $stateProvider.state('article', {
        url: '/article',
        views: {
            '': {
                templateUrl: 'article.html'
            },
            'header@article': {
                templateUrl: 'article-header.html'
            },
            'body@article': {
                templateUrl: 'article-body.html'
            },
            'footer@article': {
                templateUrl: 'article-footer.html'
            }
        }
    });
}]);
  • /#/article 주소 요청에 대응하는 3개의 뷰를 가진 article이라는 이름의 State를 생성한다.
  • 1개의 스테이트는 1개 이상의 View를 가질 수 있다. 기본 뷰가 되는 부모 뷰는 ''로 선언한다. 나머지 3개의 자식 뷰는 {ui-view}@{state}의 형식으로 선언할 수 있다. 각 뷰마다 각각의 templateUrl(또는 template) 및 controller를 지정할 수 있다.

참고 글

관련 글

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

JavaScript, 오브젝트의 종류

JavaScript 오브젝트의 종류

JavaScript 오브젝트는 크게 3가지로 분류할 수 있다.

  • 첫째는 ECMAScript 스펙에 명시된 오브젝트이다. Native Object라고 부른다. Object, Number, String, Boolean, Array, Date 등이 해당된다.

  • 둘째는 JavaScript 소스 코드가 실행되는 환경에서 제공되는 오브젝트이다. Host Object라고 부른다. 웹 브라우저 환경에서는 window, XMLHttpRequest, HTMLElement 등이 해당된다. 해당 오브젝트들은 웹 브라우저 환경이 아닐 경우(예를 들면 Node.js) 사용할 수 없다.

  • 셋째는 사용자가 정의한 오브젝트이다. User Object라고 부른다.

Global 오브젝트란?

앞서 window 오브젝트가 웹 브라우저 환경에서만 제공되는 Host Object라고 설명했다. window 오브젝트는 Host Object이면서 Global Object라고도 부른다. 사실 웹 브라우저에서 생성하는 모든 User ObjectGlobal 오브젝트인 window 오브젝트에 저장된다. 호스트 환경마다 다른 Global 오브젝트의 레퍼런스를 획득하는 방법은 아래와 같다.

var global = (
    function() {
        return this;
    }
    ()
);
저작자 표시 비영리 동일 조건 변경 허락
신고

Framework7, Hello World 출력하기

개요

웹 디자이너와 퍼블리셔의 영역이었된 HTML, CSSBootstrap를 시작으로 다양한 CSS 프레임워크가 등장하면서 개발자 또한 구조화된 웹 디자인이 가능하게 되었다. 시간이 흘러 전통적인 웹 페이지보다 보다 네이티브 클라이언트에 가까운 SPA(Single Page Application) 개념이 웹에 등장하면서 이런 수요를 총족하기 위한 다양한 프레임워크가 등장하고 있다. 이 중에 앱 스타일의 개발에 특화된 Framework7이라는 프레임워크의 사용법을 간단히 설명하고자 한다.

HTML

<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="theme-color" content="#2196f3">
    <!-- Framework7 1.4.2 안드로이드 테마 CSS를 로드한다. -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/framework7/1.4.2/css/framework7.material.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/framework7/1.4.2/css/framework7.material.colors.min.css">
</head>

<body>
    <!-- Framework7의 Views를 작성한다. 반드시 메인 뷰를 포함해야 한다. -->

    <!-- Framework7 1.4.2 JavaScript를 로드한다. -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/framework7/1.4.2/js/framework7.min.js"></script>

    <!-- 애플리케이션 JavaScript를 로드한다. -->
    <script src="hello.js"></script>
</body>

</html>

JavaScript

// Framework7 오브젝트를 초기화한다. 앱 단위의 오브젝트로 단 한번만 초기화한다. 이 시점에 초기 요소들이 화면에 렌더링된다.
var helloApp = new Framework7({});

helloApp.addView('.view-main', {
    dynamicNavbar: true
});

Views

  • Views는 실제 화면에 출력될 Framework7의 단위이다. HTML로는 아래와 같으며 파일 1개 당 단 1개의 Views 만을 허용한다. Views의 기본 HTML 구조는 아래와 같다.
<div class="views">
    <!-- view-main는 해당 뷰가 메인 뷰임을 의미한다. 페이지 링크 클릭시 페이가 로드될 기본 뷰가 된다. -->
    <div class="view view-main">
        <!-- 하나의 view는 navbar, pages, toolbar를 가질 수 있다. -->
        <div class="navber">...</div>
        <div class="pages">
            <div class="page" data-page="hello">...</div>
            ...
        </div>
        <div class="toolbar">...</div>
    </div>
</div>

Page

  • Page는 페이지를 나타내는 Framework7의 단위이다. HTML로는 아래와 같다.
<!-- data-page에는 각 페이지를 식별할 수 있는 페이지의 이름을 명시해야 한다. -->
<div class="page" data-page="world">
    <div class="page-content">
        ...
    </div>
</div>
  • 일반적인 웹 애플리케이션과 가장 차별되는 부분은 a 엘러먼트의 페이지 링크 클릭시 페이지를 새로 로드하는 것이 아니라 해당 HTML 파일을 AJAX로 불러와서 현재 DOM을 갱신한다는 것이다.
  • 한가지 주의할 점은 각 페이지가 JavaScript를 가지고 있어도 실행되지 않는다는 것이다. 모든 페이지 단위 JavaScript는 최초 생성된 Framework7 오브젝트의 통제 하에서만 실행이 가능하다. 아래와 같다.
var world_onPageInit = function(page) {
    // 페이지가 나타난 후 실행될 JavaScript를 작성
}

var world_onPageBeforeRemove = function(page) {
    // 페이지를 사라진 후 실행될 JavaScript를 작성
}

helloApp.onPageInit("world", world_onPageInit);
helloApp.onPageBeforeRemove("world", world_onPageBeforeRemove);

관련 글

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

Vue.js, Hello World 출력하기

개요

나는 2011년 모 사내 시스템을 MVVM 패턴의 Knockout으로 개발하면서 상당한 생산성 향상을 경험했다.(심지어 해당 시스템은 닷넷 기반의 클라이언트 애플리케이션이었는데 나는 기어코 WebView를 적용하여 도입하고 말았다.) 데이터 바인딩과 컴포넌트의 개념이 일반적인 닷넷 기반의 클라이언트 개발에 익숙한 개발자들은 전통적인 웹 프론트엔드 개발 방식이 비효율적이라는 생각을 많이 했을 것이다. Knockout은 그러한 불만을 적당히 해결해주는 라이브러리였다. 시간이 흘러 웹 프론트엔드 개발 환경이 더욱 복잡해지면서 Angular, React 등의 본격적인 엔터프라이즈 레벨의 프레임워크가 등장하기 시작했다. 서로가 각자의 장점을 내세워 점유율을 높여가고 있지만 반대로 도입에 필요한 학습곡선이 지나치게 높다는 점, 프레임워크 종속이 너무 크다는 점 때문에 거부감을 느끼는 개발자들도 많다. Vue.jsKnockout와 유사한 MVVM 패턴의 경량 라이브러리(프레임워크가 아니다!)로 굉장히 쉬운 사용 방법으로 앞서 언급한 거인들 사이에서 점유율을 서서히 높여가고 있다. 이번 글에서는 Vue.js로 간단히 Hello World를 출력하는 방법을 소개하고자 한다.

HTML

<!DOCTYPE html>
<html>

<head>
    <!-- Vue.js 2.0 JavaScript를 로드한다. -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.0-rc.3/vue.min.js"></script>
</head>

<body>
    <!-- Vue.js의 View를 선언한다. -->
    <div id="helloView">
        <p>{{message}}</p>
    </div>

    <!-- 애플리케이션 JavaScript를 로드한다. -->
    <script src="hello.js"></script>
</body>

</html>
  • Vue.js 라이브러리 로드시 head 엘러먼트 내부에 위치해야 한다. body 엘러먼트 내부의 마지막에 위치할 경우 렌더링 되기 전의 View의 모습이 브라우저에 보여질 수 있다.


  • HTML에는 1개 이상 다수의 View를 선언할 수 있다. 선언된 View 내부에서는 Vue.js가 제공하는 애트리뷰트(v-로 시작한다.)와 템플릿({{}}로 표현되는 Mustache 스타일의 템플릿이 기본 제공된다.)을 사용할 수 있다.


  • 각각의 View를 식별하기 위해 id 애트리뷰트를 명시한다. 하지만 class 애트리뷰트 명시도 가능하며 심지어 식별자를 부여하지 않아도 상관 없다. JavaScript 코드에서 ViewModel 선언시 가장 처음에 발견한 View만을 인식하기 때문이다.


  • 애플리케이션 JavaScript 소스 파일은 반드시 body 엘러먼트 내부의 마지막에 로드해야 한다. 그렇지 않을 경우 View가 렌더링되지 않은 상태에서 로드되어 정상적인 실행을 보장할 수 없다.

JavaScript

// 앞서 HTML에서 선언한 View의 id를 명시한다.
var helloView = '#helloView';

// View에 출력될 Model을 명시한다.
var helloModel = {
    message: 'Hello, Vue.js!'
};

// 앞서 작성한 View, Model를 사용하여 ViewModel 오브젝트를 생성한다. 이 시점에 해당 View에 ViewModel이 적용되어 브라우저에 렌더링된다.
var helloViewModel = new Vue({
    el: helloView,
    data: helloModel
});

// ViewModel 오브젝트의 Model로 참조된 오브젝트의 값을 변경하면 실시간으로 브라우저에 새로운 값이 렌더링된다.
helloModel.message = 'Good-bye, Vue.js!';
  • Vue.jsECMAScript 5 기반으로 작성되었다. 이 말은 마이크로소프트 계열 브라우저의 경우 IE 9 이상 만을 지원한다는 의미이다. 여전히 IE 7, 8의 사용 비율이 높게 나타나는 국내에서는 도입 전 고려해야 하는 부분이다. (Angular 2, React 15 또한 IE 9 이상 만을 지원한다.)

참고 글

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