Notice
Recent Posts
Recent Comments
05-04 06:25
«   2025/05   »
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
Archives
Today
Total
관리 메뉴

YESHTML5

nodejs template "PUG" 에 대하여 본문

Back-end

nodejs template "PUG" 에 대하여

슬/도/아/밤/ 2018. 11. 9. 15:00
반응형



NodeJS의 템플릿 "PUG"에 관해서 이야기해볼까한다.


[참고화면]

[URL]
https://www.npmjs.com/package/pug

[공식문서]
https://pugjs.org/api/getting-started.html


nodejs를 사용하다보면 템플릿에 대해서 고민이 한번쯤 들것이다. 

static하게 html을 읽을수도 있고, 파일형태로 로드하고 직접 DOM을 표현할수도 있을것이다. 

하지만 사용하다보면 php에서 사용하는 include 개념도 쓰고싶고, 템플릿의 장점을 잘 사용했으면 하는것들이, 많이 들때가 있다. 

몇가지 찾아보고 테스트를 해봤다. 


먼저 PHP와 유사한(?) 문법을 가진 EJS를 사용해봤다. 


아무래도  <?php echo "TEST"; ?> 형태로 익숙하다보니 사용을 했으나 사용하면서, 점점 한계를 느꼈다.   ( 어쩌면 내공이 부족하거나, 나와 맞지않을수도... )

그다음 찾아본것이 pug다.  원래이름은 JADE인데  소송 이름도용(?) 관련으로 지금은 PUG 로 사용하고있다.  

아직은 사용중이라, 특징을 잡는다면, 기본템플릿을 정하고  그 템플릿을 계속해서 쓰고, 일부분만 바꿀수있다. 

일반 웹페이지를 볼때, header , contents , footer 에서 정하고 각각 일부분만 override혹은 교체 형태로 사용할수있다.  다음 코드를 보면


layout.pug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
doctype html
html(lang="en")
    head
        title yeshtml5.com
        link(rel='shortcut icon',href='/images/common/favicon.ico',type='image/x-icon')
        link(rel='shortcut icon',href='/images/common/favicon.ico',type='image/png')
        link(rel='stylesheet', href='/css/common.css', type='text/css' )
        link(rel='stylesheet', href='/css/layout.css', type='text/css' )
        link(rel='stylesheet', href='https://fonts.googleapis.com/css?family=Open+Sans:300,400', type='text/css' )
        script(src="/js/jquery.js" , type="text/javascript")
        script(src="/js/vue.js" , type="text/javascript")
        script(src="/js/axios.js" , type="text/javascript")
        block scripts
    body
        header
            include inc.navi.html
        block header
        main
            block content
        footer
            block footer
cs

