Juni_Dev_log

(node.js) [Part.8] 뷰 템플릿 적용하기 - pug 뷰 템플릿 사용하기 본문

Theorem (정리)/node.js

(node.js) [Part.8] 뷰 템플릿 적용하기 - pug 뷰 템플릿 사용하기

Juni_K 2021. 1. 31. 20:46

pug 포맷은 웹 문서의 태그를 그대로 사용하지 않고 최대한 간단한 형태로 입력하기 때문에 공백과 들여쓰기를 기준으로 태그의 구조가 결정된다.

따라서 pug 포맷을 사용하면 HTML 태그를 사용하는 것보다 훨씬 적은 내용을 입력해도 웹 문서를 만들 수 있다.

pug를 사용할 때는 ejs와 마찬가지로 뷰 템플릿 파일을 먼저 만들고 pug 뷰 엔진에서 응답 웹 문서를 만들 때 사용한다.

 

⚙️ 설치

%npm install pug -g

를 통해서, pug 를 먼저 설치후 진행한다.

 

먼저 pug 로 템플릿 문서를 만들어보자.

[ViewExample2] 를만들고 복사해서 app.js 에서의 뷰 엔진을 pug로 변경한다.

# app.js

1
2
3
4
5
6
7
8
9
...
//===== 뷰 엔진 설정 =====//
app.set('views', __dirname + '/views');
app.set('view engine''pug');
// (★)pug 모듈 관련 오류 발생으로, 경로를 지정하는 코드 삽입
app.engine('pug', require('pug').__express)
 
console.log('뷰 엔진이 pug로 설정되었습니다.');
...
cs

- app 객체의 set() 메소드로 호출할 떄 속성 이름을 view engine 으로 전달하는 부분을 찾아 pug로 바꿔준다.

 

📌 pug 로 HTML 문서 만들기

이제 pug로 HTML 문서를 어떻게 만드는지 간단한 예제를 만들어보자.

[views] 폴더에 test1_success.pug 를 만든다.

 

# test1_success.pug

 
1
2
3
4
5
6
7
8
9
doctype html
 
html
    head
        title "성공"
    body
        block content
            #container
                p "조회에 성공했습니다."
cs

 

pug는 공백을 두 개 사용하여 들여쓰기를 한다. (Tab 을 사용해서 들여쓰기를 할 수도 있다.)

(공백과 Tab 을 같이 사용하면 오류가 발생할 수 있다.)

 

 

이제 [routes] 폴더 안에 test.js 모듈을 만들고 test1 함수를 만든다. 이 함수는 클라이언트에서 테스트를 위해 보내온 요청을 처리한다.

# test.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var test1 = function(req,res){
    console.log('test 모듈 안에 있는 test1 호출됨');
    
    res.writeHead('200',{'Content-Type':'text/html; charset=utf8'});
    
    // 뷰 템플릿을 이용하여 렌더링한 후 전송
    var context = {};
    req.app.render('test1_success',context,function(err,html){
        console.log('rendered : ' + html);
        res.end(html);
    });
};
 
module.exports.test1 = test1;
cs

- test1_success.png 파일을 읽고, context 객체의 속성들을 적용하여 웹 문서를 만든다.

- 지금 읽어들인 뷰 템플릿 파일에는 변수를 사용하지 않기 때문에, context 에 아무런 속성도 넣지 않은 채 파라미터로 전달해도 된다.

- module.exports 에 test1 이라는 속성을 추가하고 test1 함수를 할당했으므로 이제 이 모듈을 불러들이는 쪽에서 test1 함수를 사용할 수 있다.

 

config.js 파일에 새로 추가한 라우팅 정보를 추가한다.

#config.js

1
2
3
4
5
6
...
    route_info: [
        ...
        ,{file:'./test', path:'/process/test1', method:'test1', type:'post'}
    ]
}
cs

- 라우팅 정보는 route_info 속성에 배열로 추가되었다.

- 따라서 그 속성 값의 마지막에 새로운 한 줄을 추가한다. ./test로 하고 사용자가 요청할 패스는 /process/test1으로 한다. test.js 모듈 파일을 불러왔을 때 쓸 수 있는 함수의 이름은 test1이므로 method 속성의 값을 test1으로 한다.

그리고 클라이언트가 요청할 때 사용할 방식은 post로 등록한다.

 

