AngularJS 디렉티브(directive)

디렉티브(directive)는 템플릿에 삽입하여 사용하는 HTML적인 “태그"와 “속성"을 만드는 방법이다. 이를 제공함으로써 자신의 출력을 태그로 쉽게 표시할 수 있다. 디렉티브을 만들어, AngularJS를 파워업을 할 수 있다.

디렉티브(directive) 기본 및 생성

AngularJS에서는 HTML 태그에 고유한 속성을 추가하고, 혹은 태그를 작성하여 HTML안에 특수한 기능을 할당했다. 이러한 기능을 “지시어” 즉, 디렉티브(directive)라고 한다.

이 디렉티브은 AngularJS에 제공되어 있을 뿐만 아니라 직접 만들 수도 있다. 직접 디렉티브을 작성하여 자신만의 태그를 추가해 나갈 수 있는 것이다.

디렉티브은 모듈에 작성한다. 모듈의 객체에 제공하고 있는 “directive"라는 메소드를 이용하여 만든다. 이는 다음과 같은 형태로 작성한다.

모듈.directive(이름, 함수);

첫번째 인수는 디렉티브의 이름을 지정한다. 이 이름이 그대로 태그 이름과 속성 이름으로 사용할 수 있게된다. 두번째 인수에 디렉티브의 구체적인 처리를 하는 함수 객체를 지정한다.

directive 메소드 자체의 사용법은 간단한다. 문제는 인수에 제공하는 함수를 어떻게 만들면 되는가? 이다. 이는 “지시문에서 필요한 값을 가진 개체를 반환"처리를 생각합니다.

우선은 가장 간단한 형태로 “template"라는 값을 가진 객체를 반환하는 디렉티브 함수를 보도록 하자. 이는 다음과 같다.

function() {
    return {template: ... 출력 내용 ...};
}

“template"는 출력 태그의 템플릿이다. 즉, 여기에 준비한 내용이 그대로 Web 페이지에 표시된다고 할수 있다.

간단한 디렉티브 생성

간단한 예제를 만들어 보자. 여기에서는 “OK!“라고 출력하는 OK 디렉티브를 만들어 보겠다.

우선 다음과 같이 스크립트를 작성하고 “script.js"라고 저장한다.

var myapp = angular.module('myapp',[]);

myapp.directive('ok', function(){
    return {
        template:'<span style="font-size:24pt;color:red;">OK!</span>'
    };
});

여기에서는 directive의 첫번째 인자에 ‘ok’라고 지정하고 있다. 두번재 인수는 return {template : ...}와 같은 문장만을 수행하는 함수를 지정하고 있다. 디렉티브의 기본적인 구조를 안다면 그렇게 어려운 것을 하고 있는 것은 아니다라는 것을 알 것이다.

그럼, 이를 실제로 사용을 해보자. 아래에 HTML 파일의 예제는 아래와 같다.

<!DOCTYPE html>
<html>
<head>
    <title>AngularJS Sample</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script src="script.js"></script>
    <style>
    body { color:gray; }
    h1 { font-size:18pt; font-weight:bold; }
    </style>
</head>
<body ng-app="myapp" ng-init="num=0">
    <h1>디렉티브</h1>
    <ok></ok>
</body>
</html>

이 Web 페이지를 열면, 빨간색으로 “OK!“가 표시된다.

리스트을 보면, <ok></ok>라는 태그가 작성되어 있고, 이것이 OK 디렉티브문을 이용하고 있는 부분이다. HTML은 <ok/>이라는 태그는 물론 없다. 이 <ok/> 태그가 아래와 같은 태그로 변환되는 것이다.

<span style = "font-size : 24pt; color : red;">OK!</span>

이러한 디렉티브를 사용하면, 간단한 커스텀 태그를 사용하여 복잡한 표시도 할 수 있다.

디렉티브(directive) 요소(element) 조작

아주 간단한 태그를 작성하는 디렉티브라면, 이것으로 충분히 사용할 수 있다. 그런데, 보통의 HTML 태그라는 것은, 그렇게 간단한 사용하는 것은 <br> 정도일 것이다. 보통은 시작 태그와 종료 태그 사이에 값을 쓰거나, 태그에 속성을 지정하여 좀 더 표현력을 갖게 된다.

디렉티브에서도 이런 일이 가능하다. 여기에는 directive 메소드의 인수로 제공하는 함수를 약간 수정해야 한다.

앞전 예제에서는 directive 메소드의 두번째 인수에서 지정한 함수에는 객체를 return하도록 하였다. 이런 방식이라면, 객체에 값을 준비하는 정도 밖에 할 수 없다. 이보다 복잡한 처리는 할 수 없다는 것이다.

