pug
Node.js에서 사용되는 템플릿 엔진
Pug는 HTML과 JavaScript를 조합한 언어로, HTML의 장점인 가독성과 유지보수성을 유지하면서 JavaScript의 동적 기능을 사용할 수 있습니다.
확장자 : *.pug
주요 문법
구분 | 문법 | 설명 |
변수 삽입 | {{ variable }} | 변수의 값을 삽입합니다.
#{ } 보다 성능이 좋다.
- HTML 을 HTML 요소로 변환하여 출력 |
변수 삽입 | #{ } | 변수의 값을 삽입합니다.
- HTML 을 HTML 그대로 출력 |
조건문 | if (condition) { ... } | 조건에 따라 HTML을 생성합니다. |
반복문 | each (item in array) { ... } | 배열의 요소를 순회하며 HTML을 생성합니다. |
함수 호출 | {{ function(arguments) }} | 함수를 호출하여 HTML을 생성합니다. |
서버에서 뷰로 변수 전달방법
1.
res.render()에 직접 변수 전달
2.
app.locals 에 전역 변수 설정
3.
res.locals에 로컬 변수 설정
4.
res.locals에 객체 전체를 설정
res.render()에 직접 변수 전달
•
js
app.get('/example', (req, res) => {
res.render('example', { title: 'Express Example', message: 'Hello World' });
});
JavaScript
복사
•
pug
// example.pug
h1= title
p= message
JavaScript
복사
app.locals 에 전역 변수 설정
•
js
app.locals.title = 'Express Example';
JavaScript
복사
•
pug
// example.pug
h1= title
p= message
JavaScript
복사
res.locals 에 변수 설정
•
js
app.get('/example', (req, res) => {
res.locals.title = 'Express Example';
res.render('example', { message: 'Hello World' });
});
JavaScript
복사
•
pug
// example.pug
h1= title
p= message
JavaScript
복사
res.locals 에 객체 설정
•
js
app.get('/example', (req, res) => {
res.locals.data = { title: 'Express Example', message: 'Hello World' };
res.render('example');
});
JavaScript
복사
•
pug
// example.pug
h1= data.title
p= data.message
JavaScript
복사
주요 예시 코드
Pug에서는 - 기호를 사용하여 JavaScript 코드 블록을 삽입할 수 있습니다. 따라서 - var isAdmin = true와 같은 코드는 Pug에서 유효한 문법입니다. 이 코드를 사용하면 Pug 템플릿에서 변수를 정의하고 초기화할 수 있습니다.
변수 삽입
// 변수 삽입 예시
- var username = 'John Doe'
h1 Welcome, {{username}}!
JavaScript
복사
조건문
•
if
// 조건문 예시
- var isAdmin = true
- var isUser = true
if isAdmin
p You are an admin.
else if isUser
p You are a User.
else
p You are not an admin.
JavaScript
복사
•
case
- var score = 'C'
case score
when 'A'
p 'A' 학점 입니다
when 'B'
p 'B' 학점 입니다
when 'C'
p 'C' 학점 입니다
when 'D'
p 'D' 학점 입니다
default
P '학고' 입니다
HTML
복사
반복문
// 반복문 예시
- var fruits = ['Apple', 'Banana', 'Orange']
ul
each fruit in fruits
li= fruit
JavaScript
복사
함수 호출
// 함수 호출 예시
- function capitalize(str) {
return str.toUpperCase();
}
p= capitalize('hello, world!')
p {{ capitalize('hello, world!') }}
JavaScript
복사
게시판 예시코드
1.
라우터 파일 생성 및 설정
2.
라우터 정의
3.
pug 레이아웃 파일 작성
4.
style.css 연결
5.
index.pug 작성
6.
header, footer 추가
7.
로그인, 회원가입 화면 작성
8.
게시판 화면 작성
라우터 파일 생성 및 설정
1.
routes 폴더 생성
2.
index.js, board.js 파일 생성
3.
app.js 에 import 및 미들웨어 설정
routes 폴더 생성
index.js, board.js 파일 생성
app.js 에 import 및 미들웨어 설정
// 👩💻 라우터 모듈 import
const indexRouter = require('./routes/index');
const boardRouter = require('./routes/board');
JavaScript
복사
// 👩💻 라우터 설정
app.use('/', indexRouter);
app.use('/board', boardRouter);
JavaScript
복사
라우터 정의
1.
index.js
2.
board.js
index.js
const express = require('express');
const router = express.Router();
// GET / 라우터
router.get('/', (req, res) => {
res.render('index', { title: 'Main' });
});
// 로그인
router.get('/login', (req, res) => {
res.render('login', { title: 'Login' });
});
// 회원가입
router.get('/join', (req, res) => {
res.render('join', { title: 'Join' });
});
module.exports = router;
JavaScript
복사
board.js
const express = require('express')
const router = express.Router()
let boardList = [
{ title : '제목1', writer : '작성자1', content : '내용1' },
{ title : '제목2', writer : '작성자2', content : '내용2' },
{ title : '제목3', writer : '작성자3', content : '내용3' },
]
// 👩💻 게시글 목록
router.get('/', (req, res) => {
res.render('board/list', {boardList} )
})
// 👩💻 게시글 등록
router.get('/insert', (req, res) => {
res.render('board/insert')
})
// 👩💻 게시글 등록
router.post('/', (req, res) => {
// 구조분해할당
const { title, writer, content } = req.body;
// const title = req.body.title
// const writer = req.body.writer
// const content = req.body.content
const newBoard = { title, writer, content };
boardList.push(newBoard);
res.redirect('/board');
});
// 👩💻 게시글 수정 페이지
router.get('/update/:id', (req, res) => {
console.log(`id : ${req.params.id}`);
let id = req.params.id
const board = boardList[id];
res.render('board/update', { board, id });
});
// 👩💻 게시글 수정
router.post('/update', (req, res) => {
const { id, title, writer, content } = req.body;
boardList[id] = { title, writer, content };
res.redirect(`/board/${id}`);
});
// 👩💻 게시글 삭제
router.post('/delete', (req, res) => {
const id = req.params.id;
boardList.splice(id, 1);
res.redirect('/board');
});
// 👩💻 게시글 읽기
// 요청 경로에 파라미터 매핑 방법 ➡ '/:파라미터명'
router.get('/:id', (req, res) => {
console.log(`id : ${req.params.id}`);
let id = req.params.id
let board = boardList[id]
res.render('board/read', {board, id})
})
module.exports = router;
JavaScript
복사
pug 레이아웃 파일 작성
1.
layout.pug
doctype html
html
head
title= title
//- 여기에 CSS 연결
body
block content
JavaScript
복사
style.css 연결
1.
public/css 폴더 생성
2.
style.css 파일 생성
3.
스타일 작성
public/css 폴더 생성
style.css 파일 생성
스타일 작성
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
ul {
list-style-type: none;
}
a {
text-decoration: none;
}
body {
font-family: Arial, sans-serif;
}
/* header */
header {
display: flex;
justify-content: space-between;
align-items: center;
background-color: #333;
color: #fff;
padding: 0 20px;
text-align: center;
height: 80px;
}
header h1 {
margin: 0;
}
header nav {
height: 100%;
}
header nav ul {
height: 100%;
}
header nav li {
display: inline-block;
height: 100%;
}
header nav a {
display: flex;
justify-content: center;
align-items: center;
color: #fff;
text-decoration: none;
min-width: 100px;
height: 100%;
}
header nav a:hover {
background-color: #fff;
color: #333;
}
/* footer */
footer {
background-color: #333;
color: #fff;
padding: 10px;
text-align: center;
}
.container {
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 300px;
margin: 100px auto;
}
.container-md {
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 480px;
margin: 100px auto;
}
.container-lg {
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 1024px;
margin: 100px auto;
}
.container h2 {
text-align: center;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}
.form-group input,
.form-group .btn {
display: inline-block;
font-size: 14px;
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.form-group .btn {
text-align: center;
background-color: #4caf50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
margin-bottom: 10px;
}
.form-group .btn:hover {
background-color: #45a049;
}
/* table */
.table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.table th, .table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.table th {
background-color: #f2f2f2;
}
.table tr:hover {
background-color: #f5f5f5;
}
/* button */
.btn-box {
display: flex;
justify-content: space-between;
align-items: center;
margin: 20px 0;
}
.btn {
display: inline-block;
text-align: center;
font-size: 14px;
padding: 8px 18px;
box-sizing: border-box;
background-color: #4caf50;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
margin-bottom: 10px;
}
JavaScript
복사
layout.pug
doctype html
html
head
title= title
link(rel='stylesheet', href='/css/style.css')
body
block content
HTML
복사
index.pug 작성
extends layout
block content
div.container-lg
h1= title
h3 #{title} 화면입니다
HTML
복사
1.
extends layout: 현재 템플릿이 layout.pug라는 다른 템플릿을 확장한다는 의미입니다. 이는 기본 레이아웃을 정의하고, 다양한 페이지에서 이 레이아웃을 확장하여 사용할 수 있게 해줍니다.
2.
block content: 이 부분은 layout.pug에서 정의한 block content에 해당합니다. block content는 다른 템플릿에서 확장될 때 채워질 부분을 정의하는 역할을 합니다.
3.
div.container-lg: 이 부분은 특정 페이지의 콘텐츠를 감싸는 div 요소로, Bootstrap의 container-lg 클래스를 사용하여 컨텐츠의 너비를 조절하고 있습니다.
4.
h1= title: title 변수의 값을 출력하는 헤딩(제목) 요소입니다.
5.
h3 #{title} 화면입니다: title 변수의 값을 포함한 간단한 설명을 출력하는 또 다른 헤딩(부제목) 요소입니다.
header, footer 추가
header.pug
header
h1 Pug
nav
ul
li
a(href="/") Home
li
a(href="/login") Login
li
a(href="/board") Board
HTML
복사
footer.pug
footer
p Copyright © 2023 Pug
HTML
복사
layout.pug
doctype html
html
head
title= title
link(rel='stylesheet', href='/css/style.css')
body
//- 여기에 header 추가
include fragment/header.pug
block content
//- 여기에 footer 추가
include fragment/footer.pug
HTML
복사
로그인, 회원가입 화면 작성
login.pug
extends layout
block content
div.container
h2 Login
form
div.form-group
label(for="username") Username:
input(type="text", id="username", name="username", required)
div.form-group
label(for="password") Password:
input(type="password", id="password", name="password", required)
div.form-group
button.btn(type="submit") Login
a.btn(href="/join") Sign Up
HTML
복사
join.pug
extends layout
block content
div.container
h2 Sign Up
form
div.form-group
label(for="username") Username:
input(type="text", id="username", name="username", required)
div.form-group
label(for="email") Email:
input(type="email", id="email", name="email", required)
div.form-group
label(for="password") Password:
input(type="password", id="password", name="password", required)
div.form-group
button.btn(type="submit") Sign Up
HTML
복사
게시판 화면 작성
1.
board/list.pug (목록)
2.
board/insert.pug (글쓰기)
3.
board/read.pug (글읽기)
4.
board/update.pug (글수정)
board/list.pug
extends ../layout
block content
div.container-lg
div.btn-box
h1 게시글 목록
a.btn(href="/board/insert") 글쓰기
table.table(border=1)
thead
tr
th No
th Title
th Writer
tbody
each board, index in boardList
tr
td(width=80)= index
td
a(href=`/board/${index}`) #{board.title}
td(width=200)= board.writer
HTML
복사
board/insert.pug
extends ../layout
block content
div.container-lg
h1 게시글 등록
form(action="/board", method="post")
table.table.form-group(border=1)
tr
th(width="200") Title
td
input(type="text", name="title")
tr
th(width="200") Writer
td
input(type="text", name="writer")
tr
th(width="200") Content
td
input(type="text", name="content")
div.btn-box
a.btn(href="/board") 목록
button.btn(type="submit") 등록
HTML
복사
board/read.pug
extends ../layout
block content
div.container-lg
h1 #{board.title}
table.table(border=1)
tr
th(width="200") No
td #{id}
tr
th(width="200") Writer
td #{board.writer}
tr
th(width="200") Content
td #{board.content}
div.btn-box
a.btn(href="/board") 목록
a.btn(href=`/board/update/${id}`) 수정
HTML
복사
board/update.pug
extends ../layout
block content
div.container-lg
h1 게시글 수정
form(action="/board/update", method="post")
input(type="hidden", name="id" value=`${id}`)
table.table.form-group(border=1)
tr
th(width="200") No
td #{id}
tr
th(width="200") Title
td
input(type="text", name="title", value=`${board.title}`)
tr
th(width="200") Writer
td
input(type="text", name="writer", value=`${board.writer}`)
tr
th(width="200") Content
td
input(type="text", name="content", value=`${board.content}`)
div.btn-box
a.btn(href="/board") 목록
button.btn(type="submit") 수정
HTML
복사
app.js 전체코드
const express = require('express');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const path = require('path');
// 👩💻 라우터 모듈 import
const indexRouter = require('./routes/index');
const boardRouter = require('./routes/board');
const app = express();
// 포트 설정: 3000 사용
app.set('port', 3000);
// 뷰 엔진 설정: Pug를 사용하도록 설정
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// 로깅 미들웨어 설정: 개발 환경에서는 dev 모드로 로그를 출력
app.use(morgan('dev'));
// 정적 파일 제공 미들웨어 설정: public 폴더를 정적 파일 제공 디렉토리로 설정
app.use('/', express.static(path.join(__dirname, 'public')));
// JSON 파싱 미들웨어 설정
// - JSON 형식의 요청 본문을 파싱
// - URL 인코딩된 요청 본문을 파싱
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// cookie-parser 미들웨어를 사용하여 쿠키를 파싱하는 부분
app.use(cookieParser());
// Express 애플리케이션에 세션 미들웨어 설정
app.use(session({
resave: false, // 세션 데이터가 변경되지 않으면 서버에 다시 저장하지 않음
saveUninitialized: false, // 초기화되지 않은 세션을 저장소에 저장하지 않음
secret: 'session-secret',
// 세션 쿠키 설정
cookie: {
httpOnly: true, // 브라우저에서 쿠키에 접근할 때만 가능하도록 httpOnly 속성 사용
secure: false, // HTTPS가 아닌 환경에서도 쿠키 전송 허용
},
name: 'session-cookie', // 세션 쿠키의 이름 설정
}));
// 👩💻 라우터 설정
app.use('/', indexRouter);
app.use('/board', boardRouter);
// 404 오류 처리 미들웨어
app.use((req, res, next) => {
// 요청된 경로가 존재하지 않을 때 404 상태 코드와 'Not Found' 메시지를 응답으로 전송
res.status(404).send('Not Found');
});
// 에러 처리 미들웨어
app.use((err, req, res, next) => {
// 에러가 발생했을 때, 에러 메시지를 콘솔에 출력하고 500 상태 코드와 에러 메시지를 응답으로 전송
console.error(err);
res.status(500).send(err.message);
});
// Express 애플리케이션을 특정 포트에서 실행하는 부분
app.listen(app.get('port'), () => {
// 애플리케이션이 실행되면 콘솔에 해당 포트에서 대기 중임을 출력
console.log(app.get('port'), '번 포트에서 대기 중');
});
JavaScript
복사
실행 화면
/
메인화면
/login
로그인 화면
/join
회원가입 화면
/board
게시글 목록
/board/insert
게시글 등록
/board/0
게시글 읽기
/board/update/0
게시글 수정






















