라우팅 분리
라우터를 만들 때는 요청 메서드와 주소별로 분기 처리를 하느라 코드가 복잡해진다.
express를 이용하면 라우팅을 깔끔하게 관리할 수 있다.
routes 폴더를 만들고 그 안에 index.js로 관리해보자.
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send('Hello world!!');
});
module.exports = router;
index.js
const { Router } = require('express');
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send('Hello, User');
});
module.exports = router;
user.js
app.js에 app.use를 이용하여 연결한다. 또한 에러 처리 미들웨어를 설정하자.
dotenv.config();
const indexRouter = require('./routes');
const userRouter = require('./routes/user');
const app = express();
app.set('port', process.env.PORT || 3000);
app.use('/',indexRouter);
app.use('/user',userRouter);
app.use((req, res, next) => {
res.status(404).send('Not Found');
});
indexRouter는 app.use로'/'에 연결하였고, userRouter는 app.use로 '/user'에 연결했다.
indexRoutser는 use의 '/'와 get의 '/'이 합쳐져 GET / 라우터가 되었고,
userRouter는 use의 '/user'와 get의 '/'rk gkqcuwu GET /user 라우터가 된다.
indexRouter | userRouter |
next('route')으로 라우터에 연결된 나머지 미들웨어들을 건너뛸 수도 있다.
또한, 라우터 주소에는 정규표현식을 비롯한 특수 패턴을 사용할 수 있다.
router.get('/user/:id', function(req, res) {
console.log(req.params, req.query);
});
주소에 :id을 이용함으로써 req.params.id에 /user/id 와 같은 주소를 처리할 수 있다.
이러한 패턴은 일반 라우터보다 뒤에 위치해야 한다. 그래야 다른 라우터를 방해하지 않는다.
router.route나 app.route로 코드를 더 깔끔하게 만들 수 있다.
router.route('/abc')
.get((req, res) => {
res.send('GET /abc');
})
.post((req, res) => {
res.send('POST /abc');
});
req, res 객체
express의 req, res 객체는 http 모듈의 req, res 객체를 확장한 것이다. 기존 http 모듈의 메서드도 사용할 수 있고, express가 추가한 메서드나 속성을 사용할 수도 있다.
- req.app: req 객체를 통해 app 객체에 접근할 수 있다.
- req.body: 요청의 본문을 해석한 객체
- req.cookies: 쿠키를 해석한 객체
- req.ip: 요청의 ip주소
- req.params: 라우트 매개변수에 대한 정보가 담긴 객체
- req.query: 쿼리스트링에 대한 정보가 담긴 객체
- req.signedCookies: 서명된 쿠키에 대한 정보
- req.get(헤더 이름): 헤더의 값을 가져오고 싶을 때 사용하는 메서드
- res.app: app객체에 접근할 수 있다.
- res.cookie(키, 값, 옵션): 쿠키를 설정하는 메서드
- res.clrearCookie(키, 값, 옵션): 쿠키를 제거하는 메서드
- res.end(): 데이터 없이 응답을 보낸다.
- res.json(JSON): JSON 형식의 응답
- res.redirect(주소): 리다이렉트할 주소와 함께 응답
- res.render(뷰, 데이터): 템플릿 엔진을 렌더링 해서 응답
- res.send(데이터): 데이터와 함께 응답
- res.sendFile(경로): 경로에 위치한 파일을 응답
- res.set(헤더, 값): 응답의 헤더를 설정
- res.status(코드): HTTP 상태코드 지정
템플릿 엔진
HTML은 정적인 언어이다. 주어진 기능만 사용할 수 있고 사용자가 기능을 직접 추가할 수 없다.
HTML로 1000개가 넘는 데이터를 모두 표현하고 싶다면 일일이 직접 코딩해서 넣어야 한다.
하지만 자바스크립트로는 반복문을 이용하여 간단하게 처리할 수 있다.
템플릿 엔진은 자바스크립트를 사용해서 HTML을 렌더링 할 수 있게 한다.
기존 HTML과는 문법이 살짝 다를 수 있다.
대표적인 템플릿 엔진으로는 퍼그(Pug)와 넌적스(Nunjucks)가 있다.
퍼그(제이드)
예전 이름은 제이드였다. 문법이 간단하므로 코드의 양이 줄어든다. 문법은 루비와 비슷하다.
퍼그를 설치하고 살펴보자.
npm i pug
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
views는 템플릿 파일들이 위치한 폴더를 지정하는 것이다. res.render 메서드가 이 폴더 기준으로 템플릿 엔진을 찾아서 렌더링한다. res.render('index')라면 views/index.pug를 렌더링한다.
view engine은 어떠한 종류의 템플릿 엔진을 사용할지를 나타낸다. 현재는 pug로 설정했다.
퍼그는 HTML과 달리 화살 괄호(< >)와 닫는 태그가 없다. 탭 또는 스페이스로만 태그의 부모 자식 관계를 규명한다.
들여쓰기에 유의해야 한다.
doctype html
html
head
title=title
link(rel='stylesheet', href='/stylesheets/style.css')
동일한 간격만 있으면 문제없다.
속성 중 아이디와 클래스가 있는 경우에는 다음과 같이 표현할 수 있다. div 태그인 경우 div 문자는 생략 가능하다.
#login-button
.post-image
.span#highlight
p.hidden.full
HTML 텍스트는 다음과 같이 태그 또는 속성 뒤에 한 칸 띄고 입력하면 된다.
p Hello World
button(type='submit') 전송
style이나 script 태그로 CSS 또는 자바스크립트 코드를 작성하고 싶다면 다음과 같이 태그 뒤에 점을 붙인다.
style.
h1 {
font-size: 30px;
}
script.
const message = 'Pug';
alert(message);
HTML과 다르게 자바스크립트 변수를 템플릿에 렌더링 할 수 있다. res.render 호출 시 보내는 변수를 퍼그가 처리한다.
h1 = title
p Welcome to #{title}
button(class=title, type='submit') 전송
input(placehold=tile+' 연습')
res.render로 오는 변수를 위와 같이 렌더링 가능하다.
- 를 이용하여 직접 변수를 선언할 수도 있다.
- const node = 'Node.js'
- const js = 'Javascript'
p #{node}와 #{js}
반복문도 사용할 수 있다.
ul
each fruit in ['사과', '배', '오레지', '바나나', '복숭아']
li= fruit
물론 조건문도 가능하다.
if isLoggedIn
div 로그인 되었습니다
else
div 로그인이 필요합니다.
include를 이용하여 다른 퍼그파일이나 HTML을 넣을 수 있다.
include header
main
h1 메인
p 다른파일 include
include footer
extends를 이용하여 공통된 레이아웃을 적용할 수도 있다.
extends layout
block content
main
p 내용
block script
script(src="/main.js")
block이름이 같은 부분에 새롭게 덮어 씌운다.
넌적스
넌적스는 파이어폭스를 만든 모질라에서 만들었다. HTML 문법을 그대로 사용하되 추가로 자바스크립트 문법을 사용할 수 있고, 파이썬 템플릿 엔진인 Twig와 문법이 상당히 유사하다.
npm i nunjucks
const nunjucks = require('nunjucks');
dotenv.config();
const indexRouter = require('./routes');
const userRouter = require('./routes/user');
const app = express();
app.set('port', process.env.PORT || 3000);
app.set('view engine', 'html');
nunjucks.configure('views', {
express: app,
watch: true,
});
연결 방법을 살펴보면 configure의 첫 번째 인수로 views의 경로를 넣고, 두 번째 인수로 옵션을 넣는다.
express 속성에 app 객체를 연결한다. watch 옵션이 true이면 HTML 파일이 변경될 때 템플릿 엔진을 다시 렌더링한다.
퍼그와 같이 변수를 다룰 수 있다.
<h1>{{ title }}</h1>
<p>Welcome to {{ title }}</p>
<button class="{{ title }}" type="submit">전송</button>
<input placeholder="{{ title }} 연습" />
넌적스에서 변수는 {{ }}로 감싼다.
내부에 변수를 선언하는 방법은 {% set 변수 = '값' %}이다.
{% set node = 'NOde.js' %}
{% set js = 'Javascript' %}
<p>{{ node }}와 {{ js }}</p>
이스케이프하고 싶지 않다면 {{변수 | safe }}를 사용한다.
<p>{{ '<strong>이스케이프</strong>' }}</p>
<p>{{ '<strong>이스케이프 X</strong>'| safe }}</p>
넌적스에서 특수한 구문은 {% %} 안에 쓴다.
<ul>
{% set fruit = ['사과', '배', '오렌지', '바나나', '복숭아'] %}
{% for item in fruit %}
<li>{{ item }}</li>
{% endfor %}
</ul>
include는 다음과 같이 한다.
{% include "header.html" %}
<main>
<h1>메인</h1>
</main>
{% include "footer.html" %}
extends와 block은 다음 전체 코드를 보며 알아보자.
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
layout.html
{% extends "layout.html" %}
{% block content %}
<h1>{{ title }}</h1>
<p>Welcome to {{ title }}</p>
{% endblock %}
index.html
{% extends "layout.html" %}
{% block content %}
<h1>{{ message }}</h1>
<h1>{{ error.status }}</h1>
<h1>{{ error.stack }}</h1>
{% endblock %}
error.html
extends로 block에 껴 넣을 코드를 작성한다. 이제 완성된 페이지를 봐보자.
그전에, error 처리 미들웨어를 살짝 변경해야 한다.
app.use((err, req, res, next) => {
res.locals.message = err.message;
res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
res.status(err.status || 500);
res.render('error');
});
res.locals에 에러에 대한 정보를 담아 보낸다.
error 객체의 스택 트레이스는 시스템 환경이 배포 환경이 아닌 경우에만 표시한다.
localhost:3000/abc에 접속해보자.