마지막으로 [public] 폴더 안에 test1.html 파일로 만든다.

#test1.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
    <head>
        <meta charset="utf-8">
        <title>pug 테스트</title>
    </head>
    <body>
        <h3>Pug 테스트 1</h3>
        <p>아래 [조회] 버튼을 누르세요.</p>
        <br>
        <form id="form1" method="post" action="/process/test1">
            <input type="submit" value="조회" name="">
        </form>
        <br>
    </body>
</html>
cs

app.js 파일을 실행하고 test1.html 을 열어본다.

 

정상적으로 온 응답을 확인할 수 있다. 그러면 페이지 소스 보기 메뉴를 통해서 웹 문서의 소스를 확인해본다.

응답 받은 웹 문서의 소스

 

이처럼 pug 템플릿에 넣어둔 태그 이름이 HTML 문서의 태그로 어떻게 변환되는지 알 수 있다.

 

pug 템플릿과 응답으로 받은 웹 문서의 코드 비교

 

📌 pug 템플릿으로 로그인 웹 문서 만들기

이제 로그인에 대한 응답을 보낼 때도 pug 템플릿을 사용할 수 있도록 코드를 수정해보자.

먼저, 로그인 화면에 대한 응답을 pug 템플릿으로 바꿔보자.

#login_success.pug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
doctype html
html
  head
    meta(charset='utf8')
    title 로그인 성공 페이지
  body
    h1 로그인 성공
    div
      p 사용자 아이디 : #{userid}
    div
      p 사용자 이름 : #{username}
    br
    br
    a(href='/public/login.html') 다시 로그인하기
cs

- 변수를 넣고 싶다면 # 기호 뒤에 중괄호{} 를 붙이고 그 안에 변수 이름을 입력한다.

- 명령 프롬프트에서 pug 명령으로 login_success.pug 파일을 변환하여 확인해보자.

 

%pug login_success.pug --pretty

 

이제 이 템플릿 파일을 사용해 클라이언트에 응답을 보내도록 user.js 파일의 코드를 수정한다.

뷰 엔진이 pug로 설정되어있으므로 실제 실행될 때는 pug 템플릿을 읽어 들인다.

#user.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
...
// 조회된 레코드가 있으면 성공 응답 전송
            if (docs) {
                console.dir(docs);
 
                // 조회 결과에서 사용자 이름 확인
                var username = docs[0].name;
                
                // 뷰 템플릿을 사용하여 렌더링 후 전송
                var context = {userid:paramId, username:username};
                req.app.render('login_success', context, function(err, html){
                    if(err){
                        console.error('뷰 렌더링 중 오류 발생 : ' + err.stack);
                        
                        res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
                        res.write('<h2>뷰 렌더링 중 오류 발생</h2>');
                        res.write('<p>'+ err.stack +'</p>');
                        res.end();
                        
                        return;
                    }
                    console.log('rendered : ' + html);
                    
                    res.end(html);
                });
            
            } 
...
cs

위의 코드는 앞에서 사용한 것과 같지만, render() 메소드는 login_success.pug 파일을 변환하여 결과 HTML 파일을 만들어 준다는 것을 이해해야한다.

 

pug 템플릿으로 뷰 엔진이 결과 웹 문서를 만들어 내는 과정을 정리하면 다음과 같다.

 

pug 템플릿 파일로 결과 웹 문서를 만드는 과정

 

 

pug 뷰 엔진은 pug 템플릿 파일을 로딩한 후 context 객체에 들어 있는 속성들로 결과 웹 문서를 만들어낸다.

이제 사용자가 요청했을 때, 정상적으로 동작하는지 확인해보자.

 

pug 엔진이 만들어 낸 로그인 성공 페이지

 

📌 pug 템플릿으로 사용자 리스트 웹 문서 만들기

이제 사용자 리스트를 보여줄 웹 문서를 pug 템플릿으로 만들어보자.

listuser.pug 파일을 만들고 다음과 같이 입력한다.

# listuser.pug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
doctype html
html
  head
    meta(charset='utf8')
    title 사용자 리스트 페이지
  body
    hi 사용자 리스트
    div
      ui
        - for (var i=0; i < results.length; i++){
        - var curId = results[i]._doc.id;
        - var curName = results[i]._doc.name;
        li #{i} - 아이디 : #{curId}, 이름 : #{curName}
        - }
    br
    br
    a(href='/public/listuser.html') 다시 요청하기
