Search
Duplicate
moon
sun

퍼그 pug

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

게시글 수정