2단계 아래 폴더구조 아래인 test.pug

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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
//- interface
extends ../../layout
//- scripts
block append scripts
    script(src="https://unpkg.com/masonry-layout@4/dist/masonry.pkgd.min.js")
    script(src="https://unpkg.com/imagesloaded@4/imagesloaded.pkgd.min.js")
    style(type='text/css').
        /*=== unsplash =========================================*/
        body {background:#ddd;}
        .info {position:relative;margin-bottom:20px;padding:20px;font-weight:100; box-sizing:border-box;}
        .info div.page {text-align:center;}
        .info div.page p.navi em {display:inline-block;margin-right:10px;}
        .info div.page .search input[type="input"] {display:inline-block;width:200px;padding:5px 15px; background:#111; color:#fff;font-size:16px;border:0;}
        .info div.page .search button {display:inline-block;margin-left:10px;padding:5px 20px; border:0; background:#111;color:#fff;font-size:16px;}
        .info div.page .navi {display:block;padding:10px 0; color:#000; font-size:20px;}
        .info div.arrow {padding:50px auto;}
        .info div.arrow button {position:absolute;top:50%;transform:translateY(-50%);font-size:30px;font-weight:100;}
        .info div.arrow button.prev {left:50px;}
        .info div.arrow button.next {right:50px;}
        .unsplash {position:relative;display:block;width:100%;margin:0 auto;text-align:center;box-sizing:border-box;}
        .unsplash .tag {text-align:left;}
        .unsplash .tag a {display:inline-block;padding:2px 4px;font-size:14px;}
        .unsplash .tag a:hover {color:#ff0000;text-decoration:underline;}
        .unsplash dl {position:relative;display:inline-block;width:360px;min-height:300px;margin:10px;padding:10px; box-sizing:border-box;}
        .unsplash dl dt {margin-bottom:20px;}
        .unsplash dl dt .user {position:relative;margin-bottom:10px;padding-bottom:10px;border-bottom:1px solid #d7d7d7;text-align:left;}
        .unsplash dl dt .user > * {display:inline-block;vertical-align:middle;}
        .unsplash dl dt .user .profile {width:50px;height:50px;border-radius:50%;overflow:hidden;}
        .unsplash dl dt .user .name { margin-left:20px; }
        .unsplash dl dt .user .updated { position:absolute;bottom:0;right:0;transform:translateY(-50%); font-size:10px;}
        .unsplash dl dd {overflow:hidden;}
        .unsplash dl dd img {max-width:100%;max-height:100%;vertical-align:top;transition:all 0.3s ease-out;}
        .unsplash dl dd a:hover img {-webkit-filter:contrast(130%); filter:contrast(130%);transform:scale(1.2, 1.2)}
        .unsplash dl dd.download a {display:block;position:absolute;bottom:15px;right:15px;padding:5px; background:#2b669a; color:#fff; font-size:10px;}
        /*==================================================
            Media Query style
        ==================================================*/
        @media only screen and (max-width:1000px) {
            .unsplash dl {width:96%; margin:10px auto; }
        }
//- content
block content
    div#index
        div.info.panel
            div.page
                p.search
                    input#searchIpt(v-on:keyup.enter="query",v-bind:placeholder="search",v-bind:value="search" type="input" )
                    button(v-on:click="query") 검색
                p.navi
                    em {{page}}
                    | / {{total}}
                div.arrow
                    button(v-on:click="prev" class="prev") < PREV
                    button(v-on:click="next" class="next") > NEXT
        div.unsplash
            dl.panel( v-for="item in items")
                dt
                    div.user
                        span.profile
                            a(v-bind:href='item.user.portfolio_url' , target="_blank")
                                img(v-bind:src='item.user.profile_image.large',class="flex")
                        span.name {{item.user.username}}
                        span.updated {{item.user.updated_at}}
                    p.tag
                        a(v-on:click="tagLink",v-for="tag in item.tags") {{tag.title}}
                dd
                    a(v-bind:href='item.links.html',target="_blank")
                        img(v-bind:src='item.urls.regular')
                dd.download
                    a.panel(v-bind:href='item.links.download',target="_blank") DOWNLOAD
    <!--[script]----->
    script(type='text/javascript').
        var index = new Vue({
            el: '#index',
            data: {
                page: 1,
                total: 10,
                per_page: 30,
                search: 'wallpaper',
                items: {}
            },
            created: function () {
            },
            updated: function () {
                if (document.querySelector('.unsplash')) {//imagesLoaded complete -> Masonry
                    imagesLoaded(document.querySelector('.unsplash'), function (instance) {
                        var grid = document.querySelector('.unsplash');
                        var msnry = new Masonry(grid, {
                            fitWidth: true,
                            itemSelector: 'dl.panel'
                        });
                    });
                }
            },
            methods{
                init: function () {
                    index.$data.items = [];
                },
                prev: function () {
                    index.$data.page = index.$data.page - 1;
                    this.ajax();
                },
                next: function () {
                    index.$data.page = index.$data.page + 1;
                    this.ajax();
                },
                query: function (event) {
                    index.$data.search = document.querySelector('#searchIpt').value;
                    this.ajax();
                },
                tagLink: function (event) {
                    index.$data.page = 1;
                    index.$data.search = event.target.innerHTML;
                    this.ajax();
                },
                ajax: function (event) {
                    this.init();
                    axios.post('/component/unsplash/' + index.$data.search, {
                        page: index.$data.page,
                        per_page: index.$data.per_page
                    }).then(function (response) {
                        //pagination
                        index.$data.total = Math.ceil(response.data.total / index.$data.per_page);
                        index.$data.items = response.data.results;
                    }).catch(function (error) {
                        console.log(error);
                    });
                }
            }
        });
        /*--[start]--*/
        index.ajax();
cs

 

js에서 설정예.

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
/*
 */
 
const express = require('express')
const router = express.Router()
const path = require('path')
const bodyParser = require('body-parser').json();
const request = require('request')
 
/**
 * @router : unsplash
 * https://unsplash.com 의 API 가져와서 갤러리, 시간당 200query가 막히면 다른 API 키발급필요
 * @document : https://unsplash.com/documentation
 */
 
router
    .get('/', (req, res) => {
        res.render(path.normalize(__dirname + "/unsplash.pug"))
    })
    .post('/:search/', bodyParser, (req, res) => {
        const api = "----------"
        const url = 'https://api.unsplash.com/search/photos'
        request({
            method: "GET",
            qs: {
                client_id: api,
                page: req.body.page || 1,
                per_page: req.body.per_page || 30,
                query: req.params.search
            },
            url: url,
            json: true
        }, function (err, response, body) {
            res.json(body)
        })
    })
 
/*---[exports]-----*/
module.exports = router
cs


이렇게 사용을 해보니, 장점은 다음과 같다. 

1. include개념처럼 붙여쓸수있다. 

2. 기본템플릿을 정해서 상속해서 재사용가능하다. 

3. 공통적인 요소는 다양하게 include 할수있고,  override , 혹은 붙여서 쓸수있다.

4. 변수처리및 서버의 정보를 가져와서 length값만큼 for문을 돌릴수있다. 

결론은 한번써보자. 그리고 안맞으면 다른거 찾아보자. 

본소스는 RESTFUL  API 형태로 unsplash를 만들어 본것이다. 

GITHUB에서

https://github.com/yeshtml5/node/tree/master/app/template/component/unsplash  에서 찾아보고 다운로드 할수있다.






반응형
Comments