cs

pug 안에서 자바스크립트 코드를 사용하려면 코드 줄의 가장 앞에 - 기호를 붙여줘야한다.

- 기호가 붙은 부분은 웹 문서가 아닌 자바스크립트 코드로 인식되므로 이 코드의 실행 결과와 다른 내용이 합쳐져서 웹 문서가 만들어진다.

 

변수를 사용하기 위해서는 #{} 를 사용하면 된다.

 

이제 뷰 템플릿을 이용해서 응답을 보내도록 user.js 파일을 다음과 같이 수정한다.

#user.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
...
if (results) {
                console.dir(results);
 
                res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
                
                // 뷰 템플릿을 이용하여 렌더링한 후 전송
                var context = {results:results};
                req.app.render('listuser',context,function(err,html){
                    if(err){
                        throw err;
                    }
                    res.end(html);
                });
            }
...
cs

이 부분도 ejs 와 같기 때문에, 똑같이 진행한다.

 

app.js 를 실행하고 파일을 열고 전송 버튼을 누르면 응답 페이지가 나온다.

localhost:3000/public/listuser.html

pug 뷰 엔진이 만들어 낸 사용자 리스트 응답 페이지

정상적으로 나오는 것을 확인할 수 있다.

📌 pug 템플릿으로 사용자 추가 웹 문서 만들기

마지막으로 adduser 함수를 호출할 때 사용자가 볼 수 있는 응답 페이지를 pug 템플릿으로 만들어보자.

이번에는 대부분의 응답 문서에서 공통으로 사용하는 부분을 layout.pug 라는 별도 파일로 만든 후 adduser.pug 파일에서 상속하도록 만드는 것이다.

'상속한다' 라는 것은 layout.pug 파일의 내용을 바탕으로 adduser.pug 파일에서 일부 수정한다는 뜻이다.

#layout.pug

1
2
3
4
5
6
7
8
9
doctype html
html
  head
    meta(charset='utf8')
    title extends 로 상속함
    script(src='/public/jquery-3.1.1.min.js')
  body
    block content
    include ./footer.pug
cs

- pug 템플릿 안에서 <script>태그를 추가하려면 src 속성을 이용하면 된다.

- body 하위 태그에 block 코드를 넣는다.

: block 코드 부분은 이 파일을 상속받는 파일에서 이 부분을 대체할 수 있다는 것을 알려준다.

- include 키워드는 다른 pug 템플릿 파일을 읽어와서 코드를 붙여준다.

 

footer.pug 를 만들어보자

#footer.pug

1
2
div#footer
  a(href='/public/login.html')로그인으로- pug 에서 include 됨
cs

- id 속성으로 footer를 넣는다.

- <a>태그를 사용해서 /public/login.html 페이지로 이동한다.

 

#adduser.pug

1
2
3
4
extends layout
 
block content
  h2 #{title}
cs

 

- 다른 pug 파일을 상속하여 새로운 pug 파일을 만들 때는 extends 키워드를 사용한다.

- 다른 파일의 내용을 상속받으면, block으로 지정했던 부분을 대체할 수 있다.

- layout.pug 파일에서 이름이 content인 block을 지정했으므로, 여기에서도 block content코드를 입력한 후 그 아래에 대체할 코드를 입력한다.

- h2 태그와 함께 title 변수를 출력한다.

 

이를 정리하면 다음과 같다.

 

pug 에서 extends 와 include 사용하기

 

adduser.pug 파일에서 layout.pug 파일의 내용을 그대로 사용하려면, extends 키워드를 사용해 상속한다.

이렇게 상속 파일 안에서 특정 영역에 있는 코드를 바꾸려면 block 키워드를 찾아 새로운 코드를 넣어준다.

 

또한, include 키워드를 사용하여 다른 pug 파일을 지정하면, 그 파일의 내용을 읽어온 후 추가한다.

 

마지막으로 adduser.pug 뷰 템플릿을 사용하도록 user.js 파일을 다음과 같이 수정한다.

