npm init 또는 npm init -y를 입력해 다음과 같이 package.json을 생성한다.
{
"name": "learn-express",
"version": "0.0.1",
"description": "익스프레스를 배우자",
"main": "app.js",
"scripts": {
"start": "nodemon app"
},
"author": "ZeroCho",
"license": "MIT"
}
🍉 start 속성은 반드시 넣어야 하고, nodedemon app은 app.js를 nodemon으로 실행한다는 뜻이다.
nodemon 모듈은 수정 사항이 생길 때마다 서버를 자동으로 재시작한다.
🍎 express, nodemon 설치
$ npm i express
$ npm i -D nodemon
app.js
// express 모듈을 실행해 app 변수에 할당
// 익스프레스 내부에 http 모듈 내장되어 있어 서버의 역할
const express = require('express');
const app = express();
// app.set('port', 포트) : 서버가 실행될 포트 설정
// process.env 객체에 PORT 속성이 있다면 사용하고, 없다면 기본값 3000번 포트 이용
app.set('port', process.env.PORT || 3000);
// app.get(주소, 라우터) : 주소에 대한 GET 요청이 들어올 때 어떤 동작을 할 지
app.get('/', (req, res) => { // req : 요청에 관한 정보 객체, res : 응담에 관한 정보 객체
res.send('Hello, Express'); // 응답 내용, 익스프레스에서는 res.send 사용
});
// listen을 하는 부분은 http 웹 서버와 동일
app.listen(app.get('port'), () => { // app.get('port')로 포트 가져옴
console.log(app.get('port'), '번 포트에서 대기 중');
});
🍅 GET 요청 외에도 POST, PUT, PATCH, DELETE, OPTIONS에 대한 라우터를 위한 app.post, app.put, app.patch. app.delete, app.options 메서드가 존재한다.
🍓 $ npm start로 실행 → http://localhost:3000 으로 접속
index.html
<html>
<head>
<meta charset="UTF-8" />
<title>익스프레스 서버</title>
</head>
<body>
<h1>익스프레스</h1>
<p>배워봅시다.</p>
</body>
</html>
app.js
const express = require('express');
const app = express();
const path = require('path'); // path 모듈로 파일 경로 지정
app.set('port', process.env.PORT || 3000);
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '/index.html')); // res.sendfile 메서드
});
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기 중');
});
요청과 응답의 중간(middle)에 위치해 기능을 추가하거나 나쁜 요청을 걸러낸다.
미들웨어는 app.use와 함께 사용한다.
const express = require('express');
const app = express();
const path = require('path');
app.set('port', process.env.PORT || 3000);
// app.use에 매개변수가 req, res, next인 함수를 넣는다.
app.use((req, res, next) => { // next : 다음 미들웨어로 넘어가는 함수
console.log('모든 요청에 다 실행됩니다.');
next();
});
// app.use, app.get 같은 라우터에 미들웨어를 여러 개 장착할 수 있다. next를 호출해야 다음 미들웨어로 넘어간다.
// 주소를 첫 번째 인수로 넣지 않으면 모든 요청에서 실행되고, 주소를 넣으면 해당 요청에서만 실행된다.
app.get('/', (req, res, next) => {
console.log('GET / 요청에서만 실행됩니다.');
next();
}, (req, res) => { // 에러 발생 -> 에러 처리 미들웨어에 전달
throw new Error('에러는 에러 처리 미들웨어로 갑니다.')
});
// 에러 처리 미들웨어는 매개변수가 err, req, res, next로 네 개
app.use((err, req, res, next) => { // err : 에러에 관한 정보
console.error(err);
res.status(500).send(err.message); // res.status 메서드로 HTTP 상태 코드 지정, 기본값 200
});
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기 중');
});
실행 결과, 콘솔에 다음과 같은 메시지가 표시된다.
🍎기본적으로 익스프레스가 에러를 처리하지만, 실무에서는 직접 에러 처리 미들웨어를 연결해 주는 것이 좋다.
에러 처리 미들웨어는 특별한 경우가 아니면 가장 아래에 위치하도록 한다.
🍓 자주 사용되는 미들웨어 패키지 설치
$ npm i morgan cookie-parser express-session dotenv
→ dotenv는 process.env를 관리하기 위해 설치
const express = require('express');
const path = require('path');
// 설치한 패키지 블러오기
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const dotenv = require('dotenv');
dotenv.config();
const app = express();
app.set('port', process.env.PORT || 3000);
// 설치한 패키지 app.use에 연결, req, res, next는 미들웨어 내부에 있다.
app.use(morgan('dev'));
app.use('/', express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// process.env.COOKIE_SECRET에 cookiesecret 값이 할당.
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
app.use((req, res, next) => {
console.log('모든 요청에 다 실행됩니다.');
next();
});
app.get('/', (req, res, next) => {
console.log('GET / 요청에서만 실행됩니다.');
next();
}, (req, res) => {
throw new Error('에러는 에러 처리 미들웨어로 갑니다.')
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send(err.message);
});
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기 중');
});
🍅 .env
COOKIE_SECRET=cookiesecret
.env 파일을 읽어서 process.env로 만든다.
🍉 process.env를 별도의 파일로 관리하는 이유 : 보안과 설정의 편의성
.env 같은 별도의 파일에 비밀 키를 적어두고 dotenv 패키지로 비밀 키를 로딩하는 방식으로 관리하면,
소스 코드가 유출되더라도 비밀 키를 지킬 수 있다.
app.use(morgan('dev')); // (개발 환경) GET / 500 20.198 ms - 50
// [메서드] [주소] [상태 코드] [응답 속도] - [응답 바이트]
app.use(morgan('combined')); // (배포 환경) ::1 - - [18/Nov/2024:04:05:35 +0000] "GET / HTTP/1.1" 500 50 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36"
app.use(morgan('common')); // ::1 - - [18/Nov/2024:04:06:32 +0000] "GET / HTTP/1.1" 500 50
app.use(morgan('short')); // ::1 - GET / HTTP/1.1 500 50 - 8.182 ms
app.use(morgan('tiny')); // GET / 500 50 - 9.405 ms
app.use('요청 경로', express.static('실제 경로'));
app.use('/', express.static(path.join(__dirname, 'public')));
🍓public 폴더
css, js, 이미지 파일들을 public 폴더에 넣으면 브라우저에서 접근할 수 있다.
실제 서버의 폴더 경로에는 public이 들어 있지만, 요청 주소에는 public이 들어 있지 않다.
ex) public/stylesheets/style.css → http://localhost:3000/stylesheets/style.css로 접근할 수 있다.
app.use(express.json()); // JSON 형식 데이터 처리
app.use(express.urlencoded({ extended: false })); // URL-encoded 형식 데이터 처리
🍉 { extended: false } 옵션
false → 노드의 querystring 모듈을 사용해 쿼리스트링을 해석
true → qs 모듈을 사용해 쿼리스트링을 해석
** qs 모듈은 내장 모듈이 아니라 npm 패키지이며, querystring 모듈의 기능을 좀 더 확장한 모듈
🍅 body-parser를 직접 설치해야 하는 경우
Raw는 요청의 본문이 버퍼 데이터일 때, Text는 텍스트 데이터일 때 해석하는 미들웨어다.
버퍼나 텍스트 요청을 처리할 필요가 있다면 body-parser를 설치한 후 다음과 같이 추가한다.
$ npm i body-parser
const bodyParser = require('body-parser');
app.use(bodyParser.raw());
app.use(bodyParser.text());
app.use(cookieParser(비밀 키));
res.cookie('name', 'zerocho', {
expires: new Date(Date.now() + 900000),
httpOnly: true,
secure: true,
});
res.clearCookie('name', 'zerocho', { httpOnly: true, secure: true });
.const session = require('express-session'); // express-session : 인수로 세션에 대한 설정을 받는다.
app.use(session({
resave: false, // resave : 세션에 수정 사항이 생기지 않더라도 다시 저장할지 설정하는 것
saveUninitialized: false, // saveUninitialized : 세션에 저장할 내역이 없더라도 처음부터 세션을 생성할지 설정하는 것
// express-session은 세션 관리 시 클라이언트에 쿠키를 보낸다.
secret: process.env.COOKIE_SECRET, // secret : 쿠키 서명에 사용할 비밀키, cookie-parser의 secret과 같게 설정하는 것이 좋다.
cookie: { // cookie : 세션 쿠키에 대한 설정, 일반적인 쿠키 옵션 모두 제공
httpOnly: true, // 클라이언트에서 쿠키를 확인하지 못한다.
secure: false, // https가 아닌 환경에서도 사용할 수 있다.
},
name: 'session-cookie', // 세션 쿠키의 이름, 기본 이름은 connect.sid
}));
req.session.name = 'zerocho'; // 세션 등록
req.sessionID; // 세션 아이디 확인
req.session.destroy(); // 세션 모두 제거
🍅 store 옵션
서버를 재시작하면 메모리가 초기화되어 세션이 모두 사라진다.
따라서 배포 시에는 store에 데이터베이스를 연결해 세션을 유지하는 것이 좋다. ex) Redis
🍉 express-session에서 서명한 쿠키 앞에는 s:이 붙는다.
실제로는 encodeURIComponent 함수가 실행되어 s%3A가 되는데
s%3A의 뒷부분이 실제 암호화된 쿠키 내용이다.
앞에 s%3A가 붙은 경우, 쿠키가 express-session 미들웨어에 의해 암호화된 것이다.
app.use((req, res, next) => {
console.log('모든 요청에 다 실행됩니다.');
next();
});
app.use(
morgan('dev'),
express.static('/', path.join(__dirname, 'public')),
express.json(),
express.urlencoded({ extended: false }),
cookieParser(process.env.COOKIE_SECRET),
);
app.use((req, res, next) => {
res.locals.data = '공유 데이터'; // 데이터 저장
next();
});
app.use((req, res, next) => {
console.log(res.locals.data); // 데이터 확인
next();
});
🍎 app.set과의 차이
app.set : 익스프레스에서 전역적으로 사용되므로 하나의 요청 안에서만 유지되어야 하는 값을 넣기에는 부적절하다. app.set은 앱 전체의 설정을 공유할 때 사용한다.
res.locals : 하나의 요청 안에서만 유지되므로 res.locals 객체를 통해 요청에 종속되는 데이터를 전달하는 것이 좋다.
🍅 미들웨어를 사용할 때 유용한 패턴 : 미들웨어 안에 미들웨어를 넣는 방식
기존 미들웨어의 기능을 확장할 수 있다.
// ex) 조건문에 따라 다른 미들웨어를 적용하는 코드
app.use((req, res, next) => { if (process.env.NODE_ENV === 'production') {
morgan('combined')(req, res, next); }
else {
morgan('dev')(req, res, next); }
});
multer
이미지, 동영상 등 여러 가지 파일을 멀티파트 형식으로 업로드할 때 사용하는 미들웨어
멀티파트 형식이란 enctype이 multipart/form-data인 폼을 통해 업로드하는 데이터의 형식을 의미한다.
multipart.html
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
multer 설치 : $ npm i multer
multer 기본 설정
const multer = require('multer');
// multer 함수의 인수로 설정을 넣는다.
const upload = multer({
storage: multer.diskStorage({ // storage : 어디에(destination) 어떤 이름으로(filename) 저장할지
destination(req, file, done) { // req: 요청에 대한 정보, file: 업로드한 파일에 대한 정보, done: 함수
done(null, 'uploads/'); // 첫 번째 인수: 에러 있다면 에러, 두 번째 인수: 실제 경로, 파일명
},
filename(req, file, done) {
const ext = path.extname(file.originalname);
done(null, path.basename(file.originalname, ext) + Date.now() + ext);
}, // uploads 폴더에 [파일명+현재시간.확장자] 파일명으로 업로드
}),
limits: { fileSize: 5 * 1024 * 1024 }, // limits : 업로드에 대한 제한 사항
}); // 파일 사이즈(fileSize, 바이트 단위)는 5MB로 제한
🍅 설정을 활용하려면 서버에 upload 폴더가 있어야 한다. → 직접 생성하거나 fs 모듈로 생성
설정이 끝나면 upload 변수가 생기는데, 여기에 다양한 종류의 미들웨어가 들어 있다.
multer 사용
1) 파일을 하나만 업로드하는 경우 → single 미들웨어 사용 / 업로드된 파일 정보는 req.file 객체에, 기타 데이터는 req.body에 저장
app.post('/upload', upload.single('image'),
(req, res) => { console.log(req.file, req.body);
res.send('ok'); });
2) 여러 파일을 업로드하는 경우
→ input 태그에 multiple을 쓰고, 미들웨어는 array로 교체 / 업로드 결과 req.files 배열
multipart.html
<form id="form" action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="many" multiple />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
app.post('/upload', upload.array('many'), (req, res) => {
console.log(req.files, req.body);
res.send('ok');
});
3) 파일을 여러 개 업로드하지만 input 태그나 폼 데이터의 키가 다른 경우
→ fields 미들웨어 사용, fields 미들웨어의 인수로 input 태그의 name을 적는다.
→ 업로드 결과는 req.files.image1, req.files.image2에 각각 들어 있다.
multipart.html
<form id="form" action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image1" />
<input type="file" name="image2" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
app.post('/upload',
upload.fields([{ name: 'image1' }, { name: 'image2' }]),
(req, res) => {
console.log(req.files, req.body);
res.send('ok');
},
);
4) 파일을 업로드하지 않고도 멀티파트 형식으로 업로드하는 경우
→ none 미들웨어 사용 / 파일을 업로드하지 않았으므로 req.body만 존재한다.
multipart.html
<form id="form" action="/upload" method="post" enctype="multipart/form-data">
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
app.post('/upload', upload.none(), (req, res) => {
console.log(req.body);
res.send('ok');
});`
app.js에서 app.get 같은 메서드가 라우터 부분이다.
라우터를 많이 연결하면 코드가 복잡해지므로 익스프레스에서는 라우터를 분리할 수 있는 방법을 제공한다.
routes/index.js
const express = require('express');
const router = express.Router();
// GET / 라우터
router.get('/', (req, res) => {
res.send('Hello, Express');
});
module.exports = router;
routes/user.js
const express = require('express');
const router = express.Router();
// GET /user 라우터
router.get('/', (req, res) => {
res.send('Hello, User');
});
module.exports = router;
index.js와 user.js를 app.use를 통해 app.js에 연결하고,
에러 처리 미들웨어 위에 404 상태 코드를 응답하는 미들웨어를 추가한다.
app.js
...
const path = require('path');
dotenv.config();
const indexRouter = require('./routes'); // index.js는 생략 가능, require('./routes/index.js')
const userRouter = require('./routes/user');
...
name: 'session-cookie',
}));
// app.use로 연결할 때 use와 get의 주소가 합쳐진다.
app.use('/', indexRouter); // GET / 라우터 -> localhost:3000에서 응답
app.use('/user', userRouter); // GET /user 라우터 -> localhost:3000/user에서 응답
app.use((req, res, next) => {
res.status(404).send('Not Found');
});
app.use((err, req, res, next) => {
...
next('route')를 이용해 다음 라우터로 넘어갈 수 있다.
// 주소의 라우터를 여러 개 만들어도 된다.
// 첫 번째 라우터
router.get('/', (req, res, next) => { // 첫 번째 미들웨어
next('route'); // next('route')를 호출 -> 다음 미들웨어 실행되지 않고 다음 라우터로
}, (req, res, next) => { // 두 번째 미들웨어 -> 실행 X
console.log('실행되지 않습니다');
next();
}, (req, res, next) => { // 세 번째 미들웨어 -> 실행 X
console.log('실행되지 않습니다');
next();
});
router.get('/', (req, res) => {
console.log('실행됩니다');
res.send('Hello, Express');
});
라우트 매개변수
router.get('/user/:id', (req, res) => {
console.log(req.params, req.query);
});
router.get('/user/:id', (req, res) => {
console.log('얘만 실행됩니다.');
});
router.get('/user/like', (req, res) => {
console.log('전혀 실행되지 않습니다.');
});
{ id: '123' } { limit: '5', skip: '10' }
app.use((req, res, next) => {
res.status(404).send('Not Found');
});
router.get('/abc', (req, res) => {
res.send('GET /abc');
});
router.post('/abc', (req, res) => {
res.send('POST /abc');
});
router.route('/abc')
.get((req, res) => {
res.send('GET /abc');
})
.post((req, res) => {
res.send('POST /abc');
});
익스프레스의 req, res 객체는 http 모듈의 req, res 객체를 확장한 것이다.
기존 http 모듈의 메서드에, 익스프레스가 추가한 메서드나 속성을 사용할 수 있다.
req.app : req 객체를 통해 app 객체에 접근할 수 있다. ex) req.app.get('port')
req.body : body-parser 미들웨어가 만드는 요청의 본문을 해석한 객체
req.cookies : cookie-parser 미들웨어가 만드는 요청의 쿠키를 해석한 객체
req.ip : 요청의 ip 주소가 담겨 있다.
req.params : 라우트 매개변수에 대한 정보가 담긴 객체
req.query : 쿼리스트링에 대한 정보가 담긴 객체
req.signedCookies : 서명된 쿠키들은 req.cookies 대신 여기에 담겨 있다.
req.get(헤더 이름) : 헤더의 값을 가져오고 싶을 때 사용하는 메서드
res.app : res 객체를 통해 app 객체에 접근할 수 있다.
res.cookie(키, 값, 옵션) : 쿠키를 설정하는 메서드
res.clearCookie(키, 값, 옵션) : 쿠키를 제거하는 메서드
res.end() : 데이터 없이 응답을 보낸다.
res.json(JSON) : JSON 형식의 응답을 보낸다.
res.locals : 하나의 요청 안에서 미들웨어 간에 데이터를 전달하고 싶을 때 사용하는 객체
res.redirect(주소) : 리다이렉트 할 주소와 함께 응답을 보낸다.
res.render(뷰, 데이터) : 다음 절에서 다룰 템플릿 엔진을 렌더링 해서 응답할 때 사용하는 메서드
res.send(데이터) : 데이터(문자열, HTML, 버퍼, 객체, 배열 등)와 함께 응답을 보낸다.
res.sendFile(경로) : 경로에 위치한 파일을 응답한다.
res.set(헤더, 값) : 응답의 헤더를 설정한다.
res.status(코드) : 응답 시의 HTTP 상태 코드를 지정한다.
req나 res 객체의 메서드는 메서드 체이닝(method chaining)을 지원하는 경우가 많다.
메서드 체이닝을 활용하면 코드 양을 줄일 수 있다.
res
.status(201)
.cookie('test', 'test')
.redirect('/admin');
🍅 최근에는 템플릿 엔진 대신 리액트(React), 뷰(Vue) 같은 프런트엔드 프레임워크가 더 널리 사용되고,
프런트엔드에서 작업 후 노드 서버와 Restful 방식으로 데이터를 주고받는 것이 일반적이다.
퍼그(Pug)는 문법이 간단해 코드 양이 줄어든다는 장점이 있어 꾸준한 인기를 얻고 있다.
퍼그 설치 $ npm i pug
익스프레스와 연결
...
app.set('port', process.env.PORT || 3000);
// views : 템플릿 파일들이 위치한 폴더 지정 -> res.render 메서드가 이 폴더 기준으로 템플릿 엔진을 찾아서 렌더링
app.set('views', path.join(__dirname, 'views'));
// view engine : 어떠한 종류의 템플릿 엔진을 사용할지
app.set('view engine', 'pug');
app.use(morgan('dev'));
...
퍼그 문법
1. HTML 표현
Pug
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
Pug
#login-button
.post-image
span#highlight
p.hidden.full
HTML
<div id="login-button"></div>
<div class="post-image"></div>
<span id="highlight"></span>
<p class="hidden full"></p>
Pug
p Welcome to Express
button(type='submit') 전송
HTML
<p>Welcome to Express</p>
<button type="submit">전송</button>
Pug
p
| 안녕하세요.
| 여러 줄을 입력합니다.
br
| 태그도 중간에 넣을 수 있습니다.
Pug
style.
h1 {
font-size: 30px;
}
script.
const message = 'Pug';
alert(message);
2. 변수
router.get('/', (req, res, next) => {
res.render('index', { title: 'Express' });
});
router.get('/', (req, res, next) => {
res.locals.title = 'Express';
res.render('index');
});
h1= title
p Welcome to #{title}
button(class=title, type='submit') 전송
input(placeholder=title + ' 연습')
- const node = 'Node.js'
- const js = 'Javascript'
p #{node}와 #{js}
<p>Node.js와 Javascript</p>
p= '<strong>이스케이프</strong>'
p!= '<strong>이스케이프하지 않음</strong>'
<p><strong>이스케이프</strong></p>
<p><strong>이스케이프하지 않음</strong></p>
3. 반복문
ul
each fruit in ['사과', '배', '오렌지', '바나나', '복숭아']
li= fruit
ul
each fruit, index in ['사과', '배', '오렌지', '바나나', '복숭아']
li= (index + 1) + '번째 ' + fruit
4. 조건문
if isLoggedIn
div 로그인 되었습니다.
else
div 로그인이 필요합니다.
case fruit
when 'apple'
p 사과입니다.
when 'banana'
p 바나나입니다.
when 'orange'
p 오렌지입니다.
default
p 사과도 바나나도 오렌지도 아닙니다.
5. include
header.pug
header
a(href='/') Home
a(href='/about') About
footer.pug
footer
div 푸터입니다
main.pug
include header
main
h1 메인 파일
p 다른 파일을 include할 수 있습니다.
include footer
6. extends와 block
[layout.pug]
doctype html
html
head
title= title
link(rel='stylesheet', href='/style.css')
block style
body
header 헤더입니다.
block content
footer 푸터입니다.
block script
[body.pug]
extends layout
block content
main
p 내용입니다.
block script
script(src="/main.js")
넌적스(Nunjucks)는 HTML 문법을 그대로 사용하되 추가로 자바스크립트 문법을 사용할 수 있으며,
파이썬의 템플릿 엔진인 Twig와 문법이 유사하다.
넌적스 설치 : $ npm i nunjucks
연결 방법 : view engine을 퍼그 대신 넌적스로 교체한다.
app.js
...
const path = require('path');
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');
// configure의 첫 번째 인수로 views 폴더의 경로, 두 번째 인수로 옵션
// express 속성에 app 객체를 연결, watch 옵션이 true이면 HTML 파일이 변경될 때 템플릿 엔진을 다시 렌더링
nunjucks.configure('views', { express: app, watch: true, });
app.use(morgan('dev'));
...
문법
1. 변수
router.get('/', (req, res, next) => {
res.render('index', { title: 'Express' });
});
<h1>{{title}}</h1>
<p>Welcome to {{title}}</p>
<button class="{{title}}" type="submit">전송</button>
<input placeholder="{{title}} 연습">
{% set node = 'Node.js' %}
{% set js = 'Javascript' %}
<p>{{node}}와 {{js}}</p>
<p>Node.js와 Javascript</p>
<p>{{'<strong>이스케이프</strong>'}}</p>
<p>{{'<strong>이스케이프하지 않음</strong>' | safe }}</p>
<p><strong>이스케이프</strong></p>
<p><strong>이스케이프하지 않음</strong></p>
2. 반복문
<ul>
{% set fruits = ['사과', '배', '오렌지', ' 바나나', '복숭아'] %}
{% for item in fruits %}
<li>{{loop.index}}번째 {{item}}</li>
{% endfor %}
</ul>
3. 조건문
{% if isLoggedIn %}
<div>로그인 되었습니다.</div>
{% else %}
<div>로그인이 필요합니다.</div>
{% endif %}
{% if fruit === 'apple' %}
<p>사과입니다.</p>
{% elif fruit === 'banana' %}
<p>바나나입니다.</p>
{% elif fruit === 'orange' %}
<p>오렌지입니다.</p>
{% else %}
<p>사과도 바나나도 오렌지도 아닙니다.</p>
{% endif %}
<div>{{'참' if isLoggedIn}}</div>
<div>{{'참' if isLoggedIn else '거짓'}}</div>
4. include
header.html
<header>
<a href="/">Home</a>
<a href="/about">About</a>
</header>
footer.html
<footer>
<div>푸터입니다.</div>
</footer>
main.html
{% include "header.html" %}
<main>
<h1>메인 파일</h1>
<p>다른 파일을 include할 수 있습니다.</p>
</main>
{% include "footer.html" %}
5. extends와 block
layout.html
<!DOCTYPE html>
<html>
<head>
<title>{{title}}</title>
<link rel="stylesheet" href="/style.css" />
{% block style %}
{% endblock %}
</head>
<body>
<header>헤더입니다.</header>
{% block content %}
{% endblock %}
<footer>푸터입니다.</footer>
{% block script %}
{% endblock %}
</body>
</html>
body.html
{% extends 'layout.html' %}
{% block content %}
<main>
<p>내용입니다.</p>
</main>
{% endblock %}
{% block script %}
<script src="/main.js"></script>
{% endblock %}
app.js
...
// 404에러가 발생하면, res.locals.message는 ‘${req.method} ${req.url} 라우터가 없습니다.’
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(error);
});
// next(error)에서 넘겨준 인수가 에러 처리 미들웨어의 err로 연결
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');
});
...
에러 처리 미들웨어는 error라는 템플릿 파일을 res.locals.message와 res.locals.error에 넣어준 값과 함께 렌더링 한다.
res.render에 변수를 대입하는 것 외에도, res.locals 속성에 값을 대입해 템플릿 엔진에 변수를 주입할 수 있다.
error 객체의 스택 트레이스(error.html의 error.stack)는 시스템 환경(process.env.NODE_ENV)이 production(배포 환경)이 아닌 경우에만 표시된다. 에러 스택 트레이스가 노출되면 보안에 취약할 수 있기 때문에 배포 환경인 경우에는 에러 메시지만 표시된다.
1. 미들웨어는 (req), (res), (next)를 매개변수로 갖는 함수로, app.use나 app.get, app.post 등으로 장착한다.
동시에 여러 개의 미들웨어를 장착할 수도 있고, 다음 미들웨어로 넘어가려면 (next) 함수를 호출해야 한다.
2. 요청과 응답에 대한 정보를 콘솔에 기록하는 미들웨어는 (morgan),
정적인 파일들을 제공하는 라우터 역할을 하는 미들웨어는 (static)이다.
3. body-parser는 요청의 본문에 있는 데이터를 해석해서 (req.body) 객체로 만들어주는 미들웨어이고,
cookie-parser는 요청에 동봉된 쿠키를 해석해 (req.cookies) 객체로 만들어주는 미들웨어이다.
4. multer는 이미지, 동영상 등 여러 가지 파일을 (멀티파트) 형식으로 업로드할 때 사용하는 미들웨어이고,
파일을 하나만 업로드하는 경우에는 (single) 미들웨어를,
여러 파일을 업로드하는 경우에는 (array) 미들웨어를 사용한다.
5. (템플릿 엔진)을 사용하면 자바스크립트로 HTML을 렌더링 할 수 있고, 반복문이나 동적 처리를 간단히 구현할 수 있다.
대표적인 예시로 (Pug, 퍼그)와 (Nunjuks, 넌적스)가 있다.
6. 넌적스(Nunjucks)에서 block은 {% block [블록명] %}으로 선언하고 {% endblock %}로 종료한다.
block이 되는 파일에서는 {% extends 경로 %} 키워드로 레이아웃 파일을 지정하고 block 부분을 넣는다.
7. error 객체의 스택 트레이스는 시스템 환경이 (production, 배포 환경)이 아닌 경우에만 표시된다.
1.express 서버 코드 app.js를 작성하시오.
// express 모듈을 실행해 app 변수에 할당
________________________________________
________________________________________
// app.set('port', 포트) : 서버가 실행될 포트 설정
// process.env 객체에 PORT 속성이 있다면 사용하고, 없다면 기본값 3000번 포트 이용
________________________________________
// app.get(주소, 라우터) : 주소에 대한 GET 요청이 들어올 때 어떤 동작을 할 지
// req : 요청에 관한 정보 객체, res : 응담에 관한 정보 객체
// 응답 내용은 'Hello, Express', res.send 사용
________________________________________
________________________________________
// listen을 하는 부분은 http 웹 서버와 동일
app.listen(app.get('port'), () => { // app.get('port')로 포트 가져옴
console.log(app.get('port'), '번 포트에서 대기 중');
});
2. HTML 코드를 퍼그 코드로 표현하시오.
<div id="login-button"></div>
<div class="post-image"></div>
<span id="highlight"></span>
<p class="hidden full"></p>
1.
// express 모듈을 실행해 app 변수에 할당
// 익스프레스 내부에 http 모듈 내장되어 있어 서버의 역할
const express = require('express');
const app = express();
// app.set('port', 포트) : 서버가 실행될 포트 설정
// process.env 객체에 PORT 속성이 있다면 사용하고, 없다면 기본값 3000번 포트 이용
app.set('port', process.env.PORT || 3000);
// app.get(주소, 라우터) : 주소에 대한 GET 요청이 들어올 때 어떤 동작을 할 지
app.get('/', (req, res) => { // req : 요청에 관한 정보 객체, res : 응담에 관한 정보 객체
res.send('Hello, Express'); // 응답 내용, 익스프레스에서는 res.send 사용
});
// listen을 하는 부분은 http 웹 서버와 동일
app.listen(app.get('port'), () => { // app.get('port')로 포트 가져옴
console.log(app.get('port'), '번 포트에서 대기 중');
});
2.
#login-button
.post-image
span#highlight
p.hidden.full
출처 : 조현영, 『 Node.js 교과서 개정 3판』, 길벗(2022)
Corner Node.js 1
Editor : Snoopy
[노드 1팀] 8장. 몽고디비 (0) | 2024.12.27 |
---|---|
[노드 1팀] 7장. MySQL (0) | 2024.11.29 |
[노드 1팀] 5장. 패키지 매니저 (0) | 2024.11.15 |
[노드 1팀] 4장. http 모듈로 서버 만들기 (0) | 2024.11.15 |
[노드 1팀] 3장. 노드 기능 알아보기 (2) (0) | 2024.11.08 |