그래서 return하는 값을 “함수 객체"로 하는 것이다. 그리고 이 함수에서 필요한 작업을 수행하도록 하면 된다. 이 경우에 함수는 다음과 같은 형태로 정의한다.

function() {
    return function(scope, element, attrs) {
        ...... 필요한 처리 ......
    });
}

return이 함수에는 3개의 인수가 준비되어 있다. 이들은 각각 다음과 같은 역할을 한다.

  • scope : 이것은 범위의 객체이다.
  • element : 이 지시문의 DOM 요소이다.
  • attrs : 속성 정보를 취급하는 객체이다.

디렉티브 태그에 있는 텍스트와 태그에 포함된 속성을 이용하려면 element와 attrs를 사용한다. 이러한 객체에서 필요한 정보를 얻어 조작하면 된다.

지시문 작성

그럼 앞에서 작성한 script.js의 디렉티브를 수정하고, 속성 및 태그에 포함된 텍스트를 이용하도록 변경해 보자.

예제는 아래와 같다.

var myapp = angular.module('myapp',[]);
 
myapp.directive('ok', function(){
    return function(scope,element,attrs){
        var size = attrs['size'];
        size = size == null ? '24pt' : size;
        var color = attrs['color'];
        color = color == null ? 'red' : color;
        var txt = element[0].textContent;
        var tag = '<span style="font-size:' + size  + ';color:' + color + ';">' + txt + '</span>';
        element[0].innerHTML = tag;
    };
});

여기에서는 “size”, “color"라는 두 가지 속성과 태그에 적혀 있는 텍스트에 대응하는 처리를 하고 있다. 대략 정리하면 아래와 같다.

size와 color 속성

속성은 attrs 인수에 정리하고 있다. 여기에서는 이 attrs에서 size와 color 속성을 각각 변수에 얻을 수 있다.

var size = attrs['size'];
size = size == null ? '24pt' : size;
var color = attrs['color'];
color = color == null ? 'red' : color;

이 처럼 attrs[이름]라는 형태로, 지정된 이름의 속성 값을 얻을 수 있다. 그런데, 여기서 주의할 점은 “속성이 태그에 포함되지 않은 경우도 있다"는 것이다. 이 경우는 얻은 값은 undefined가 되기 때문에, 얻은 변수에 다시 값을 대입하도록 하여 대응해 두어야 한다.

태그의 값

예를 들어, <ok>Hello</ok>와 같이 태그에 값(여기에서는 Hello)를 작성하였을 때에, 이 값을 얻어 사용할 수 있다. 값은 다음과 같이 얻는다.

var txt = element[0].textContent;

인수로 전달되는 element가 디렉티브의 DOM 요소이다. 다만, 이것은 배열되어 있으며, 인덱스 번호 0 값이 첫번째 디렉티브의 DOM 요소가 된다(보통은 0번의 값 1개 밖에 없을 것이다).

element[0]에서 내부에 있는 값을 얻으려면, 여러가지 방법이 있을 것이다. 여기에서는 textContent로 이용하여 얻을 있다. 디렉티브 태그 또는 내부에 다른 태그가 내장되어 있거나 하면 이것으로는 처리할 수는 없지만, 단순히 내부에 텍스트가 작성되어 있다면 이것으로 충분하다.

element[0].innerHTML = tag;

그리고, 출력하는 태그를 텍스트로 작성하여 element[0].innerHTML에 그것을 대입한다. 이것으로 <ok> 태그의 내부가 교체된다.

디렉티브를 HTML에 통합

그럼 작성한 새로운 OK 디렉티브를 사용한다. 이용 예제눈

<!DOCTYPE html>
<html>
<head>
    <title>AngularJS Sample</title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script src="script.js"></script>
    <style>
    body { color:gray; }
    h1 { font-size:18pt; font-weight:bold; }
    </style>
</head>
<body ng-app="myapp" ng-init="num=0">
    <h1>디렉티브</h1>
    <ok>This is sample!</ok>
    <hr>
    <ok size="36pt">This is size sample!</ok>
    <hr>
    <ok color="blue">This is color sample!</ok>
    <hr>
    <ok color="green" size="48pt">This is full sample!</ok>
</body>
</html>

여기에는 4개의 <ok> 태그를 작성하고 있다. 각각의 사용법은 아래와 같다.

특성 없음

<ok>This is sample!</ok>

size 속성

<ok size="36pt">This is size sample!</ok>

color 속성

<ok color="blue">This is color sample!</ok>

size, color 속성

<ok color="green" size="48pt">This is full sample!</ok>

이처럼 size와 color 2가지 속성을 사용할 수 있다. 이러한 속성은 지정하지 않으면 각각 size="24pt", color="red"로 디폴트로 설정된다.