#user.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
...
// 결과 객체 있으면 성공 응답 전송
            if (addedUser) {
                console.dir(addedUser);
                
                res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
                
                // 뷰 템플릿을 이용하여 렌더링한 후 전송
                var context = {title:'사용자 추가 성공'};
                req.app.render('adduser',context,function(err,html){
                if(err){
                    console.error('뷰 렌더링 중 오류 발생 : ' + err.stack);
                    
                    res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
                    res.write('<h2>뷰 렌더링 중 오류 발생</h2>');
                    res.write('<p>'+ err.stack +'</p>');
                    res.end();
                    
                    return;
                }
                console.log("rendered : " + html);
                
                res.end(html);
            })
...
cs

 

이제 app.js 파일을 실행하고 adduser.html 파일은 연후 사용자를 추가해보자.

 

pug 뷰 엔진이 만들어 낸 사용자 추가 응답 페이지

 

🐞🔨 Error 발생 (해결)

pug 파일을 실행하던 도중, error 로 2개가 발생했다.

① Error: C:\Users\kks13\OneDrive\바탕 화면\Dev\Study\nodejs\nodejs_study\ViewExample2\views\footer.pug:2:31 1| div#footer > 2| a(href='/public/login.html')로그인으로- pug 에서 include 됨 -------------------------------------^ unexpected text "로그인으로" at makeError

-> 이거는 footer.pug에서 띄어쓰기를 하지 않아서 발생한 오류 (pug파일은 띄어쓰기가 굉장히 중요하다.)

② Error: the "basedir" option is required to use includes and extends with "absolute" paths at C:\Users\kks13\OneDrive\바탕 화면\Dev\Study\nodejs\nodejs_study\ViewExample2\views\layout.pug line 9 at Function.

->  이러한 오류는 include를 할 때, 경로를 잘못 설정해서 발생한 문제였다. 경로설정을 수정하여 해결하였다.

 

이렇게 ejs 와 pug 뷰 템플릿을 이용하면 클라이언트에게 응답 보낼 웹 문서를 손쉽게 관리할 수 있고, 훨씬 간단하게 수정할 수 있다.

이제 컨트롤러에 해당하는 라우팅 함수를 만들수 있고, 데이터베이스를 다루는 모델, 그리고 결과 웹 문서를 만들어내는 뷰 엔진까지 다룰 수 있게 되었다.

 

이 세 가지 반복학습을 진행하면, 충분히 웹 서버에 기능을 추가하는 것이 쉬워질 것이다.

 

다음 장에서는 인증 기능을 추가하는 방법이나 웹 서버 이외의 다양한 서버를 만드는 방법에 대해 본격적으로 알아보도록 한다.

 


📚 참고

pugjs.org/language/includes.html

 

Includes – Pug

Includes Includes allow you to insert the contents of one Pug file into another. //- index.pug doctype html html include includes/head.pug body h1 My Site p Welcome to my super lame site. include includes/foot.pug //- includes/head.pug head title My Site s

pugjs.org

kururu.tistory.com/29

 

Nodejs기초 - 12일차 정리(pug템플릿 사용 및 상속, 패스포트 정의)

1. pug 뷰 템플릿 사용  - pug 포맷은 웹 문서의 태그를 그대로 사용하지 않고 최대한 간단한 형태로 입력하기 때문에 공백과 들여쓰기를 기준으로 태그의 구조가 결정  - pug의 옛이름은 jade  - ht

kururu.tistory.com

devkingdom.tistory.com/88

 

[Debugging] node.js 에서 Cannot set headers after they are sent to the client 에러 발생할 때

하이~ 웹서버 데몬을 올리고 post 로 url을 전송할 때 이런 에러가 발생했다.. 이 에러메세지는 기본적으로 응답값 리다이렉트 처리가 잘못 되었을 때 발생하는데, 대게는 중복처리를 했을 경우 발

devkingdom.tistory.com

slidesplayer.org/slide/15437307/

 

뷰 템플릿 적용하기 8장 Do it! Node.js 프로그래밍 이지스퍼블리싱 제공 강의 교안 2017/03 - ppt downloa

응답 웹문서를 템플레이트로 미리 만들어둘 수 있을까? 강의 주제 및 목차 강의 주제 응답 웹문서를 템플레이트로 미리 만들어둘 수 있을까? 목 차 1 ejs 뷰 템플릿 사용하기 2 pug 뷰 템플릿 사용

slidesplayer.org

=> 사진 자료

Comments