요청은 주소를 통해 들어오며 html을 요청하는것 외에 세션저장 같은 동작을 취하길 요구 할수있다. 그러므로 서버가 이해하기 쉬운 주소를 사용하면 좋은데 여기서 REST API가 쓰인다.
REST API는 Representational State Transfer의 약어로 네트워크 구조의 한 형식이다.
서버의 자원을 정의하고, 자원에 대한 주소를 지정하는 방법을 가리킨다.
주소는 의미를 명확히 전달하기 위해 명사로 구분되며 /user이면 사용자 정보에 관련된 자원을 요청하는 것이고, /post 라면 게시글에 관련된 자원을 요청하는 것이라고 추측할 수 있다.
주소 외에도 HTTP 요청 메서드라는 것을 사용하는데 폼 데이터를 전송할 때 GET 또는 POST 메서드 등을 지정하는것이 요청 메서드다.
GET
서버 자원을 가져오고자 할 때 사용한다. 요청의 본문에 데이터를 넣지 않는다. 데이터를 서버로 보내야 한다면 쿼리스트링을 사용한다.
POST
서버에 자원을 새로 등록하고자 할 때 사요한다. 요청의 본문에 새로 등록할 데이터를 넣어 보낸다.
PUT
서버의 자원을 요청에 들어 있는 자원으로 치환하고자 할때 사용한다. 요청의 본문에 치환할 데이터를 넣어 보낸다.
PATCH
서버 자원의 일부만 사용하고 할 때 사용한다. 요청의 본문에 일부 수정할 데이터를 넣어 보낸다.
DELETE
서버의 자원을 삭제하고자 할 때 사용한다.
주소 하나가 여러개의 요청 메서드를 가질 수 있으며
GET 메서드의 /user 주소로 요청을 보내면 사용자 정보를 가져오는 요청이라는 것을 알 수 있고
POST 메서드의 /user 주소로 요청을 보내면 새로운 사용자를 등록하려 한다는 것을 알 수 있다.
이렇게 주소와 메서드만보고 요청의 내용을 명확하게 알아볼 수 있다는것이 장점이다.
또한 GET 메서드 같은 경우에는 브라우저에서 캐싱할 수도 있어서 같은 주소의 GET 요청을 할 때 서버에서 가져오는 것이 아니라 캐시에서 가져올 수 있다. 이렇게 캐싱이되면 성능이 좋아진다.
HTTP 프로토콜을 사용하면 클라이언트가 누구든 상관없이 서버와 소통할 수 있다. iOS, 안드로이드, 웹이 모두 같은 주소로 요청을 보낼 수 있다. 즉, 서버와 클라이언트가 분리되어 있다는 뜻이다. 이렇게 서버와 클라이언트를 분리하면 추후에 서버를 확장할때 클라이언트에 구애되지 않아서 좋다.
- client -
function getUser() { // 로딩 시 사용자 가져오는 함수
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 200) {
var users = JSON.parse(xhr.responseText);
var list = document.getElementById('list');
list.innerHTML = '';
Object.keys(users).map(function (key) {
var userDiv = document.createElement('div');
var span = document.createElement('span');
span.textContent = users[key];
var edit = document.createElement('button');
edit.textContent = '수정';
edit.addEventListener('click', function () { // 수정 버튼 클릭
var name = prompt('바꿀 이름을 입력하세요');
if (!name) {
return alert('이름을 반드시 입력하셔야 합니다');
}
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.responseText);
getUser();
} else {
console.error(xhr.responseText);
}
};
xhr.open('PUT', '/users/' + key);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({ name: name }));
});
var remove = document.createElement('button');
remove.textContent = '삭제';
remove.addEventListener('click', function () { // 삭제 버튼 클릭
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.responseText);
getUser();
} else {
console.error(xhr.responseText);
}
};
xhr.open('DELETE', '/users/' + key);
xhr.send();
});
userDiv.appendChild(span);
userDiv.appendChild(edit);
userDiv.appendChild(remove);
list.appendChild(userDiv);
});
} else {
console.error(xhr.responseText);
}
};
xhr.open('GET', '/users');
xhr.send();
}
window.onload = getUser; // 로딩 시 getUser 호출
// 폼 제출
document.getElementById('form').addEventListener('submit', function (e) {
e.preventDefault();
var name = e.target.username.value;
if (!name) {
return alert('이름을 입력하세요');
}
var xhr = new XMLHttpRequest();
xhr.onload = function () {
if (xhr.status === 201) {
console.log(xhr.responseText);
getUser();
} else {
console.error(xhr.responseText);
}
};
xhr.open('POST', '/users');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify({ name: name }));
e.target.username.value = '';
});
ㄱ
- server -
const http = require('http');
const fs = require('fs');
const users = {};
http.createServer((req, res) => {
/*서버에 자원 요청, 사용자 목록*/
if (req.method === 'GET') {
if (req.url === '/') {
return fs.readFile('./restFront.html', (err, data) => {
if (err) {
throw err;
}
res.end(data);
});
} else if (req.url === '/about') {
return fs.readFile('./about.html', (err, data) => {
if (err) {
throw err;
}
res.end(data);
});
} else if (req.url === '/users') {
return res.end(JSON.stringify(users));
}
return fs.readFile(`.${req.url}`, (err, data) => {
if (err) {
res.writeHead(404, 'NOT FOUND');
return res.end('NOT FOUND');
}
return res.end(data);
});
}
/*서버에 데이터를 담은 form을 제출*/
else if (req.method === 'POST') {
if (req.url === '/users') {
let body = '';
req.on('data', (data) => {
body += data;
});
return req.on('end', () => {
console.log('POST 본문(Body):', body);
const { name } = JSON.parse(body);
const id = +new Date();
users[id] = name;
res.writeHead(201);
res.end('등록 성공');
});
}
}
/*서버에 데이터 수정을 요청*/
else if (req.method === 'PUT') {
if (req.url.startsWith('/users/')) {
const key = req.url.split('/')[2];
let body = '';
req.on('data', (data) => {
body += data;
});
return req.on('end', () => {
console.log('PUT 본문(Body):', body);
users[key] = JSON.parse(body).name;
return res.end(JSON.stringify(users));
});
}
}
/*서버에 데이터 삭제를 요청*/
else if (req.method === 'DELETE') {
if (req.url.startsWith('/users/')) {
const key = req.url.split('/')[2];
delete users[key];
return res.end(JSON.stringify(users));
}
}
res.writeHead(404, 'NOT FOUND');
return res.end('NOT FOUND');
})
.listen(8085, () => {
console.log('8085번 포트에서 서버 대기중입니다');
});
쿠키란 인터넷 사용자가 어떠한 웹 사이트를 방문할 경우 그 사이트가 사용하고 있는 서버를 통해 인터넷 사용자의 컴퓨터에 설치되는 작은 기록 정보 파일을 의미한다.
쿠키는 서버와 클라이언트에서 모두 저장하고 사용 가능하며, 일정 기간 동안 데이터를 저장할 수 있기 때문에 로그인 상태를 일정 시간 유지해야 하는 웹 사이트에서 사용한다. 또한 이 기록 파일에 담긴 정보는 인터넷 사용자가 같은 웹사이트를 방문할 때마다 읽히고 수시로 갱신된다.
이때 민감한 개인정보를 쿠키에 넣어두는것은 적절하지 못하다. 쿠키는 생각보다 쉽게 노출되며(개발자도구의 Application 탭) 또한, 조작될 위험도 있다.
http.createServer((req, res) => {
const cookies = parseCookies(req.headers.cookie); //요청의 헤더에서 쿠키를 추출
if (req.url.startsWith('/login')) { //요청의 url이 /login으로 시작할 경우. login 시도
const { query } = url.parse(req.url); //요청에서 queryString을 추출
const { name } = qs.parse(query); //queryString에서 name의 값을 추출
const expires = new Date(); //현재 시간이 담긴 Date객체 생성
expires.setMinutes(expires.getMinutes() + 5); //5분후 만료된다.
const randomInt = +new Date(); //세션 식별자로 쓰일 Date 객체
session[randomInt] = {
name,
expires,
};
/*응답으로 보낼 페이지의 헤더. 상태코드가 302 이므로 다른 페이지로 임시 이동 한다.*/
res.writeHead(302, {
Location: '/', //이동할 페이지
'Set-Cookie': `session=${randomInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`, //브라우저에 저장할 쿠키
});
res.end(); //응답 전송
}
/*브라우저에서 전송한 쿠키에 session이 담겨있고 세션이 만료되지 않았다면 true 아니면 false*/
else if (cookies.session && session[cookies.session].expires > new Date()) {
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' }); //응답으로 보낼 페이지의 헤더
res.end(`${session[cookies.session].name}님 안녕하세요`); //코드를 담아 응답을 전송
} else {
fs.readFile('./server4.html', (err, data) => { //전송할 html 파일을 읽어서 전송
if (err) {
throw err;
}
res.end(data);
});
}
})
.listen(8083, () => {
console.log('8084번 포트에서 서버 대기중입니다!');
});
쿠키에 이름을 담아서 보내는 대신 randomInt 라는 임의의 순자를 보냈다. 이것을 세션 아이디라고 하며 서버에 사용자 정보를 저장하고 클라이언트와는 세션 아이디로만 소통한다.
(세션 아이디는 꼭 쿠키를 사용해서 주고 받지 않아도 된다. 실제 배포용 서버에서는 세션을 위와 같이 변수에 저장하지 않는다. 서버가 멈추거나 재시작되면 메모리에 저장된 변수가 초기화되고 또한 서버의 메모리가 부족하면 세션을 저장하지 못하는 문제도 생기기 때문이다. 그래서 보통은 데이터베이스에 넣어둔다.)
그러므로 사용자의 이름과 만료시간 같은 민감한 정보는 session이라는 객체에 대신 저장한다.
cookie.session이 있고 만료기한을 넘기지 않았다면 session 변수에서 사용자 정보를 가져와서 사용한다.
이러한 방식을 세션이라 한다.
쿠키명=쿠키값
기본적인 쿠키의 값. ex) name=tester
Expires=날짜
만료기한. 이 기한이 지나면 쿠키가 제거된다. 기본값은 클라이언트가 종료될때까지
Max-age=초
Expires와 비슷하지만 날짜 대신 초를 입력한다. 해당 초가 지나면 쿠키가 제거된다. Expires보다 우선한다.
Domain=도메인명
쿠키가 전송될 도메인을 특정한다. 기본값은 현재 도메인이다.
Path=URL
쿠키가 전송될 URL을 특정할 수 있다. 기본값은 '/'이고 이 경우 모든 URL에서 쿠키를 전송할 수 있다.
Secure
HTTPS일 경우에만 쿠키가 전송된다.
HttpOnly
설정 시 자바스크립트에서 쿠키에 접근할 수 없다. 쿠키 조작을 방지하기 위해 설정하는 것이 좋다.
/*
쿠키 예시
'Set-Cookie': `name=${encodeURIComponent(name)}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`
*/
const parseCookies = (cookie = '') => //쿠키 추출
cookie 매개변수로 받은 cookie
.split(';') //; 를 기준으로 쿠키들을 나눈것을 반환한다.
.map(v => v.split('=')) //위의 split에서 넘겨받은 쿠키 문자열을 = 를 기준으로 나누어 [키, 값]의 형태로 배열에 담아 반환한다.
.reduce((acc, [k, v]) => { //위의 map에서 넘겨받은 배열을 acc에 누적시킨다.
acc[k.trim()] = decodeURIComponent(v); //acc에 공백을 지운 키와 디코드한 값을 누적
return acc; //누적값을 return
}, {});
express 모듈은 node.js로 웹 서버를 개발할 때 가장 많이 쓰이는 모듈이다. 물론 http 모듈을 통해 웹 서버를 개발해도 되지만 express 모듈은 훨씬 쉽고 편하게 개발할 수 있게 해준다.
constexpress = require('express');
constapp = express();
app.use((request, response) => { //get() 함수를 통해 URI로 호출되면 해당 로직이 실행된다.
response.send('Hello express module');
});
app.listen(3000, () => { //서버를 동작시킴
console.log('Server is running port 3000!');
});
Express-generator
* 익스프레스 프레임워크는 많은 의존성 패키지를 요구하는데 이것을 찾아 설치하기는 조금 어렵다. 그러므로 package.json을 만들어주고 기본 폴더 구조까지 잡아주는 패키지가 만들어졌는데 그것이 Express-generator 다.
해당 패키지는 콘솔 명령어로 실행 하므로 npm 전역 설치가 필요하다.
npm install -g express-generator
익스프레스 프로젝트를 생성하려면 새로 프로젝트를 만들고자 하는 폴더로 이동해서
express <프로젝트 이름> 을 입력한다.
해당 명령어는 생성된 폴더 및 파일명을 출력해주며 다음에 입력해야할 명령어를 알려준다.
프로젝트 이름으로 생성된 폴더로 이동한후 npm i 를 입력하면 프로젝트 폴더내에 생성된 package.json 에 명시된 패키지들이 설치된다.
--view=pug
* 프로젝트에서 사용할 템플릿 엔진을 설정한다. ejs를 사용하고 싶다면 pug 대신 ejs를 입력하면 된다.
생성된 폴더 구조
* 라우터를 컨트롤러라고 본다면 MVC(모델-뷰-컨트롤러) 패턴과 비슷하다.
app.js
서버 역할
bin/www
* 서버를 실행하는 스크립트로 http 모듈에 express 모듈을 연결하고 포트를 지정하는 역할을 맡는다. * js 확장자가 붙어있지 않다. * #!/usr/bin/env node 라는 주석이 첫 줄에 달려있는데. www 파일을 콘솔 명령어로 만들때 이 주석을 명령어로 쓸수있다.
public
외부(브라우저 등의 클라이언트)에서 접근 가능한 파일들을 모아두는 폴더. 이미지, 자바스크립트, css 파일들이 들어있다.
routes
주소별 라우터들을 모아둔 폴더. 서버의 로직이 담긴 파일은 이곳에 담긴다.
views
템플릿 파일을 모아둔 폴더. 화면에 보이기 위한 파일을 담는다.
models
데이터 파일을 담아두는 폴더.
bin/www
/**
* Module dependencies.
*/
/*app, debug, http 모듈을 가져온다.*/
var app = require('../app');
var debug = require('debug')('test-project:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
/*
서버가 실행될 포트를 설정.
process.env 객체에 PORT 속성이 있다면 그 값을 사용하고 없다면 기본값으로 3000번 포트를 이용한다.
app.set(키, 값)을 사용해서 데이터를 저장할수있으며 app.get(키) 로 가져올 수 있다.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
/*http.createServer에 불러온 모듈을 넣어준다. app 모듈이 createServer 메소드의 콜백함수 역할을 한다.*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
/*listen을 시작. http 웹 서버와 동일하지만 익스프레스는 콜백함수 부분을 조금 다르게 만든다.*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
/*express 패키지로 객체 생성. 이 변수에 각종 기능을 연결한다.*/
var app = express();
// view engine setup
/*app.set 메서드로 익스프레스 앱을 설정할 수 있다.*/
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
/*app.use를 사용해서 미들웨어를 연결한다.*/
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
/*app 객체를 모듈로 만들어 반환한다. 이것이 bin/www에서 사용된다.*/
module.exports = app;
package.json 을 보면 scripts에 start 속성이 있고 속성값으로 node ./bin/www 가 적혀있다. 따라서 npm run start 나 npm start 명령어로 서버를 실행할 수 있다.
- response -
constexpress = require('express');
constapp = express();
/*
app.get('위치')를 지정할 수 있다.
send() 메소드에 객체로 입력했기 때문에 JSON 형식으로 출력된다.
*/
app.get('/', (request, response) => {
constresult = [];
constmultipleNumber = 9;
for (leti = 1; i < 5; i++) {
result.push({
number:`${multipleNumber}X${i}`,
multiple:multipleNumber * i,
});
}
response.send(result);
});
/*status() 메소드를 활용해 상태 코드를 전달한다.*/
app.get('/error', (request, response) => {
response.status(404).send('404 ERROR');
});
app.listen(3000, () => {
console.log('Server is running port 3000!');
});
response 객체의 메소드
res.download()
파일이 다운로드되도록 프롬포트 한다.
res.end()
응답 프로세스를 종료한다.
res.json()
JSON응답을 전송한다.
res.jsonp()
JSONP 지원을 통해 JSON 응답을 전송한다.
res.redirect()
요청의 경로를 재지정한다.
res.render()
템플릿을 랜더링한다.
res.send()
다양한 유형의 응답을 전송한다.(문자열 HTML, 객체 JSON, 배열 JSON의 형태로)
res.sendFile
파일을 전송한다.
res.sendStatus()
응답 상태 코드를 설정한 후 해당 코드를 문자열로 표현한 내용을 응답 본문으로 전송한다.
- request -
// 요청 메서드
constexpress = require('express');
constapp = express();
app.use((request, response) => {
/*요청 헤더를 추출. 웹 브라우저로 HTTP 요청시 User-Agent 속성값을 가지고 있다.*/
constagent = request.header('User-Agent');
constparamName = request.query.name; //매개변수 추출
constbrowserChrome = 'Hello Chrome';
constbrowserOthers = 'Hello Others';
/*request() 객체에 기본적으로 내장된 메소드*/
console.log(request.headers);
console.log(request.baseUrl);
console.log(request.hostname);
console.log(request.protocol);
if (agent.toLowerCase().match(/chrome/)) { //Chrome 브라우저인가?
express 모듈의 request 객체를 이용하면 사용자가 요청(request)한 내용이 어떤 것인지 알수 있다.
request 객체를 활용하여 매개변수를 추출하고 헤더의 속성을 알아내 브라우저를 구분하여 페이지를 출력할 수 있다.
headers
요청 헤더의 추출
Header()
요청 헤더의 속성 지정 또는 추출
query
GET 방식으로 요청한 매개변수 확인
body
POST 방식으로 요청한 매개변수 확인
params
라우팅 매개변수 추출
- 미들웨어 -
요청과 응답의 중간에 위치해 미들웨어라 불린다. 라우터나 에러 핸들러 같은 다른 로직을 실행해 요청과 응답을 조작할수있으며 기능을 추가하거나 나쁜 요청을 걸러낼수 있도록 분리된 함수이다.
- use -
/*
app.use 메서드의 인자로 들어 있는 함수가 미들웨어이다.
미들웨어는 use 메서드로 app에 장착되며 장착된 순서대로 실행된후 라우터에서 클라이언트로 응답을 보낸다.
*/
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// 404 처리 미들웨어
app.use(function(req, res, next) {
/*
http-errors(createError) 패키지가 404 에러를 만들어내고
이 에러를 next에 담아 아래 에러 핸들러로 보낸다.
*/
next(createError(404));
});
// 에러 핸들러. 일반적으로 미들웨어 중 제일 아래에 위치하고 위에있는 미들웨어에서 발생하는 에러를 받아서 처리한다.
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
JSON 형식 { name : 'tester', book: 'nodejs' } 를 본문으로 보내면 req.body에 그대로 들어가지만
URL-encoded 형식 name=tester&book=nodejs를 본문으로 보내면 req.body에 { name : 'tester', book: 'nodejs' } 가 들어간다.
* body-parser가 모든 본문을 해석해는 것은 아니다. multipart/form-data 같은 폼을 통해 전송된 데이터는 해석하지 못하며 이는 다른 모듈을 사용해서 해석해야한다.
- cookie-parser -
요청에 동봉된 쿠키를 해석해준다.
var cookieParser = require('cookie-parser');
app.use(cookieParser());
해석된 쿠키들은 req.cookies 객체에 들어간다.
ex) { name : 'tester' }
인자로 문자열을 넣어줄 경우 제공한 문자열로 서명된 쿠기가 되며 서명된 쿠키는 클라이언트에서 수정했을 때 에러가 발생하게 되므로 클라이언트에서 쿠키로 위험한행동을 하는 것을 방지 할 수 있다.
- morgan -
요청에 대한 정보를 콘솔에 기록해주는 미들웨어
var logger = require('morgan');
app.use(logger('dev'));
인자로 dev 대신 short, common, combined 등을 줄 수 있으며 인자에 따라 콘솔에 나오는 로그가 다르다.
dev 인 경우
GET / 200 51.267 ms - 1539 의미는 순서대로
HTTP요청(GET) 주소(/) HTTP상태코드(200) 응답속도(51.267ms) - 응답바이트(1539) 이다.
short나 dev는 개발시
common 이나 combined는 배포시에 많이 쓰인다.
- express-session -
세션 관리용 미들웨어로 로그인 등의 이유로 세션을 구현할 때 쓴다.
express-generator로 설치되지 않으므로 npm을 통해 별도로 설치해야한다.
express-session 1.5 버전 이전에는 내부적으로 cookie-parser를 사용하고 있어서 cookie-parser 미들웨어보다 뒤에 위치해야 했지만, 1.5버전 이후부터는 사용하지 않게 되어 순서가 상관 없어졌다. 만약 어떤 버전을 사용하고 있는지 모르겠다면 cookie-parser 미들웨어를 뒤에 놓는 것이 안전하다.
var logger = require('morgan');
var session = require('express-session');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
app.use(cookieParser('secret code'));
app.use(session({
/* resave 요청이 왔을 때 세션에 수정사항이 생기지 않더라도 세션을 다시 저장할지에 대한 설정 */
resave: false,
/* 세션에 저장할 내역이 없더라도 세션을 저장할지에 대한 설정. 방문자를 추적할 때 사용된다. */
saveUninitialized: false,
/* cookie-parser의 비밀키와 같은 역할 */
secret: 'secret code',
/*
세션관리 시 클라이언트에 쿠키를 보내는데 이를 세션쿠키라 부른다.
안전하게 쿠키를 전송하려면 쿠키에서명을 추가해야 하는데 쿠키를 서명하는데 secret의 값이 필요하다.
cookie-parser의 secret과 같게 설정해야한다.
아래의 cookie 옵션은 세션 쿠키에 대한 설정으로
maxAge, domain, path, expires, sameSite, httpOnly, secure 등 일반적인 쿠키 옵션이 모두 제공된다.
store 라는 옵션도 있는데 세션을 데이터베이스에 저장할 수 있게 해준다. 데이터베이스는 보통 레디스가 자주 쓰인다.
*/
cookie: {
httpOnly: true,
secure: false,
},
}));
- connect-flash -
일회성 메시지들을 웹 브라우저에 나타낼때 좋으며 express-generator로 설치되지 않으므로 npm을 통해 별도로 설치해야한다.
connect-flash 미들웨어는 cookie-parser 와 express-session을 사용하므로 이들보다 뒤에 위치해야한다.
var flash = require('connect-flash');
app.use(flash());
flash 미들웨어는 res 객체에 req.flash 메서드를 추가하며 req.flash(키, 값)으로 해당 키에 값을 설정하고 불러온다.
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
router.get('/flash', function(req, res) {
req.session.message = '세션 메시지';
req.flash('message', 'flash 메시지');
res.redirect('/users/flash/result');
});
router.get('/flash/result', function(req, res) {
res.send(`${req.session.message} ${req.flash('message')}`);
});
module.exports = router;
/users/flash 라우터로 GET 요청을 보내면 서버에서는 세션과 flash에 미시지를 설정하고 /users/flash/result 메시지로 리다이렉트 한다. 첫 번째 /users/flash/result에는 세션 메시지와 flash 메시지가 모두 보인다.
해당 메시지는 일회성이기 때문에 새로고침시 보이지 않는다. 이러한 성질을 이용해서 로그인 에러나 회원가입 에러 같은 일회성 경고 메시지를 보낼수 있다.
- Router -
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
app.use('/', indexRouter);
app.use('/users', usersRouter);
라우팅 미들웨어는 첫 번째 인자로 주소를 받아서 특정 주소에 해당하는 요청이 왔을때만 미들웨어가 동작하게 할 수도 있다. 주소가 /로 시작하면 routes/index.js를, /users로 시작하면 routes/users.js를 호출하라는 의미이다.
use 메서드는 모든 HTTP 메서드에 대해 요청 주소만 일치하면 실행되지만 get, post, put, patch, delete 같은 메서드는 주소뿐만 아니라 HTTP 메서드까지 일치하는 요청일 때만 실행된다.
/* HTTP 메서드는 상관없이 / 주소요청일 때 실행 */
app.use('/', function(req, res, next) { next(); }
/* GET 메서드 / 주소의 요청일 때 실행 */
app.get('/', function(req, res, next) { next(); }
/* POST 메서드 /data 주소의 요청일 때 실행 */
app.post('/', function(req, res, next) { next(); }
app.use 처럼 router 하나에 미들웨어를 여러 개 장착할 수도 있다. 라우터 로직이 실행되는 미들웨어 전에 로그인 여부 또는 관리자 여부를 체크하는 미들웨어를 중간에 넣을수있다.