Handlebars  코딩 해보기

 

1)    기본적인 구조를 통해 이해

 

Handlebar 템플릿은 아래처럼 script태그 안에 type “text/x-handlebars-template” 등으로 설정하고 위치시킨다. 그리고 템플릿 구조에 {{ }} 감싸진 부분이 데이터가 binding되는 부분이다.

{{#users}} {{/users}} 안에 들어있는 html 태그들은 items라는 배열의 길이만큼 반복하게 된다. 그리고 내부의 {{name}},{{id}},{{email}} 배열 요소 객체의 name,id,email 속성값이 바인딩 된다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<script id="entry-template" type="text/x-handlebars-template">

<table>

    <thead> 

        <th>이름</th> 

        <th>아이디</th> 

        <th>메일주소</th> 

    </thead> 

    <tbody> 

        {{#users}} 

        <tr> 

            <td>{{name}}</td> 

            <td>{{id}}</td> 

            <td><a href="mailto:{{email}}">{{email}}</a></td> 

        </tr> 

        {{/users}} 

    </tbody> 

</table>

</script>

Colored by Color Scripter

cs

 

실제 템플릿과의 바인딩은 자바스크립트를 통해서 이루어진다. 아래는 자바스크립트를 통해 Binding하는 코드이다.

아래 코드를 보면 먼저 앞에서 작성한 Handlebar Template의 내용을 가져온다. Handlebars.compile 메소드를 통해 Template을 컴파일한 뒤, 리턴 된 함수의 Parameter에 바인딩할 데이터를 Parameter로 넣으면 데이터가 BindingHTML Template이 리턴된다. HTML TemplateDOM에 추가하면 끝난다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//핸들바 템플릿 가져오기

var source = $("#entry-template").html(); 

 

//핸들바 템플릿 컴파일

var template = Handlebars.compile(source); 

 

//핸들바 템플릿에 바인딩할 데이터

var data = {

        users: [

            { name"홍길동1", id: "aaa1", email: "aaa1@gmail.com" },

            { name"홍길동2", id: "aaa2", email: "aaa2@gmail.com" },

            { name"홍길동3", id: "aaa3", email: "aaa3@gmail.com" },

            { name"홍길동4", id: "aaa4", email: "aaa4@gmail.com" },

            { name"홍길동5", id: "aaa5", email: "aaa5@gmail.com" }

        ]

}; 

 

//핸들바 템플릿에 데이터를 바인딩해서 HTML 생성

var html = template(data);

 

//생성된 HTML DOM 주입

$('body').append(html);

Colored by Color Scripter

cs

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

//Handlebar 템플릿을 가져오기

var source = $("#entry-template").html();

//Handlebar 템플릿을 precompile

var template = Handlebars.compile(source);

//Handlebar 템플릿에 바인딩할 데이터

var data = {

items:[

{name"shoes", price:"5000", intro:"www.a.com"},

{name"T-shirt", price:"1000", intro:"www.b.com"},

{name"Jeans", price:"2000", intro:"www.c.com"}

]

};

//Handlebar 템플릿에 데이터를 바인딩해서 HTML 생성

var itemList = template(data);

//생성된 HTML DOM 주입

$('body').append(itemList);

cs

 

아래와 같은 결과가 출력된다.

 

1

2

3

4

5

6

이름    아이디    메일주소

홍길동1    aaa1    aaa1@gmail.com

홍길동2    aaa2    aaa2@gmail.com

홍길동3    aaa3    aaa3@gmail.com

홍길동4    aaa4    aaa4@gmail.com

홍길동5    aaa5    aaa5@gmail.com

cs

 

 

참고로 링크 부분의 malito는 저 링크를 클릭했을 때 바로 전자메일을 보낼 수 있게 해주는 태그이다.

 

코드를 통해 알 수 있듯이 실제 템플릿이 데이터를 컴파일하고, 바인딩해주는 부분이 모두 자바스크립트 코드에서 이루어진다. Handlebars.compile 메서드를 이용해 템플릿을 컴파일하고, 리턴 된 함수의 ParameterBinding된 데이터를 Parameter로 넣으면 Binding HTML템플릿이 리턴 된다. 그리고 이 HTML템플릿을 DOM에 추가함으로써 구현이 완료된다.

 

 

2)    사용자 정의 Helper 사용해보기

 

Handlebar Mustache를 기초로 개발되었지만, Mustache와의 차별 점은 바로 사용자 정의 Helper이다. Mustache는 단순하게 주입된 데이터를 바인딩해주는 템플릿 엔진이었다면 Handlebar if, unless 등의 기본 Helper뿐만 아니라 사용자 정의 Helper를 추가해서 좀 더 템플릿 엔진에서 간단한 로직을 포함시킬 수 있다는 것이다. 아래는 기본적인 사용자 정의 Helper예시이다.

 

1

2

{{!-- 사용자 정의 Helper email id 인자로 넘긴다 --}}

<td><a href="mailto:{{email id}}">{{email id}}</a></td>

cs

 

앞의 예시에서 HTML코드에서  email라고만 되었던 부분에 {{email id}} 형태로 바뀌고, idParameter를 전달해주고 있다. , email이라는 이름의 사용자 정의 Helper가 사용되고 있는걸 알 수 있다. 이를 해석하면 email이라는 이름의 사용자 정의 Helper를 호출하면서 Parameterid 값을 전달한다는 의미이다.

전체 코드를 보면 아래와 같다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

<script id="entry-template" type="text/x-handlebars-template">

<table>

    <thead> 

        <th>이름</th> 

        <th>아이디</th> 

        <th>메일주소</th> 

    </thead> 

    <tbody> 

        {{#users}} 

        <tr> 

            <td>{{name}}</td> 

            <td>{{id}}</td> 

            

            {{!-- 사용자 정의 헬퍼인 email id 인자로 넘긴다 --}}

            <td><a href="mailto:{{email id}}">{{email id}}</a></td> 

        </tr> 

        {{/users}} 

    </tbody> 

</table>

</script>

Colored by Color Scripter

cs

 

그리고 {{!--   --}}를 통행 주석을 표시해 주고도 있다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

//핸들바 템플릿 컴파일

var template = Handlebars.compile(source); 

 

//핸들바 템플릿에 바인딩할 데이터

var data = {

        users: [

            { name"홍길동1", id: "aaa1" },

            { name"홍길동2", id: "aaa2" },

            { name"홍길동3", id: "aaa3" },

            { name"홍길동4", id: "aaa4" },

            { name"홍길동5", id: "aaa5" }

        ]

}; 

 

//커스텀 헬퍼 등록 (id 인자로 받아서 전체 이메일 주소를 반환)

Handlebars.registerHelper('email'function (id) {

  return id + "@daum.net";

});

 

//핸들바 템플릿에 데이터를 바인딩해서 HTML 생성

var html = template(data);

 

//생성된 HTML DOM 주입

$('body').append(html);

cs

 

스크립트 부분을 보면 Binding할 데이터에 email이 없는걸 확인할 수 있다. 그 대신 Custom Helper가 등록하는 코드가 구현되고 있는걸 확인할 수 있다. Handlebars.registerHelper라는 메소드를 사용하여 사용자 정의 Helper를 등록하고 있다. 첫번째 ParameterHelper의 이름을 넣고, 두 번째 Parameter에서 Helper의 동작을 정의한다. 위 코드에서는 id을 인자로 받아서 ‘@daum.net’이라는 문자열을 더해주는 로직이다 

결과는 아래와 같다.

 

1

2

3

4

5

6

7

 

이름    아이디    메일주소

홍길동1    aaa1    aaa1@daum.net

홍길동2    aaa2    aaa2@daum.net

홍길동3    aaa3    aaa3@daum.net

홍길동4    aaa4    aaa4@daum.net

홍길동5    aaa5    aaa5@daum.net

cs

 

 

3)    Handlebar에서 제공하는 기본 Helper

 

Handlebar if, unless 등의 기본 Helper와 사용자 정의 Helper를 이용해서 간단한 로직을 구현할 수 있다하지만 if의 경우 오직 true/false만 판별할 수 있기 때문에 복잡한 조건은 Handlebar를 통해서 처리할 수 없다. 사용자 정의 helper를 통해 가능하지만, Temple에 로직을 배제하는 사상과 맞지 않기 때문에 가능하면 사용하지 않는게 좋다.

그럼 Handlebars에서 제공하는 기본 Helper를 알아보자

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

<script id="entry-template" type="text/x-handlebars-template">

<table>

    <thead> 

        <th>이름</th> 

        <th>아이디</th> 

        <th>메일주소</th>

        <th>요소정보</th>

    </thead> 

    <tbody> 

        {{!-- {{#each users}}  {{#users}} 로도 대체 가능하다 --}}

        {{#each users}}

        <tr>

            {{!-- {{name}}  {{this.name}}  같고 {{.}}  현재 name id 포함하고 있는 오브젝트를 가리킨다 --}}

            <td>{{name}}</td> 

            <td>{{id}}</td> 

            

            {{!-- 사용자 정의 헬퍼인 email id 인자로 넘긴다 --}}

            <td><a href="mailto:{{email id}}">{{email id}}</a></td> 

            

            {{!-- 요소의 순번은 @index 혹은 @key 잡을  있는데,

            array object 모두 잡을  있는 key  나아보인다 --}}

            

            {{#if @first}}

                <td> 아이템 ({{@key}} 번째 요소)</td>

            {{else if @last}}

                <td>마지막 아이템 ({{@key}} 번째 요소)</td>

            {{else}}

                <td>중간 아이템 ({{@key}} 번째 요소)</td>

            {{/if}}

        </tr> 

        {{/each}}

    </tbody> 

</table>

</script>

Colored by Color Scripter

cs

 

 

배열 등을 반복해주는 each Helper의 경우에는 #배열명으로 대체가 가능하다. @기호와 함께 몇 번째 인덱스인지도 구분이 가능하다. @first로 첫번째 인덱스를 알 수 있고, @last로 마지막 인덱스를 확인할 수 있다. 또한 해당 인덱스가 몇 번째 인덱스인지, 오브젝트라면 어떤 오브젝트인지 @key를 통해 알 수 있다. 앞에서 설명했듯이 if true/false 만 판별이 가능하며, elseelse if 를 지원하며, unless if의 반대되는 Helper이다.

 

 

4)    partial template 등록 및 사용

 

다른 템플릿엔진처럼 HandlebarHandlebar 템플릿을 다른 Handlebar 템플릿의 일부로 포함시킬 수 잇다. 이를 통해 템플릿의 재 사용성과 가독성을 높일 수 있고, 좀 더 객체지향적인 프로그래밍이 가능하다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

<!-- 핸들바 템플릿 -->

<script id="partial-template" type="text/x-handlebars-template">

    {{!-- #unless 헬퍼는 #if  정반대 기능을 한다--}}

    <h1>리스트 {{#unless users}}<small>사용자 리스트가 없습니다.</small>{{/unless}}</h1>

</script>

 

<!-- 핸들바 템플릿 -->

<script id="entry-template" type="text/x-handlebars-template">

 

{{!-- 조각 템플릿은 #>  사용해서 포함시킬  있다--}}

{{#> commonHeader}}

    partial template 로드 실패시 보여지는 내용

{{/commonHeader}}

 

<table>

    <thead> 

        <th>이름</th> 

        <th>아이디</th> 

        <th>메일주소</th> 

        <th>요소정보</th>

    </thead> 

    <tbody> 

        {{!-- {{#each users}}  {{#users}} 로도 대체 가능하다 --}}

        {{#each users}} 

        <tr> 

            <td>{{name}}</td> 

            <td>{{id}}</td> 

            

            {{!-- 사용자 정의 헬퍼인 email id 인자로 넘긴다 --}}

            <td><a href="mailto:{{email id}}">{{email id}}</a></td> 

            

            {{!-- 요소의 순번은 @index 혹은 @key 잡을  있는데,

            array object 모두 잡을  있는 key  나아보인다 --}}

            

            {{#if @first}}

                <td> 아이템 ({{@key}} 번째 요소)</td>

            {{else if @last}}

                <td>마지막 아이템 ({{@key}} 번째 요소)</td>

            {{else}}

                <td>중간 아이템 ({{@key}} 번째 요소)</td>

            {{/if}}

        </tr> 

        {{/each}}

    </tbody> 

</table>

</script>

Colored by Color Scripter

cs

 

위의 예를 보면, 앞서 나온 예제와는 다른 부분이 있다. Template을 담고 있는 Script 태그가 한 개 더 있다.

 

1

2

3

4

<script id="partial-template" type="text/x-handlebars-template">

{{!-- #unless 헬퍼는 #if  정반대 기능을 한다--}}

<h1>리스트 {{#unless users}}<small>사용자 리스트가 없습니다.</small>{{/unless}}</h1>

</script>

Colored by Color Scripter

cs

 

Entry-template 안을 잘 보면, #> 기호와 함께 partial template을 삽입해 주는 부분을 발견 할 수 있다. 이는 #>와 함께 partial template의 이름을 넣어주면 해당 위치에 partial template이 삽입된다. partial template의 이름은 자바스크립트에서 등록한다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

//핸들바 템플릿 컴파일

var template = Handlebars.compile(source); 

 

//핸들바 템플릿에 바인딩할 데이터

var data = {

        users: [

            { name"홍길동1", id: "aaa1" },

            { name"홍길동2", id: "aaa2" },

            { name"홍길동3", id: "aaa3" },

            { name"홍길동4", id: "aaa4" },

            { name"홍길동5", id: "aaa5" }

        ]

}; 

 

//조각 템플릿을 'commonHeader' 라는 이름으로 등록

Handlebars.registerPartial('commonHeader', partial);

 

//커스텀 헬퍼 등록 (id 인자로 받아서 전체 이메일 주소를 반환)

Handlebars.registerHelper('email'function (id) {

  return id + "@daum.net";

});

 

//핸들바 템플릿에 데이터를 바인딩해서 HTML 생성

var html = template(data);

 

//생성된 HTML DOM 주입

$('body').append(html);

cs

 

위 코드를 보면, entry-template 과 마찬가지로 partial-template을 가져온 뒤, Handlebars.registerPartial 메소드를 사용해서 가져온 TemplatecommonHeader라는 이름으로 등록해주고 있는 것을 확인할 수 있다. 이렇게 등록만 해주면 데이터를 Binding해주는 시점에서 partial template을 삽입해주게 되고, partial template에도 주어진 데이터와 Binding될 부분이 있다면 데이터 Binding역시 처리되게 된다.

 

 

5)    withas 그리고 상대 경로 참조


지금까지 살펴본 내용들은 기본적인 Handlebars의 문법이었다면, 이런 문법을 조합한 좀더 복잡한 구조를 살펴보자. 자주 있는 상황은 아니지만, 이중 혹은 삼중의 Loop을 돌리는 상황에서 상위 Loop의 값을 하위 Loop에서 참조해야 하는 상황에서 활용할 수 있다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

<!-- 핸들바 템플릿 -->

<script id="entry-template" type="text/x-handlebars-template">

 

    <table>

        <thead> 

            <th>이름</th> 

            <th>아이디</th> 

            <th>취미</th>

        </thead> 

        <tbody> 

            {{!-- {{#each users}}  {{#users}} 로도 대체 가능하다 --}}

            {{#each users as |user userId|}} 

                <tr> 

                    <td>{{name}}</td> 

                    <td>{{id}}</td> 

                    <td>

                      {{#each hobbys as |hobby hobbyId|}}

                        {{!-- 처음이 아닌 경우에는 쉼표(,) 넣기 --}}

                        {{#unless @first}}, {{/unless}}

 

                                                {{!-- 상위 이터레이션의 인덱스 넘버를 가져올  --}}

                                                {{@../index}} ==

                        {{!-- 상위 이터레이션의 인덱스 넘버는 아래와 같은 방식도 가능하다 --}}

                        {{userId}}

 

                        {{!-- 상대경로로 참조한 name  myName 이라는 변수로 할당 --}}

                        {{#with ../this.name as |myName|}}

                          {{!-- 상대경로를 참조해서 #hobby 현재값 출력 --}}

                          {{myName}}  {{hobbyId}} 취미 {{hobby}}

                        {{/with}}

                      {{/each}}

                    </td>

                </tr> 

            {{/each}}

        </tbody> 

    </table>

 

</script>

Colored by Color Scripter

cs

 

../ 를 사용해서 상위의 경로를 참조할 수 있고, with as 를 활용해서 이렇게 참조한 상위의 값을 다른 이름으로 할당해서 사용하고 있다.

상위 Loop에서 참조하고 있는 user item에 있는 name myName으로 할당하여 활용하고 있다. 그리고 상대 경로를 참조하여 hobby의 값도 출력해주고 있다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//핸들바 템플릿 가져오기

var source = $("#entry-template").html();

 

//핸들바 템플릿 컴파일

var template = Handlebars.compile(source); 

 

//핸들바 템플릿에 바인딩할 데이터

var data = {

        users: [

            { name"홍길동", id: "aaa", hobbys: ['축구''야구''농구'] },

            { name"이순신", id: "bbb", hobbys: ['족구''피구''탁구'] },

            { name"이성계", id: "ccc", hobbys: ['수구''배구''농구'] },

            { name"장영실", id: "ddd", hobbys: ['축구''피구''농구'] },

            { name"장보고", id: "eee", hobbys: ['배구''야구''족구'] }

        ]

}; 

 

//핸들바 템플릿에 데이터를 바인딩해서 HTML 생성

var html = template(data);

 

//생성된 HTML DOM 주입

$('body').append(html);

Colored by Color Scripter

cs

 

결과는 아래와 같다.

1

2

3

4

5

6

이름    아이디    취미

홍길동    aaa    0 == 0 홍길동  0 취미 축구 , 0 == 0 홍길동  1 취미 야구 , 0 == 0 홍길동  2 취미 농구

이순신    bbb    1 == 1 이순신  0 취미 족구 , 1 == 1 이순신  1 취미 피구 , 1 == 1 이순신  2 취미 탁구

이성계    ccc    2 == 2 이성계  0 취미 수구 , 2 == 2 이성계  1 취미 배구 , 2 == 2 이성계  2 취미 농구

장영실    ddd    3 == 3 장영실  0 취미 축구 , 3 == 3 장영실  1 취미 피구 , 3 == 3 장영실  2 취미 농구

장보고    eee    4 == 4 장보고  0 취미 배구 , 4 == 4 장보고  1 취미 야구 , 4 == 4 장보고  2 취미 족구

cs

 

 

6)    lookup을 활용한 변수 참조


 lookup Helper를 활용하면 특정 상대 경로에 있는 변수 값을 참조할 수 있다. lookup Helper로 참조할 수 있는 변수 값은 with as 를 활용하는 방법으로 거의 대부분 참조 가능하다. 개인적으로는 with as 를 활용하는 것이 더 명시적이기 때문에 선호한다.

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

<!-- 핸들바 템플릿 -->

<script id="entry-template" type="text/x-handlebars-template">

 

    <table>

        <thead> 

            <th>이름</th> 

            <th>아이디</th> 

            <th>취미</th>

        </thead> 

        <tbody> 

            {{!-- {{#each users}}  {{#users}} 로도 대체 가능하다 --}}

            {{#each users as |user userId|}} 

                <tr> 

                    <td>{{name}}</td> 

                    <td>{{id}}</td> 

                    <td>

                      {{#each hobbys as |hobby hobbyId|}}

                          {{!-- 

                                                    상대경로를 참조해서 상위 이터레이션의 name 값을 가져오는 것은

                          with 헬퍼를 사용해서 {{#with ../this.name as |myName|}}

                           같이 처리해줄 수도 있고 아래와 같이

                                                    lookup 활용해서 처리해줄 수도 있다.

                                                --}}

                        {{lookup ../this "name"}}  {{hobbyId}} 취미 {{hobby}}

                      {{/each}}

                    </td>

                </tr> 

            {{/each}}

        </tbody> 

    </table>

 

</script>

Colored by Color Scripter

cs

 

Lookup을 통해 상대 경로에  name값을 찾아서 출력해주고 있다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//핸들바 템플릿 가져오기

var source = $("#entry-template").html();

 

//핸들바 템플릿 컴파일

var template = Handlebars.compile(source); 

 

//핸들바 템플릿에 바인딩할 데이터

var data = {

        users: [

            { name"홍길동", id: "aaa", hobbys: ['축구''야구''농구'] },

            { name"이순신", id: "bbb", hobbys: ['족구''피구''탁구'] },

            { name"이성계", id: "ccc", hobbys: ['수구''배구''농구'] },

            { name"장영실", id: "ddd", hobbys: ['축구''피구''농구'] },

            { name"장보고", id: "eee", hobbys: ['배구''야구''족구'] }

        ]

}; 

 

//핸들바 템플릿에 데이터를 바인딩해서 HTML 생성

var html = template(data);

 

//생성된 HTML DOM 주입

$('body').append(html);

Colored by Color Scripter

cs

 

 

1

2

3

4

5

6

이름    아이디    취미

홍길동    aaa    홍길동  0 취미 축구 홍길동  1 취미 야구 홍길동  2 취미 농구

이순신    bbb    이순신  0 취미 족구 이순신  1 취미 피구 이순신  2 취미 탁구

이성계    ccc    이성계  0 취미 수구 이성계  1 취미 배구 이성계  2 취미 농구

장영실    ddd    장영실  0 취미 축구 장영실  1 취미 피구 장영실  2 취미 농구

장보고    eee    장보고  0 취미 배구 장보고  1 취미 야구 장보고  2 취미 족구

cs

 

 

2.    Handlebar의 장점

 

  • precompile할 수 있기 때문에 성능개선에 도움이 된다.

  • Helper를 자유롭게 추가할 수 있다.

  • JADE와 같은 문법에 비해 친숙하면서도 가독성이 높다.

  • Grunt 등으로 빌드 자동화에 precompile 과정을 포함시키기 쉽다.

  • 템플릿에는 로직 코드를 넣지 않는 것이 일반적이다.

    • 컴파일한 템플릿에서 오류가 발생했을 때 디버깅도 어렵다.

    • 실제 문법에서도 간단한 분기문, 배열, 반복문 정도를 지원한다.

 


 

참고사이트

 

페이지: http://handlebarsjs.com/

깃허브: https://github.com/wycats/handlebars.js/

http://ohgyun.com/427

http://sailboat-d.tistory.com/40

http://jojoldu.tistory.com/23

http://tmondev.blog.me/220398995882

http://programmingsummaries.tistory.com/381

https://blog.outsider.ne.kr/939

http://aroundck.tistory.com/957

http://uncle-bae.blogspot.kr/2016/06/handlebars-expression.html


'IT > Handlebar' 카테고리의 다른 글

Handlebar란  (0) 2017.01.12



1.    Handlebar.js


Handlebar.js(이하 Handlebar) 자바스크립트의 템플릿 엔진 하나이다. 먼저 템플릿 엔진이라 하면 프로그램 로직과 프레젠테이션 계층을 분리하기위한 수단이다.  Mustache 기반으로 구현한 템플릿 인자, 여기서 Mustache 콧수염모양의 {{ }} Bracket 이용하여 data 표현하는 것을 의미한다. 이를 이용하면 html페이지에서 HTML+Bracket 구성으로 디자이너와 개발자가 협업할 때도 디자이너에게도 이해하기 쉬운 구조로써 협업을 하는데도 도움이 된다.

 

 

2.    기본 사용방법


표현식


  • {{title}}  같은 형태로 표현되며, 현재 Context에서 title이라는 Property를 찾아서 대체하는 의미이다.

  • {{section.title}}  같은 형태로 dot(.)으로 분리된 경로 탐색도 가능하다. 현재 Context에서 section을 찾고, title 속성을 찾아서 대체하는 의미이다.

  • {{section/title}} 과 같이 / 문법을 사용할 수 있다. 식별자는 아래 열거된 UniCode를 제외하고 모두 사용 가능하다.

    • Whitespace ! " # % & ' ( ) * + , . / ; < = > @ [ \ ] ^ ` { | } ~

  • 배열 접근 표현식을 통해 Property에 접근할 수 있다section 배열의 3번째 titles 의 모든 property 를 context 로 갖으면서 subject 와 body 에 접근한다.

1

2

3

4

5

6

{{#each section.[3].titles}}

  <h1>{{subject}}</h1>

  <div>

    {{body}}

  </div>

{{/each}}

cs


  • {{! }} 블록으로 주석을 추가할 수 있다.

  • {{log}} 블록으로 템플릿 출력 시 로깅할 수 있다.

  • {{{body}}} HTML요소는 자동으로 이스케이프 시켜준다.

 

Helper


  • Helper 기본적으로 다른 개발 언어의 함수라고 볼 수 있다.

  • 기본 문법은 아래와 같다.

 

1

{{{ Helper_Name param1 parm2 ... }}}

cs

  • HandlebarsHelper Call은 단순한 식별자이며, 0개 이상의 Parameter를 받을 수 있다. 그리고 구분은 공백으로 한다. 나누게 된다.  Handlesbars는 위에 설명한 표현식과 완전히 동일한 방법으로 사용된다.

  • 반복과 같은 다른 템플릿엔진에서 제공하는 Helper 존재한다.

 

1

2

3

{{#each userlist}}

{{user}}

{{/each}}

cs

 

아래 코드는 link라는 Helper story라는 Parameter를 넣어 실행시킨 결과를 출력하라는 의미이다.

 

1

{{{link story}}}

cs

 

link helper  Handlebars.registerHelper(Helper_Name, function ) 으로 등록한다.

 

1

2

3

4

5

Handlebars.registerHelper('link'function(object) {

  return new Handlebars.SafeString(

    "<a href='" + object.url + "'>" + object.text + "</a>"

  );

});

Colored by Color Scripter

cs

 

Helper 는 최종 Parameter들로 key-value pair hash 를 전달할 수도 있다.

 

1

{{{link "See more..." href=story.url class="story"}}}

cs

 

이럴 경우 Helper 함수에서는 hash 부분을 하나의 object 로 받아서, object.hash 로 접근하여 key-value pair 에 대한 context를 얻을 수 있다.

 

1

2

3

4

5

6

7

8

9

10

11

Handlebars.registerHelper('link'function(text, options) {

  var attrs = [];

 

  for(var prop in options.hash) {

    attrs.push(prop + '="' + options.hash[prop] + '"');

  }

 

  return new Handlebars.SafeString(

    "<a " + attrs.join(" "+ ">" + text + "</a>"

  );

});

Colored by Color Scripter

cs

 

Subexpressions


Handlebars subexpression을 제공한다. 이는 단일 mustache를 이용하여 복합 Helper를 호출할 수 있다. 그리고 내부 Helper의 결과를 외부 Helper Parameter로 전달 할 수 있다. Subexpression ( )로 구분한다.

1

{{outer-helper (inner-helper 'abc''def' }}

cs

 

위 코드에서는 inner-helper는 문자열 'abc'를 받는 Helper로 호출이 되며 이 결과는 outer-helper의 첫번째 Parameter로 전달된다. 그리고 'def'는 두번째 Parameter 로 전달된다.

 

Whitespace Control


템플릿의 공백은 특정 mustache스테이트먼트의 각 부분에서 ~ 문자를 브레이스{의 의해서 생략이 될 수 있다.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

{

    nav: [

        {url: 'foo', test: true, title: 'bar'},

        {url: 'bar'}

    ]

}

 

 

{{#each nav ~}}

    <a href="{{url}}">

        {{~#if test}}

            {{~title}}

        {{~^~}}

            Empty

        {{~/if~}}

    </a>

{{~/each}}

Colored by Color Scripter

cs

 

출력 결과 뉴라인과 포매팅된 공백 처리 결과는 아래와 같다.

 

1

<a href="foo">bar</a><a href="bar">Empty</a>

cs

 

이 확장은 라인 처리의 기본적인 동작이며 "standalone" Helper이다.

 

1

2

3

4

5

6

7

8

9

{{#each nav}}

    <a href="{{url}}">

        {{#if test}}

            {{title}}

        {{^}}

            Empty

        {{/if}}

    </a>

{{~/each}}

cs

 

 

결과는 아래와 같다.

 

1

2

3

4

5

6

<a href="foo">

    bar

</a>

<a href="bar">

    Empty

</a>

cs

 

 

 

Escaping


Handlebars컨텐츠는 하나 혹은 두개의 방법으로 이스케이프 될 수 있다. 이는 인라인 이스케이프 혹은 raw block helpers이다. 인라인 escape prefixing mustache블록으로 \으로 시작하는 블록이다. Raw block {{{{ mustache 브레이스로 생성된다.

 

1

2

3

4

\{{escaped}}

{{{{raw}}}}

    {{escaped}}

{{{{/raw}}}}

cs



 

참고사이트

 

페이지: http://handlebarsjs.com/

깃허브: https://github.com/wycats/handlebars.js/

http://ohgyun.com/427

http://sailboat-d.tistory.com/40

http://jojoldu.tistory.com/23

http://tmondev.blog.me/220398995882

http://programmingsummaries.tistory.com/381

https://blog.outsider.ne.kr/939

http://aroundck.tistory.com/957

http://uncle-bae.blogspot.kr/2016/06/handlebars-expression.html

 

'IT > Handlebar' 카테고리의 다른 글

Handlebars 코딩 해보기  (0) 2017.01.12

Lombok Annotation

@Getter / @Setter

Annotation Getter Setter 함수를 생성해준다. 그리고  AccessLevel(PUBLIC, PROTECTED, PACKAGE, PRIVATE) 지정을 통해서 접근 레벨도 제한할 있으며, 특정 필드는 메서드 생성이 필요하지 않을 경우 AccessLevel.NONE 설정을 통해 자동 메소드 생성을 막을 있다.

 

아래 예제 소스를 통해 어떻게 활용하는지 알아보자.

Lombok Code

 

1

2

@Getter @Setter private boolean employed = true;

@Setter(AccessLevel.PROTECTED) private String name;

cs

 

Java Code

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

private boolean employed = true;

private String name;

 

public boolean isEmployed() {

    return employed;

}

 

public void setEmployed(final boolean employed) {

    this.employed = employed;

}

 

protected void setName(final String name) {

    this.name = name;

}

Colored by Color Scripter

cs

 

 

@NonNull

@NonNull Class위에서는 사용이 불가능하며, Class 내에서 필요한 멤버에 직접 사용해야 한다. @NonNull 사용하면 @Setter 생성되는 setter 메서드에 전달되는 parameter @NonNull 붙게 된다. 하지만 해당 setter 메서드에 null 넘기도록 코드를 작성 수도 있다. , 컴파일 시점에서 적용되지 않고 runtime시에 Null Check 하여 NullPointerException 발생하도록 하는 것이다. , setXXX null을 넘겨줄 경우 if (xxx==null) 체크 코드가 들어가서 throw new NullPointerException("XXX")을 던져준다. 멤버 변수에 붙여줄 경우에는 그 필드를 꼭 받도록 생성자가 수정된다.

 

Lombok Code

1

2

@Getter @Setter @NonNull

private List<Person> members;

cs

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

@NonNull

private List<Person> members;

 

public Family(@NonNull final List<Person> members) {

    if (members == nullthrow new java.lang.NullPointerException("members");

    this.members = members;

}

    

@NonNull

public List<Person> getMembers() {

    return members;

}

 

public void setMembers(@NonNull final List<Person> members) {

    if (members == nullthrow new java.lang.NullPointerException("members");

    this.members = members;

}

Colored by Color Scripter

cs

 

 

@ToString

@ToStringClass에 있는 필드들을 검사해서 문자열로 변환해주는 toString() 메소드를 생성한다. 상호 참조하는 객체의 경우에는 toString() 호출시 Stack Overflow 가 발생할 수 있다. 한쪽 객체에서 다른 쪽 객체에 대해 @ToString(excluded={“propertyName”}) 형태로 제외토록 만들어야 한다.

@EqualsAndHashCode(of = {})로 꼭 필요한 필드만 비교하도록 처리한다.

 

 

Lombok Code

1

2

3

4

5

6

@ToString(callSuper=true,exclude="someExcludedField")

public class Foo extends Bar {

    private boolean someBoolean = true;

    private String someStringField;

    private float someExcludedField;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

public class Foo extends Bar {

    private boolean someBoolean = true;

    private String someStringField;

    private float someExcludedField;

    

    @java.lang.Override

    public java.lang.String toString() {

        return "Foo(super=" + super.toString() +

            ", someBoolean=" + someBoolean +

            ", someStringField=" + someStringField + ")";

    }

}

Colored by Color Scripter

cs

 

 

@EqualsAndHashCode

@EqualsAndHashCode은 코드에서 객체 비교 등의 용도로 사용되는 equals(), hashCode() 메소드의 코드를 생성해준다. 그리고 exclude={field-name,..}을 통해 특정 필드를 제외할 수도 있다.

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})

public class Person extends SentientBeing {

    enum Gender { Male, Female }

 

    @NonNull private String name;

    @NonNull private Gender gender;

    

    private String ssn;

    private String address;

    private String city;

    private String state;

    private String zip;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public class Person extends SentientBeing {

    

    enum Gender {

        /*public static final*/ Male /* = new Gender() */,

        /*public static final*/ Female /* = new Gender() */;

    }

    @NonNull

    private String name;

    @NonNull

    private Gender gender;

    private String ssn;

    private String address;

    private String city;

    private String state;

    private String zip;

    

    @java.lang.Override

    public boolean equals(final java.lang.Object o) {

        if (o == thisreturn true;

        if (o == nullreturn false;

        if (o.getClass() != this.getClass()) return false;

        if (!super.equals(o)) return false;

        final Person other = (Person)o;

        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;

        if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;

        if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) return false;

        return true;

    }

    

    @java.lang.Override

    public int hashCode() {

        final int PRIME = 31;

        int result = 1;

        result = result * PRIME + super.hashCode();

        result = result * PRIME + (this.name == null ? this.name.hashCode());

        result = result * PRIME + (this.gender == null ? this.gender.hashCode());

        result = result * PRIME + (this.ssn == null ? this.ssn.hashCode());

        return result;

    }

}

Colored by Color Scripter

cs

 

 

 

@Data

@DataClass에 정의된 모든 필드에 대한 getter, setter toString, equals, hashCode, final로 지정됐거나 @NonNull로 명시된 필드에 대한 값을 받는 생성자 메소드 코드를 생성해 준다. , 앞에서 배웠던 @Getter, @Setter, @NonNull, @EqualsAndHashCode, @ToString 에 대한 걸 모두 해주는 Annotation이다.

 

Lombok Code

1

2

3

4

5

6

@Data(staticConstructor="of")

public class Company {

    private final Person founder;

    private String name;

    private List<Person> employees;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

public class Company {

    private final Person founder;

    private String name;

    private List<Person> employees;

    

    private Company(final Person founder) {

        this.founder = founder;

    }

    

    public static Company of(final Person founder) {

        return new Company(founder);

    }

    

    public Person getFounder() {

        return founder;

    }

    

    public String getName() {

        return name;

    }

    

    public void setName(final String name) {

        this.name = name;

    }

    

    public List<Person> getEmployees() {

        return employees;

    }

    

    public void setEmployees(final List<Person> employees) {

        this.employees = employees;

    }

    

    @java.lang.Override

    public boolean equals(final java.lang.Object o) {

        if (o == thisreturn true;

        if (o == nullreturn false;

        if (o.getClass() != this.getClass()) return false;

        final Company other = (Company)o;

        if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;

        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;

        if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;

        return true;

    }

    

    @java.lang.Override

    public int hashCode() {

        final int PRIME = 31;

        int result = 1;

        result = result * PRIME + (this.founder == null ? this.founder.hashCode());

        result = result * PRIME + (this.name == null ? this.name.hashCode());

        result = result * PRIME + (this.employees == null ? this.employees.hashCode());

        return result;

    }

    

    @java.lang.Override

    public java.lang.String toString() {

        return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";

    }

}

Colored by Color Scripter

cs

 

 

 

 

@Value
@ValueImmutable Class을 생성해준다.  @Data와 비슷하지만 모든 필드를 기본적으로 Private Final로 로 하고, Setter 함수를 생성하지 않고, Class또한 Final로 지정하는 것만 빼고 동일하다.

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

@Value public class ValueExample {

   String name;

   @Wither(AccessLevel.PACKAGE) @NonFinal int age;

   double score;

   protected String[] tags;

   

   @ToString(includeFieldNames=true)

   @Value(staticConstructor="of")

   public static class Exercise<T> {

     String name;

     T value;

   }

}

Colored by Color Scripter

cs

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

public final class ValueExample {

   private final String name;

   private int age;

   private final double score;

   protected final String[] tags;

   

   @java.beans.ConstructorProperties({"name""age""score""tags"})

   public ValueExample(String name, int age, double score, String[] tags) {

     this.name = name;

     this.age = age;

     this.score = score;

     this.tags = tags;

   }

   

   public String getName() {

     return this.name;

   }

   

   public int getAge() {

     return this.age;

   }

   

   public double getScore() {

     return this.score;

   }

   

   public String[] getTags() {

     return this.tags;

   }

   

   @java.lang.Override

   public boolean equals(Object o) {

     if (o == thisreturn true;

     if (!(o instanceof ValueExample)) return false;

     final ValueExample other = (ValueExample)o;

     final Object this$name = this.getName();

     final Object other$name = other.getName();

     if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;

     if (this.getAge() != other.getAge()) return false;

     if (Double.compare(this.getScore(), other.getScore()) != 0return false;

     if (!Arrays.deepEquals(this.getTags(), other.getTags())) return false;

     return true;

   }

   

   @java.lang.Override

   public int hashCode() {

     final int PRIME = 59;

     int result = 1;

     final Object $name = this.getName();

     result = result * PRIME + ($name == null ? 43 : $name.hashCode());

     result = result * PRIME + this.getAge();

     final long $score = Double.doubleToLongBits(this.getScore());

     result = result * PRIME + (int)($score >>> 32 ^ $score);

     result = result * PRIME + Arrays.deepHashCode(this.getTags());

     return result;

   }

   

   @java.lang.Override

   public String toString() {

     return "ValueExample(name=" + getName() + ", age=" + getAge() + ", score=" + getScore() + ", tags=" + Arrays.deepToString(getTags()) + ")";

   }

   

   ValueExample withAge(int age) {

     return this.age == age ? this : new ValueExample(name, age, score, tags);

   }

   

   public static final class Exercise<T> {

     private final String name;

     private final T value;

     

     private Exercise(String name, T value) {

       this.name = name;

       this.value = value;

     }

     

     public static <T> Exercise<T> of(String name, T value) {

       return new Exercise<T>(name, value);

     }

     

     public String getName() {

       return this.name;

     }

     

     public T getValue() {

       return this.value;

     }

     

     @java.lang.Override

     public boolean equals(Object o) {

       if (o == thisreturn true;

       if (!(o instanceof ValueExample.Exercise)) return false;

       final Exercise<?> other = (Exercise<?>)o;

       final Object this$name = this.getName();

       final Object other$name = other.getName();

       if (this$name == null ? other$name != null : !this$name.equals(other$name)) return false;

       final Object this$value = this.getValue();

       final Object other$value = other.getValue();

       if (this$value == null ? other$value != null : !this$value.equals(other$value)) return false;

       return true;

     }

     

     @java.lang.Override

     public int hashCode() {

       final int PRIME = 59;

       int result = 1;

       final Object $name = this.getName();

       result = result * PRIME + ($name == null ? 43 : $name.hashCode());

       final Object $value = this.getValue();

       result = result * PRIME + ($value == null ? 43 : $value.hashCode());

       return result;

     }

     

     @java.lang.Override

     public String toString() {

       return "ValueExample.Exercise(name=" + getName() + ", value=" + getValue() + ")";

     }

   }

}

Colored by Color Scripter

cs

 

 

@Cleanup

@Cleanup Local 변수에 붙이면 Cleanup code 가 현재 code 가 종료될 때 자동으로 호출해주도록 한다. 그리고 close() method가 있으면 해당 변수 scope 끝까지 자동으로 try { } 로 묶어주고 finally 블록에서 field.close() 를 넣어준다. 이것은 컴파일 타임에 close()를 호출하도록 하는 것이므로 별도의 interface가 필요로 하지 않는다.

close() 가 없고 다른 method cleanup 을 수행할 경우 @Cleanup("dispose") 같이 method명을 작성해주면 해당 메소드를 호출해준다. , 만약 destroy() 을 불러야 하는 경우라면 @Cleanup("destroy")라고 작성해 주면 된다. , try 블록에서 예외가 발생했고 cleanup 메서드에서도 예외가 발생할 경우 cleanup 메서드에서 발생한 예외는 완전히 무시된다. 그러므로 @Cleanup 완전히 의존하는 것은 위험하다.

 

 

Lombok Code

1

2

3

4

5

6

7

8

9

public void testCleanUp() {

    try {

        @Cleanup ByteArrayOutputStream baos = new ByteArrayOutputStream();

        baos.write(new byte[] {'Y','e','s'});

        System.out.println(baos.toString());

    } catch (IOException e) {

        e.printStackTrace();

    }

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

public void testCleanUp() {

    try {

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {

            baos.write(new byte[]{'Y''e''s'});

            System.out.println(baos.toString());

        } finally {

            baos.close();

        }

    } catch (IOException e) {

        e.printStackTrace();

    }

}

Colored by Color Scripter

cs

 

 

 

@Synchronized

@Synchronizedstatic instance lock 오브젝트를 자동으로 생성해주고, Annotation을 메서드에 적용할 경우에는 메서드 Body synchronized 키워드로 감싸준다.

여기서 관심있게 볼 부분은 new Object() serialize 되지 않는 것을 고려하여 $lock, instance용 오브젝트를 new Object[0]로 선언해준다는 것이다. Annotation된 메소드가 정적이면 Lombok을 통해 $LOCK, Class 오브젝트가 자동으로 작성되어 @Synchronized 메소드와 동기화시킨다.

만약 별도의 lock 객체를 생성해서 특정 메서드에만 적용하고 싶다면 (read-lock 같은 경우를 위해) @Synchronized("lock-object-name") 형식으로 쓰면 된다. 대신 lock-object-name은 프로그래머가 직접 필드를 선언해야만 한다. 예를 들면, @Synchronized("myObject") myObject오브젝트와 대조하여 @Synchronized 메소드를 동기화한다.

 

 

 

Lombok Code

1

2

3

4

5

6

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

 

@Synchronized

public String synchronizedFormat(Date date) {

    return format.format(date);

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

private final java.lang.Object $lock = new java.lang.Object[0];

private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");

 

public String synchronizedFormat(Date date) {

    synchronized ($lock) {

        return format.format(date);

    }

}

Colored by Color Scripter

cs

 

 

@SneakyThrows
잘 일어나지 않는 checked exceptions을 처리해준다. 물론 IDE을 통해 checked exception Quick-Fix로 고치면 자동으로 e.printStackTrace()을 붙여준다.

 

 

Lombok Code

1

2

3

4

@SneakyThrows

public void testSneakyThrows() {

    throw new IllegalAccessException();

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

public void testSneakyThrows() {

    try {

        throw new IllegalAccessException();

    } catch (java.lang.Throwable $ex) {

        throw lombok.Lombok.sneakyThrow($ex);

    }

}

Colored by Color Scripter

cs

 

val

Local 변수 선언을 val을 이용해서 하면 초기화 값을 이용하여 변수 타입을 유추해서 final로 생성해 준다. 즉 자바스크립트에서 var 키워드와 동일한 기능을 한다고 생각하면 된다.

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public String example() {

    val example = new ArrayList<String>();

  example.add("Hello, World!");

  val foo = example.get(0);

  return foo.toLowerCase();

}

public void example2() {

  val map = new HashMap<Integer, String>();

  map.put(0"zero");

  map.put(5"five");

  for (val entry : map.entrySet()) {

    System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());

  }

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public String example() {

     final ArrayList<String> example = new ArrayList<String>();

     example.add("Hello, World!");

     final String foo = example.get(0);

     return foo.toLowerCase();

}

   

public void example2() {

     final HashMap<Integer, String> map = new HashMap<Integer, String>();

     map.put(0"zero");

     map.put(5"five");

     for (final Map.Entry<Integer, String> entry : map.entrySet()) {

       System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());

     }

}

Colored by Color Scripter

cs

 

 

 

 

@Log

자동으로 logging을 위한 필드인 private static final Logger log 가 추가됩니다. 이후 로그를 출력하려는 곳에서 log.error(), log.warn(), log.debug(), log.info() 형태로 사용하면 된다.

이외에도 다양한 로그 타입을 지원한다. (@CommonsLog, @JBossLog, @Log4j, @Log4j2, @Slf4j, @XSlf4j)

 

Lombok Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@Log

public class LogExample {

   public static void main(String... args) {

     log.error("Something's wrong here");

   }

}

 

@Slf4j

public class LogExampleOther {

   public static void main(String... args) {

     log.error("Something else is wrong here");

   }

}

 

@CommonsLog(topic="CounterLog")

public class LogExampleCategory {

   public static void main(String... args) {

     log.error("Calling the 'CounterLog' with a message");

   }

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

public class LogExample {

   private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

   

   public static void main(String... args) {

     log.error("Something's wrong here");

   }

}

 

public class LogExampleOther {

   private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);

   

   public static void main(String... args) {

     log.error("Something else is wrong here");

   }

}

 

public class LogExampleCategory {

   private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");

 

   public static void main(String... args) {

     log.error("Calling the 'CounterLog' with a message");

   }

}

Colored by Color Scripter

cs

 

 

@Builder
@Builder AnnotationClass에 대한 복잡한 Builder API들을 자동으로 생성해 준다. 그리고 @BuilderClass 뿐만 아니라, 생성자, 메서드에도 사용할 수 있다. 추가적으로 @Singular Annotation도 제공한다. @Singular@ Builder Annotation을 사용한 Class/Method/Constructor에서 Collection 객체에만 사용할 수 있다. Collection 변수에 @Singular을 설정하면 setter 함수 대신 두 종류의 Adder 함수와 Clear함수를 생성해 준다. Adder 함수는 1개 또는 모두 요소를 추가하는 두 종류의 Adder 함수가 생성된다. 그리고 Collectionclear 하는 함수도 생성해준다. 현재 Lombok에서 지원하는 Collectionjava.utilGuava(com.google.common.collect) 만을 지원한다.

 

 

Lombok Code

1

2

3

4

5

6

@Builder

public class BuilderExample {

   private String name;

   private int age;

   @Singular private Set<String> occupations;

}

Colored by Color Scripter

cs

 

 

Java Code

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

public class BuilderExample {

   private String name;

   private int age;

   private Set<String> occupations;

   

   BuilderExample(String name, int age, Set<String> occupations) {

     this.name = name;

     this.age = age;

     this.occupations = occupations;

   }

   

   public static BuilderExampleBuilder builder() {

     return new BuilderExampleBuilder();

   }

   

   public static class BuilderExampleBuilder {

     private String name;

     private int age;

     private java.util.ArrayList<String> occupations;

     

     BuilderExampleBuilder() {

     }

     

     public BuilderExampleBuilder name(String name) {

       this.name = name;

       return this;

     }

     

     public BuilderExampleBuilder age(int age) {

       this.age = age;

       return this;

     }

     

     public BuilderExampleBuilder occupation(String occupation) {

       if (this.occupations == null) {

         this.occupations = new java.util.ArrayList<String>();

       }

       

       this.occupations.add(occupation);

       return this;

     }

     

     public BuilderExampleBuilder occupations(Collection<extends String> occupations) {

       if (this.occupations == null) {

         this.occupations = new java.util.ArrayList<String>();

       }

 

       this.occupations.addAll(occupations);

       return this;

     }

     

     public BuilderExampleBuilder clearOccupations() {

       if (this.occupations != null) {

         this.occupations.clear();

       }

       

       return this;

     }

 

     public BuilderExample build() {

       // complicated switch statement to produce a compact properly sized immutable set omitted.

       // go to https://projectlombok.org/features/Singular-snippet.html to see it.

       Set<String> occupations = ...;

       return new BuilderExample(name, age, occupations);

    }

     

     @java.lang.Override

     public String toString() {

       return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";

     }

   }

}

Colored by Color Scripter

cs

 

 

그 이외에 Lombok의 공식적인 Feature은 아니지만 테스트 중인 다양한 feature들도 있다.

 

참고 사이트

 

https://projectlombok.org/features/

http://jnb.ociweb.com/jnb/jnbJan2010.html

https://blogs.idincu.com/dev/?p=17

http://edoli.tistory.com/99

http://kwonnam.pe.kr/wiki/java/lombok?s[]=lombok

 

'IT > Lombok' 카테고리의 다른 글

LOMBOK 이란  (0) 2017.01.06

LOMBOK

자바 개발자라면 누구나 경험 했을 것이다. 간단한 Class 하나 만드는데 특히 VO, DTO, Entity등의 Class 만들 getter/setter 함수 등을 기계적으로 생성했어야 했던 귀찮은 작업들을.

 

1

2

3

4

5

6

7

8

9

public class inconvenientClass{

    private T a;

    public T getA () {

     return a;

    } 

    public void setA(T a) {

     this.a = a;

    }

}

cs

 

코드와 같이 단순히 변수 하나만 정의된 간단한 class이지만, 값을 핸들링 하기 위해 쓸데없이 작성해야 하는 코드가 많았다.

물론 IDE 자동생성 기능을 통해 쉽게 생성할 있지만, 여전히 변수 명이 변경되면 다시 getter/setter 함수를 변경해줘야 하는 번거로움이 발생한다. 이를 해결해 주기 나온 Lombok 이다. 간단하게 이해하면 Getter/Setter 자동 생성라이브러리라고 생각하면 된다. 물론 다양한 기능을 제공한다. 그럼 지금부터 하나씩 알아 가보자

Lombok이란?

Lombok 자바에서 @Getter, @Setter 같은 annotation 기반으로 관련 기존 DTO, VO, Domain Class 작성할 , 멤버 변수에 대한 Getter/Setter Method, Equals(), hashCode(), ToString() 멤버 변수에 값을 설정하는 생성자 등등을 자동으로 생성해 주는 라이브러리다.

먼저 Lombok 사용한 코드와 사용하지 않는 코드를 먼저 보자

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

public class BasicSampel {

    private Integer id;

    private String name;

 

    public Integer getId() {

        return id;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

 

    @Override

    public boolean equals(Object o) {

        if (this == o) return true;

        if (!(o instanceof LombokSampel)) return false;

 

        LombokSampel that = (LombokSampel) o;

 

        if (id != null ? !id.equals(that.id) : that.id != nullreturn false;

        return name != null ? name.equals(that.name) : that.name == null;

    }

 

    @Override

    public int hashCode() {

        int result = id != null ? id.hashCode() : 0;

        result = 31 * result + (name != null ? name.hashCode() : 0);

        return result;

    }

}

 

Colored by Color Scripter

cs

 

아래 코드는 Lombok 사용했다. 깜짝 놀랄 정도로 소스가 짧아졌다는 눈으로 확인할 있을 것이다.

 

1

2

3

4

5

6

7

8

9

import lombok.Data;

import lombok.Setter;

import lombok.EqualsAndHashCode;

 

@Data

public class LombokSampel {

    private Integer id;

    private String name;

}

Colored by Color Scripter

cs

 

샘플 코드를 통해 확인했듯이 Lombok 사용한 코드가 매우 짧아졌다. 필요한 곳에 적당한 Lombok Annotation 붙여 주기만 하면 끝이다. Annotation 문자 그대로 이해할 있을 정도로 매우 직관적이다. @Getter  getter() 함수를, @Setter setter() 함수를 생성하고 @EqualsAndHashCode  equals() 함수와 hashCode() 함수를 자동으로 생성해준다.

그럼 Lombok에서 제공해주는 Annotation 무엇이 있는지 알아보자.


'IT > Lombok' 카테고리의 다른 글

Lombok Annotation  (0) 2017.01.10

정규 표현식. 개발자라면 한번쯤은 정복해 보고 싶은 분야일것이다.

3~4년 전부터 공부하기 시작했지만 하다 어려워서 중간에 포기하다가 이제야 다시 공부 하면서 정리한 내용이다.

처음 배우는 사람들도 쉽게 이해할 수 있도록 초보자 입장에서 최대한 쉽게 쓰려고 노력했다.


앞으로 강좌는 아래와 같은 목차로 진행될 예정이다.


1. 정규 표현식 개요

2. 문자열 찾기

3. 문자 집합(Character Set)

4. 메타 문자(Meta Character)

5. 반복 문자 찾기

6. 위치 찾기

7. 하위 표현식 

8. 수량자(Quantifier)

9. 역참조

10. 전방/후방 탐색

11. 조건 달기


정규표현식의 사전적인 의미로는 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용하는 형식 언어이다. 

다시 설명하면 문자열을 처리하는 방법 언어로써 특정한 조건의 문자를 "검색"하거나 "치환"하기 위한 용도로 사용한다.

프로그램 언어로 문자열을 처리하기 위해서는 조건문을 사용할 경우 매우 복잡해 질 수가 있지만, 정규표현식을 이용하면 간단하게 표현이 가능하다.

하지만 코드가 간단하지만 가독성이 떨어져 정규표현식을 모르는 사람이 볼 경우 이해하기 힘들 수 있다.


정규표현식을 연습할 수 있는 사이트는 아래와 같다. 

 

이 강좌도 아래 사이트를 이용하여 실습을 진행할 예정이다



정규표현식을 시각화해서 보여주는 도구


정규표현식에 대한 도움말과 각종 사례들을 보여주는 서비스로 정규표현식을 라이브로 만들 수 있는 기능도 제공



'IT > 정규표현식' 카테고리의 다른 글

정규 표현식 - 개요  (0) 2017.02.28

이번에는 자바에서 많이 사용하게 되는 문자열 Class에 대해 알아보자.


Java에서 제공하는 문자열 Class String, StringBuffer, StringBuilder들이 있다.


보통은 String Class를 사용하지만 Java를 좀더 Deep 하게 알게 되고, 문자열 처리에서 시간이 너무 많이 걸렸던 문제를 경험하게 되면 StringBuffer를 사용하게 된다. 두 문자열 Class의 차이는 메모리 처리인데 그로 인해 상황에 따라 성능 차이가 발생하게 된다.


그럼 각 Class의 차이점에 대해 알아보자.


간단히 보면 아래 표와 같다.


String

StringBuffer

StringBuilder

Immutable(불변함)

Mutable(변함, 변하기 쉬움)

 

synchronized

unsynchronized


그럼 차이점을 기준으로 하나씩 이야기 해보겠다.


먼저 String StringBuffer/StringBuilder의 차이점부터 확인해보자.


String은 변경이 불가능한 immutable 클래스이다. (String는 내부적으로 char[]를 사용하는데 이 배열은 변경이 불가능하다. ) , 한번 생성되면 String 객체가 변할 수 없다. 여기서 변경이 불가능하다는 말이 무슨 뜻일까?


먼저 아래 소스를 보자.



String str = "Hello";

        str = str.concat(" World");

    str += " JAVA";



위에서 문자열 처리에 사용한 concat() 메소드와 “+” 연산자를 보면 처음 생성한 str String 객체가 변경이 가능한 것처럼 보인다. 하지만 String Class의 동작 원리를 보면 원래 객체와 다른 새로운 String 객체를 만들어 반환해 준다. 새로 String 객체가 생성되기 때문에 기존 String 객체가 가지고 있는 문자열은 변경되지 않은 체 사용 가능한 상태로 남아있는다

아래 그림을 통해 이해해 보자.

str에 문자열이 더해질 때 마다 추가한 문자열이 합쳐진 String 객체가 새로 생성하여 str 변수에 해당 객체의 주소 정보로 갱신해 주는 것이다.  다시 설명 하면 처음 “Hello” 문자열을 가진 객체의 내용을 바꾸는 게 아니라 새 String 객체를 만들어 “Hello World” 라는 문자열 저장하고 객체의 주소를 str 변수에 다시 할당해 주는 것이다. (, String 클래스 객체는 Heappermanent area(Garbage Collection 대상 영역)에 생성되며, 한 번 생성된 객체의 내부 내용을 변화시킬 수 없다그리고 기존 객체를 사용하는 곳이 없으면 GC로 회수된다.)


위와 같은 동작 원리 때문에 String Class의 변경은 객체를 생성하기 위한 시간과 메모리를 낭비하게 되고, GC의 발생 빈도를 높아지게 된다.


하지만 Immutable ClassString Class에서 문자열은 오직 생성자에서만 생성이 가능하고 변경이 불가능하기 때문에 변경을 원한다면, 원하는 값을 가진 새로운 객체를 생성해야 한다. 그래서 변경은 적고 읽기만 많은 경우, 또는 여러 쓰레드나 객체에서 공유하는 경우 synchronization(동기화) 와 같은 특별한 안정장치 없이도 안전하게 공유할 수 있게 된다.


이런 이유 때문에 대부분의 문자열이 복잡한 문자열 처리과정보다는 한번 설정된 문자열들을 여러 곳에서 공유하는 경우가 많으므로 자바에서 기본 문자열을 처리하는 클래스로 String 클래스를 immutable 패턴으로 설정했다.


그럼 이제 Mutable ClassStringBuffer/StringBuilder 클래스에 대해 알아보자.


StringBuffer/StringBuilder Classappend(), insert(), delete() 등의 메소드를 통하여 StringBuffer/StringBuilder 객체가 가지고 있는 문자열을 변경 할 수 있으며, Mutable Class이기 때문에 String Class처럼 새로운 객체를 생성하지 않고 기존의 문자열을 변경한다. (String과 마찬가지로 내부적으로 char[]을 사용하지만 이 값은 변경이 가능하다.)


먼저 아래 소스를 보자


StringBuffer sb = new StringBuffer();

        sb.append("Hello");

        sb.append(" World");

        sb.append(" JAVA");


위 코드를 실행하면 StringBuffer/StringBuilder new 연산자가 한번만 실행하게 된다. 다시 풀어 쓰면 한번 생성된 StringBuffer/StringBuilder 객체 크기를 증가시키면서 값을 더한다. 아래 그림의 통해 StringBuffer/StringBuilder Class가 어떻게 동작하는지 이해해 보자.

sb 변수에 처음 할당된 StringBuffer/StringBuilder() 객체의 주소의 변경 없이 기존 객체의 공간이 부족하게 되면 기존 버퍼의 크기를 증가 시키면서 새로운 문자열을 더하고 있다. 위와 같이 객체 생성을 생성하지 않는 원리 때문에 String 클래스 보다 효율적이라고 생각하기 쉽다. 하지만 동기화 처리 때문에 단순 참조일 경우에는 상대적으로 성능이 낮아 진다.


그럼 이제 StringBuffer StringBuilder 차이에 대해 알아보자. 기본적으로 두 클래스가 제공하는 메소드는 동일하지만 차이점은 StringBuffer는 멀티 쓰레드 상태에서 동기화를 지원하지만 StringBuilder는 동기화를 지원하지 않는다는 것이다. StringBuffer에서 제공하는 메소드에는 synchronized 적용되고 있다는 것이다. Class 소스 일부를 보면 아래와 같다.


public final class StringBuffer {

 public synchronized StringBuffer append(String str) {

super.append(str);

return this;

}

[...]

}

public final class StringBuilder {

public StringBuilder append(String str) {

super.append(str);

return this;

}

[...]

}



같은 메소드이지만 StringBuffer Class의 메소드는 synchronized keyword가 있는 걸 확인할 수 있다. 이로 인해 ThreadSafe한 설계가 가능해 진다. 그래서 여러 개의 스레드에서 하나의 StringBuffer 객체를 처리해도 문제가 되지 않는다. 그와는 반대로 StringBuilder Class의 메소드들은 ThreadSafe 하지 않기 때문에 여러 개의 스레드에서 하나의 StringBuilder 객체를 처리하게 문제가 발생하게 된다.


그렇기 때문에 멀티스레드 환경이라면 값 동기화 보장을 위해 StringBuffer를 사용하고, 단일스레드 환경이라면 StringBuilder를 사용하는 것이 좋다. 단일스레드환경에서 StringBuffer를 사용해도 문제는 없지만 동기화 처리 때문에 StringBuilder에 비해 성능이 떨어진다.


JDK 1.5 버전 이전에서는 문자열 연산(+, concat)을 할 때에는 조합된 문자열을 새로운 메모리에 할당하여 참조함으로 인해서 성능상의 이슈가 있었다그러나 JDK 1.5 버전 이후부터는 컴파일 단계에서 String 객체를 사용하더라도 StringBuilder로 컴파일 되도록 변경되었다그래서 JDK 1.5 이후 버전에서는 String 클래스를 활용해도 StringBuilder와 성능상으로 차이가 없어졌다.


단순한 성능만 놓고 본다면 연산이 많은 경우 아래와 같이 된다.


StringBuilder > StringBuffer >>> String


하지만 사용하는 환경에 따라 성능이 차이가 나기 때문에 다양한 고려 사항을 고민해서 선택해서 사용 해한다. 간단하게 정리하면 아래와 같다.


-       멀티 쓰레드 환경에서 하나의 문자열에 대하여 다른 문자나 문자열의 추가가 많이 발생할 경우 StringBuffer 클래스를 사용하는 게 유리하다.

-       멀티 쓰레드 환경에서 하나의 문자열에 대하여 다른 문자나 문자열의 추가가 많이 발생할 경우 StringBuilder 클래스를 사용하는 게 유리하다.

-       String 클래스는 문자열의 + 연산 회수가 많지 않을 경우에 유리하다.

참고 사이트
  • http://javacan.tistory.com/entry/39
  • http://skynaver.tistory.com/entry/String%EA%B3%BC-StringBuffer%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90
  • https://slipp.net/questions/271
  • http://fowler.egloos.com/1243657


'IT > JAVA' 카테고리의 다른 글

GC 튜닝 절차  (0) 2018.11.09
Java Method Signature  (0) 2017.12.08
VO vs DTO  (0) 2017.01.12
Annotaion  (0) 2016.05.09

1. JAVA Annotation이란?

- Java Annotation은 자바5(JEE5)부터 제공되기 시작한 문법 요소로서 @를 이용한 주석이다.

- 자바에서 사용하는 "//, /**/"와  같은 주석과는 다른 의미로, 클래스나, 멤버 변수, 멤버 메서드, 지역 변수, 매개 변수 등 구성요소에 '부착'되는 주석입니다.

@customAnnotation

public class CustomAnnotationSample 

{

...

}


- CustomAnnotationSample이라는 클래스에 @customAnnotation이라는 Annotaion을 사용했다. 단순히 CustomAnnotationSample  Class에 customAnnotation이라는 라벨를 달았다고만 생각하면 된다. Annotaion을 달았다고 어떤 기능이 동작하거나 생기는건 아니다. 제품들에 바코드가 붙었다고 무슨 기능을 하지 않는 것처럼 바코드 리더로 찍어보기 전에는 아무런 의미가 없다. 바코드를 읽는 바코드 리더기와 같이 Annotaion 부착된 구성요소는 자바의 Reflection 을 이용하여 기능을 부여할 수 있다.

2. Annotaion 사용 용도

- 데이터를 문서화하거나, 컴파일 타임이나 런타임시에 원하는 동작을 수행할 수 있도록 하는 데 사용

- 해당 엘리먼트에 대해 생성된 자바 바이트코드를 변경하지 추가 수식자 (modifier)라 할 수 있음

- 어노테이션이 나오기 전까지 메타데이터는 프로퍼티 파일과 XML파일을 활용


3. Annotation의 장점

- 코드의 가독성 증대 : 관련 코드 곁에 메타데이터를 설정할 수 있으므로 코드의 가독성 증대

- 개발 효율성 증대 : 복잡한 XML 스키마를 파악하지 않아도 되며, 개발시 개발 툴과 컴파일러의 도움을 받을 수 있으므로 개발 효율성이 증대

- 별도의 파서를 적용하지 않고도 간단히 런타임 시에 활용할 수 있는 편리함

- JUnit, Framework, Permission Module 에서 사용


4. Annotation의 단점

- Annotaion 처리시 리플렉션을 통해 해당 클래스를 분석해야 하는 오버헤드 발생

- XML 파일을 이용하는 방법도 오버헤드가 있으므로, 경우에 따라서 Annotaion이 더 빠를 수도 있음.

- Annotaion은 모듈이나 어플리케이션 전반적인 메타데이터를 설정할 수 없음 : Annotaion의 범위는 클래스나 패키지 레벨로 한정되기 때문에 여러 클래스에 걸친 공통적인 설정이나 모듈레벨 설정이 어렵움, 웹 어플리케이션 개발시 서블릿 필터나, 인터셉터를 이용해서 문제 해결이 가능함.



5. Annotaion 종류

Annotaion은 메타데이터 저장을 위해 클래스처럼 멤버의 개수에 따라 Marker/Single-Value/Full Annotaion으로 분류

1) Marker Annotaion 

 - 멤버 변수가 없으며, 단순히 표식으로서 사용되는 Annotaion이다. 컴파일러에게 어떤 의미를 전달

 - @AnnotationName

2) Single-Value Annotaion 

 - 멤버로 단일 변수만 갖는 Annotaion. 단일 변수 밖에 없기 때문에 (값)만을 명시하여 데이터를 전달

 - @AnnotationName(elementValue)

3) Full Annotaion 

 - 멤버로 둘 이상의 변수를 갖는 Annotaion으로, 데이터를 (값=쌍)의 형태로 전달

 - @AnnotationName(element=value, element=value, ...)


6. Built-In Annotaion

1) Standard Annotations

JAVA에서 지원하는 Annotaion으로 @Override, @Deprecated, @SuppressWarning등이 있다.

(1) @Override

    - 메소드에 대해서만 사용

- 해당 메소드가 부모 클래스에 있는 메소드를 오버라이드 했다는 것을 명시적으로 선언

- 만일 자식 클래스에 부모 클래스에 해당하는 메소드가 없으면 컴파일러가 인지하여 에러 발생 시킴


(2) @Deprecated

- Marker Annotaion으로 클래스, 메소드, 필드 등에 선언하며 지정한 요소가 더이상 사용되지 않음을 의미

- 컴파일러는 deprecated된 메소드나 클래스 혹은 변수를 사용할 때마다 경고를 발생


(3) @SuppressWarnings

- 클래스, 메소드, 필드의 선언, 컴파일러의 경고를 제거(이 부분에 대해서 경고문을 출력하지 말라는 의미)

- 참고로 다른 Annotation 과 다르게 속성값을 지정해 줄 수도 있다. 

- all, deprecation, unchecked, fallthrough, path, serial, finally 등의 금지 옵션이 있음.

 ex) @SuppressWarnings("deprecation"), @SuppressWarnings("serial")



2) Custom Annotations

 - 개발자가 정의하는 Annotaion으로 class 형태로 만들어짐

 - Annotaion 선언은 @interface 로 한다.

 - 이름 앞에 '@' 문자가 오는 것 외에는 기본적으로 인터페이스를 선언하는 것과 동일(메소드들의 내용은 없고 형태만 선언)

 - default 가 찍히지 않은 메소드는 필수로 입력해야 함

1. Custom Annotations 정의

// 메소드와 클래스가 여전히 작업중임을 나타내기 위해 정의한 Marker Annotaion

public @interface customAnnotation{}

       - Annotaion 정의 파일을 컴파일하고, 이파일을 클래스패스에서 참조할 수 있으면 다른 소스코드상에서 Annotaion을 사용 할 수 있다.

  

2. 멤버 추가하기

  - Annotaion 유형은 멤버 변수를 가질 수 있으며, 이 변수들 컴파일시 또는 런타임에 메타데이터로서 사용될 수 있다.

    이렇게 정의를 하면 자동으로 accessor와 mutator를 제공해준다.

 public @interface customAnnotation{

public int value();

}

- 정의한 Annotaion을 다음과 같이 사용한다.

@customAnnotation(vale = 1)

public void customAnnotationSample(){

// ..

}

- 단일 멤버를 갖을 경우에만 위와 같이 사용할 수 있다.

3. 디폴트 값 설정하기

public @interface customAnnotation{

public int value();

public String desc() default "My Custom Annotation"

}

- 디폴트값을 사용한 예는 다음과 같다.

@customAnnotation(vale = 1)

public void customAnnotationSample(){

// ..

}

3) Meta-Annotations

(1) @Target

 - 어노테이션을 정의 시 어노테이션이 사용 가능한 대상을 지정

 - ElementType의 상수로 정의

- ANNOTATION_TYPE : Annotaion 형

- CONSTRUCTOR : 생성자

- FIELD : enum 상수를 포함한 필드(멤버변수)

- LOCAL_VARIABLE : 지역변수

- METHOD : 메소드

- PACKAGE : 패키지

- PARAMETER : 매개변수

- TYPE : 클래스, 인터페이스(어노테이션 형 포함), 열거형

@Target({ElementType.TYPE,

ElementType.METHOD,

ElementType.CONSTRUCTOR,

ElementType.ANNOTATION_TYPE})

public @interface customAnnotation{

public int value();

public String desc() default "My Custom Annotation";

}

(2) @Retention

 - Annotaion정보의 유지 범위를 설정, JVM이 어떻게 사용자 어노테이션을 다루어야 하는지를 서술

 - RetentionPolicy의 상수로 정의

- SOURCE : Annotaion 정보를 클래스 파일에 저장되지 않고 소스를 처리하는 도구에서만 사용.

- CLASS : Annotaion 정보를 컴파일러는 클래스 파일에 저장하지만 VM에서는 읽지 않고 버려짐.

- RUNTIME : Annotaion 정보를 컴파일러는 클래스 파일에 저장하고, VM에서 저장된 정보를 읽음.

@Retention(RetentionPolicy.RUNTIME)

public @interface customAnnotation{

public int value();

public String desc() default "My Custom Annotation"

}  

(3) @Documented : @Documented 해당 어노테이션을 Javadoc에 포함

(4) @Inherited :서브 클래스가 부모 어노테이션을 상속받도록 함

7. Annotation 접근

 - 실행중인 프로그램에서 Annotation에 접근하는 방법을 알아보자.

 - 인터페이스 java.lang.reflect.AnnotateElement를 통해서 실행중인 Annotation에 접근 할 수 있다.

 - boolean isAnnotationPresent(Class<? extends Annotation> annotationType)

- Annotation 존재 여부를 반환한다.

- Annotation이 존재하면 true, 존재하지 않으면 false 반환

 - <T extends Annotation> T getAnnotation(Class<T> annotationType)

- Annotation을 반환

 - Annotation[] getAnnotations()

- 존재하는 모든 Annotation들을 반환 (private 제외)

 - Annotation[] getDeclaredAnnotations()

- 존재하는 모든 Annotation들을 반환 (private 포함)

앞에서 만든 Annotaion 값 확인해보자

import java.lang.reflect.Method;



@customAnnotation(value=0)

public class CustomAnnotationSample 

{

    @customAnnotation(value=1)

    public static void main(String[] args) {

    CustomAnnotationSample customAnnotationSample = new CustomAnnotationSample();

    customAnnotationSample.checkAnnotations(CustomAnnotationSample.class);

    }

 

    @customAnnotation(value=2)

    public void customAnnotation1() {

    }

    @customAnnotation(value=3, desc="My Second Custom Annotation")

    public void customAnnotation2() {

    }

    @customAnnotation(value=4, desc="My Third Custom Annotation")

    public void customAnnotation3() {

    }

 

    @SuppressWarnings({ "rawtypes", "unchecked" })

public void checkAnnotations(Class customAnnotationSampleClass){

    customAnnotation annotation = null;

    if(customAnnotationSampleClass.isAnnotationPresent(customAnnotation.class))

    {    

    annotation = (customAnnotation) customAnnotationSampleClass.getAnnotation(customAnnotation.class);

    int value = annotation.value();

            String desc = annotation.desc();

            System.out.println(customAnnotationSampleClass.getSimpleName() + " Class : value=" + value + " desc=" + desc);

    }

        Method[] methods = customAnnotationSampleClass.getDeclaredMethods();

        for ( Method tempMethod:methods ){

            annotation = tempMethod.getAnnotation(customAnnotation.class);

            if ( annotation != null ){

                int value = annotation.value();

                String desc = annotation.desc();

                System.out.println("This Method "+ tempMethod.getName() + "() : value=" + value + " desc=" + desc);

            } else {

                System.out.println("This Method "+ tempMethod.getName() + "() : Annotation is not exist");

            }

        }

    }

}


결과

CustomAnnotationSample Class : value=0 desc=My Custom Annotation

This Method main() : value=1 desc=My Custom Annotation

This Method customAnnotation1() : value=2 desc=My Custom Annotation

This Method customAnnotation2() : value=3 desc=My Second Custom Annotation

This Method customAnnotation3() : value=4 desc=My Third Custom Annotation

This Method checkAnnotations() : annotation is not exist



8. Annotation Processing Tool

 - Annotation의 소스를 체크하고 컴파일을 처리해줌

 - JDK 5에는 어노테이션을 읽고 이해하는 API가 표준화되어 있지 않아 JDK에 들어있는 썬 고유의 라이브러리를 사용했고, 자바 SE 6에 기본으로 포함되었다.

 - JDK 5에는 APT(Annotation Processing Tool)라는 패키지가 포함되어, JSR 269(JSR 269 Pluggable Annotation Processing API)가 나오기 전까지 임시로 그 역할을 대신했다.

 - Apache Ant에서는 APT에 대응하는 앤트 타스크를 따로 두어 지원하였다. http://ant.apache.org/manual/CoreTasks/apt.html에서 확인할 수 있다

 

참고 사이트

http://wiki.gurubee.net/display/DEVSTUDY/Annotation

http://hiddenviewer.tistory.com/88

http://gubok.tistory.com/167

http://secretroute.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%9D%98%E7%A5%9E-Vol1-Annotation

http://ellehu.com/java/3727

http://piece.tistory.com/7

http://www.nextree.co.kr/p5864/

'IT > JAVA' 카테고리의 다른 글

GC 튜닝 절차  (0) 2018.11.09
Java Method Signature  (0) 2017.12.08
VO vs DTO  (0) 2017.01.12
String VS SringBuffer VS StringBuilder  (0) 2016.10.14

+ Recent posts