또한 모든 태그 안에 작성되어 있는 텍스트는 그대로 표시된다. 이것으로 텍스트의 색상과 크기를 간단히 설정할 수 있는 태그로써 <ok>를 만들 수 있게 되었다.

디렉티브(directive) 이벤트 설정

디렉티브는 표시를 커스터마이징하는 것만 할 수 있는 것은 아니다. 태그에 이벤트를 결합하여 움직이는 디렉티브 를 만들 수 있다.

이것은 따로 특별한 테크닉이 필요한 것은 아니다. directive에서 전달되는 element 인수는 DOM 요소이기 때문에, 그대로 이벤트의 설정할 수 있다. onclick 속성 등에 함수를 대입하거나 또는 addEventListener 메소드를 호출하여 설정하면 된다.

간단한 예제는 아래와 같다.

var myapp = angular.module('myapp',[]);
 
myapp.directive('click', function(){
    return function(scope, element, attrs){
        element[0].addEventListener("click", function(){
            var txt = this.textContent;
            alert('you click "' + txt + '"!');
        }, false);
    };
});

이것은 클릭하면 alert를 표시하는 <click> 태그 디렉티브이다. 예를 들어, 아래와 같이 태그를 작성한다.

<click>This is click sample!</click>

이 텍스트 부분을 클릭하면 화면에 알림 창이 나타나 you click "This is click Sample!"!라는 메시지가 표시된다.

여기에서는 return하는 함수 객체 안에 다음과 같이 이벤트를 지정한다.

element[0].addEventListener("click", function(){
    ...... 클릭 처리 ......
}, false);

보통 이벤트 지정과 동일하다. 이것으로 디렉티브는 DOM 요소에도 보통으로 이벤트를 추가할 수 있다.

디렉티브(directive) 속성 디렉티브 생성

태그 자체를 통째로 디렉티브로 만들뿐 아니라 속성으로써 디렉티브을 만들 수 있다. 속성으로 지정하는 지시어는 다양한 HTML 태그 다음에 추가하여 사용할 수 있다는 장점이 있다.

이는 기본적인 작성법은 이전에 설명하였다. 객체를 return하는 방식과 동일하다. 다만, 지정하는 값이 조금 다를 뿐이다. 정리하면 이렇게 된다.

모듈.directive( 이름 , 
    function() {
        return {
        restrict: 'A',
        template: 출력 내용
        };
    });

directive에서 return하는 함수 객체에는 template 외에 “restrict"라는 값을 지정해 주고 있다. 이것은 그 디렉티브가 어디에 사용할 수 있는지를 나타내는 것으로, 다음과 같은 값을 사용할 수 있다.

  • ‘E’ : 요소로 사용할 수 있다. <ok>와 같은 형태이다.
  • ‘A’ : 속성으로 사용할 수 있다. <p ok>와 같은 형태이다.
  • ‘AE’ : 위 모두에서 사용할 수 있다.

restrict:'A'를 지정하면, 속성으로 사용할 디렉티브를 만들 수 있다는 것이다.

그럼 간단한 예제로 아래에 스크립트를 올려 두었다.

myapp.directive('ok', function(){
    return {
        restrict: 'A',
        template:function(scope, element){
            var size = 24;
            switch(element['ok']){
            case 'large':
                size = 48; break;
            case 'midium':
                size = 36; break;
            case 'small':
                size = 24; break;
            }
            var obj = scope[0];
            var tag ='<span style="font-size:' + size
                + 'pt">' + scope[0].textContent
                + '</span>';
            return tag;
        }
    };
});

이것은 ok 특성에 3개의 값을 작성되어 있다. 예를 들어, 이런 식으로 사용한다.

<div ok="large">This is Large sample!</div>
<div ok="midium">This is Midium sample!</div>
<div ok="small">This is Small sample!</div>

여기에서는 template 속성 값에 함수를 지정하고 있다. 복잡한 처리를 할 경우는 그렇게 하는 것이 편리하기 때문이다. 그러나 함수를 지정하는 경우에는 인수에 주의해야 한다.

template:function(scope, element){...});

이와 같이, scope와 element 2가지가 전달된다. 속성의 값은 element에서 직접 얻어 낼 수 있다. 여기에서는 element['ok']에서 ok 속성의 값을 얻고 있다.

또한, 이 ok 속성이 있는 태그 안에 작성되는 값은 scope[0].textContent로 얻어 오고 있다. element 가 아니고, scope에서 얻어 올 수 있기 때문에 주의가 필요하다.

속성 디렉티브는 어디까지나 주가 되는 태그에 추가하는 것이므로, 그 태그 자체를 과감하게 변경해 버리는 것이 아니라, 거기에 뭔가를 추가하는 정도의 것으로 설계하는 것이 좋다. 통째로 변경해 버리는 것은 요소로서 작성하는 것이 좋다.




최종 수정 : 2017-12-27