- 익스프레스는 대표적인 웹 서버 프레임워크이다.
- npm init 명령어를 콘솔에서 호출하여 package.json을 제일 먼저 생성해야 한다.
// package.json
{
"name": "learn-express",
"version": "0.0.1",
"description": "익스프레스를 배우자",
"main": "app.js",
"scripts": {
"start": "nodemon app"
},
"author": "ZeroCho",
"license": "MIT"
}
// 콘솔
$ npm i express
$ npm i -D nodemon
- scripts 부분에 start 속성을 넣어줘야 한다.
- nodemon app은 app.js를 nodemon으로 실행한다는 뜻이다. 서버 코드를 수정하면 nodemon이 서버를 자동으로 재시작한다.
// app.js
const express = require('express');
const app = express();
app.set('port', process.env.PORT || 3000);
app.get('/', (req, res) => {
res.send('Hello, Express');
});
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기 중');
});
- Express 모듈을 실행해 app 변수에 할당한다.
- 익스프레스 내부에는 http 모듈이 내장되어 있으므로 서버의 역할을 할 수 있다.
- app.set('port', 포트)로 서버가 실행될 포트를 설정한다.
- app.set(키, 값)을 사용해서 데이터를 저장할 수 있으며, app.get(키)로 데이터를 가져올 수 있다.
- app.get(주소, 라우터)는 주소에 대한 GET 요청이 올 때 어떤 동작을 할지 정한다.
- req는 요청에 관한 정보가 들어 있고, res는 응답에 관한 정보가 들어 있다.
- 익스프레스에서는 res.write나 res.end 대신 res.send를 사용한다.
// 콘솔
$ npm start
- http://localhost:3000으로 접속할 수 있다.
- 문자열 대신 HTML로 응답하고 싶다면 res.sendFile 메서드를 사용한다.
// app.js
const express = require('express');
const path = require('path');
const app = express();
app.set('port', process.env.PORT || 3000);
app.get('/', (req, res) => {
// res.send('Hello, Express');
res.sendFile(path.join(__dirname, '/index.html'));
});
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기 중');
});
- 미들웨어(middleware)는 요청과 응답의 중간(middle)에 위치한다.
- 미들웨어는 app.use(미들웨어)의 형태로 사용된다.
// app.js
...
app.set('port', process.env.PORT || 3000);
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'), () => {
...
- app.use에 매개변수가 req, res, next인 함수를 넣는다.
- next는 다음 미들웨어로 넘어가는 함수이다. next를 실행하지 않으면 다음 미들웨어가 실행되지 않는다.
- 주소를 첫 번째 인수로 넣어주지 않는다면 미들웨어는 모든 요청에서 실행된다.
- 위 코드에서 app.get('/')의 두 번째 미들웨어에서 에러가 발생하고, 에러는 그 아래에 있는 에러 처리 미들웨어에 전달된다.
- 에러 처리 미들웨어는 매개변수가 err, req, res, next로 반드시 네 개이다. err에는 에러에 관한 정보가 담겨 있다.
- res.status 메서드로 HTTP 상태 코드를 지정하며, 기본값은 200(성공)이다.
// 콘솔
$ npm i morgan cookie-parser express-session dotenv
- morgan, cookie-parser, express-session, dotenv는 자주 사용되는 패키지이다.
// app.js
const express = require('express');
const morgan = require('morgan');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const dotenv = require('dotenv');
const path = require('path');
dotenv.config();
const app = express();
app.set('port', process.env.PORT || 3000);
app.use(morgan('dev'));
app.use('/', express.static(path.join(__dirname, 'public')));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
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();
});
...
// .env
COOKIE_SECRET=cookiesecret
- app.js를 수정하고 .env 파일을 생성한다.
- 패키지들을 불러와 app.use에 연결한다. req, res, next는 미들웨어 내부에 들어 있으며, next는 내부적으로 호출된다.
- dotenv 패키지는 .env 파일을 읽어서 process.env로 만든다.
- process.env.COOKIE_SECRET에 cookiesecret 값이 할당된다.
- 비밀 키는 별도의 파일 .env에 비밀 키를 적어두고 dotenv 패키지로 비밀 키를 로딩하는 방식으로 관리한다.
1. morgan
- morgan 미들웨어는 요청과 응답에 대한 정보를 콘솔에 기록한다.
- morgan 미들웨어는 app.use(morgan('dev')); 와 같이 사용한다.
- GET / 500 7.409 ms - 50와 같은 정보가 출력되는데, 각각 [HTTP 메서드] [주소] [HTTP 상태 코드] [응답 속도] - [응답 바이트]를 의미한다.
2. static
- static 미들웨어는 정적인 파일들을 제공하는 라우터 역할을 한다.
- 기본적으로 제공되므로 express 객체에서 꺼내어 app.use('요청 경로', express.static('실제 경로'));와 같이 사용한다.
3. body-parser
- 요청의 본문에 있는 데이터를 해석해서 req.body 객체로 만드는 미들웨어이다. 보통 폼 데이터나 AJAX 요청의 데이터를 처리한다.
- json형식의 데이터를 해석할 때, app.use(express.json());와 같이 사용한다.
4. cookie-parser
- 요청에 동봉된 쿠키를 해석해 req.cookies 객체로 만드는 미들웨어이다.
- app.use(cookieParser(비밀 키));와 같이 사용한다. 비밀 키를 통해 해당 쿠키가 내 서버에서 만든 쿠키임을 검증한다.
- 쿠키는 클라이언트에서 위조하기 쉬우므로 비밀 키를 통해 만들어낸 서명을 쿠키 값 뒤에 붙인다. 서명된 쿠키는 req.signedCookies 객체에 들어있다.
- 쿠키를 생성할 때 res.cookie(키, 값, 옵션) 메서드를, 쿠키를 제거할 때 res.clearCookie 메서드를 사용한다.
5. express-session
- 세션 관리용 미들웨어이다. 세션은 사용자별로 req.session 객체 안에 유지된다.
- express-session은 인수로 세션에 대한 설정 객체를 받는다.
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
- 세션 관리 시 클라이언트에 쿠키를 보낸다.
- 쿠키를 서명하는 데 secret의 값이 필요하며, secret: process.env.COOKIE_SECRET, 으로 cookie-parser의 secret과 같게 설정하는 것이 좋다.
- cookie 옵션은 세션 쿠키에 대한 설정이다.
req.session.name = 'zerocho'; // 세션 등록
req.sessionID; // 세션 아이디 확인
req.session.destroy(); // 세션 모두 제거
- express-session으로 만들어진 req.session 객체에 값을 대입하거나 삭제해서 세션을 변경할 수 있다.
6. 미들웨어의 특성 활용하기
- 미들웨어는 req, res, next를 매개변수로 갖는 함수로서 app.use나 app.get, app.post 등으로 장착한다.
- 다음 미들웨어로 넘어가려면 next 함수를 호출해야 한다. 내부적으로 next를 호출하는 미들웨어들도 있다.
- express.static과 같은 미들웨어는 정적 파일을 제공할 때 next 대신 res.sendFile 메서드로 응답을 보낸다.
- 미들웨어 안에 미들웨어를 넣는 방식으로 조건문에 따라 다른 미들웨어를 적용할 수도 있다.
7. 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>
const multer = require('multer');
const upload = multer({
storage: multer.diskStorage({
destination(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);
},
}),
limits: { fileSize: 5 * 1024 * 1024 },
});
- multer 함수의 인수로 설정을 넣는다.
- 위 코드에서 storage 속성에는 어디에(destination) 어떤 이름으로(filename) 저장할지를 넣었다.
- req 매개변수에는 요청에 대한 정보가, file 객체에는 업로드한 파일에 대한 정보가 있다.
- req나 file의 데이터를 가공해서 done 함수로 넘긴다.
- 위 설정으로는 uploads라는 폴더에 [파일명+현재시간.확장자] 파일명으로 업로드한다.
- limits 속성에는 업로드에 대한 제한 사항을 설정할 수 있다.
- 위 설정을 적용하려면 서버에 uploads 폴더가 존재해야 한다.
- 설정이 끝나면 upload 변수가 생기는데, 여기에 다양한 종류의 미들웨어가 들어 있다.
- 파일을 하나만 업로드하는 경우에는 single 미들웨어를 사용한다. 업로드 결과는 req.file 객체 안에 들어 있다.
- 여러 파일을 업로드하는 경우 HTML의 input 태그에는 multiple을 쓰고, 미들웨어는 array를 사용한다. 업로드 결과는 req.files 배열에 들어 있다.
// app.js
...
const path = require('path');
dotenv.config();
const indexRouter = require('./routes');
const userRouter = require('./routes/user');
...
name: 'session-cookie',
}));
app.use('/', indexRouter);
app.use('/user', userRouter);
app.use((req, res, next) => {
res.status(404).send('Not Found');
});
app.use((err, req, res, next) => {
...
- 위 코드는 index.js와 user.js를 app.use를 통해 app.js에 연결한다. ( index.js는 생략할 수 있다.)
- 에러 처리 미들웨어 위에 404 상태 코드를 응답하는 미들웨어를 추가한다.
// 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;
- /user/:id 와 같은 라우트 매개변수를 사용하여 /users/1이나 /users/123 등의 요청도 처리할 수 있다.
- req.app: app 객체에 접근할 수 있다. req.app.get('port')와 같은 식으로 사용할 수 있다.
- req.body: body-parser 미들웨어가 만드는 요청의 본문을 해석한 객체이다.
- req.cookies: cookie-parser 미들웨어가 만드는 요청의 쿠키를 해석한 객체이다.
- req.ip: 요청의 ip 주소가 담겨 있다.
- req.params: 라우트 매개변수에 대한 정보가 담긴 객체이다.
- req.query: 쿼리스트링에 대한 정보가 담긴 객체이다.
- req.signedCookies: 서명된 쿠키들이 담겨 있다.
- req.get(헤더 이름): 헤더의 값을 가져오고 싶을 때 사용하는 메서드이다.
- res.app: app 객체에 접근할 수 있다.
- res.cookie(키, 값, 옵션): 쿠키를 설정하는 메서드이다.
- res.clearCookie(키, 값, 옵션): 쿠키를 제거하는 메서드이다.
- res.end(): 데이터 없이 응답을 보낸다.
- res.json(JSON): JSON 형식의 응답을 보낸다.
- res.locals: 하나의 요청 안에서 미들웨어 간에 데이터를 전달하고 싶을 때 사용하는 객체이다.
- res.redirect(주소): 리다이렉트할 주소와 함께 응답을 보낸다.
- res.render(뷰, 데이터): 템플릿 엔진을 렌더링해서 응답할 때 사용하는 메서드이다.
- res.send(데이터): 데이터와 함께 응답을 보낸다.
- res.sendFile(경로): 경로에 위치한 파일을 응답한다.
- res.set(헤더, 값): 응답의 헤더를 설정한다.
- res.status(코드): 응답 시의 HTTP 상태 코드를 지정한다.
1. 퍼그(제이드)
- 익스프레스와 연결하려면 app.js에 아래 코드가 들어 있어야 한다.
// app.js
...
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(morgan('dev'));
...
- views는 템플릿 파일들이 위치한 폴더를 지정한다. (res.render('index')는 views/index.pug를 렌더링한다.)
// 퍼그
HTML
doctype html
html
head
title= title
link(rel='stylesheet', href='/stylesheets/style.css')
// HTML
<!DOCTYPE html>
<html>
<head>
<title>익스프레스</title>
<link rel="stylesheet" href="/style.css" />
</head>
</html>
- 퍼그와 HTML 문법으로 작성한 위의 두 코드는 일치한다.
- 변수는 태그나 속성 뒤에 =을 붙이거나 #{변수}으로 참조한다.
- each 또는 for을 사용하여 반복문을 작성할 수 있다.
- if, else if, else를 사용하여 조건문을 작성할 수 있다.
- include 파일 경로의 형태로 다른 pug나 HTML 파일을 넣을 수 있다.
2. 넌적스
- HTML 문법을 그대로 사용하되, 추가로 자바스크립트 문법을 사용할 수 있다.
- 넌적스에서 변수는 {{ }}로 감싼다. 변수를 선언할 때는 {% set 변수 = '값' %}를 사용한다.
- 반복문은 {% %} 안에 쓰며, for in문과 endfor 사이에 위치시킨다.
- 조건문은 {% if 변수 %} {% elif %} {% else %} {% endif %}를 사용한다.
- {% include 파일 경로 %}의 형태로 다른 HTML 파일을 넣을 수 있다.
3. 에러 처리 미들웨어
// app.js
...
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(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');
});
...
- 404 에러가 발생하면 res.locals.message는 ‘${req.method} ${req.url} 라우터가 없습니다.’가 된다.
- 에러 처리 미들웨어는 error라는 템플릿 파일을 렌더링한다. 서버를 실행하고 접속하면 에러 메시지를 확인할 수 있다.
빈칸 채우기 문제 - (빈칸을 드래그해서 답을 확인해 보세요)
1. ( app.set('port', 포트) ) 메서드로 서버가 실행될 포트를 설정한다.
2. ( res.sendFile ) 메서드를 사용해 단순한 문자열 대신 HTML로 응답할 수 있다.
3. 에러 처리 미들웨어는 매개변수가 ( err ), ( req ), ( res ), ( next )로 네 개이다.
4. cookie-parser는 요청에 동봉된 쿠키를 해석해 ( req.cookies ) 객체로 만든다.
5. express-session 미들웨어를 이용하면 세션은 사용자별로 ( req.session ) 객체 안에 유지된다.
6. 미들웨어는 ( app.use )나 ( app.get ), ( app.post )등으로 장착한다.
7. 대표적인 템플릿 엔진으로는 ( 퍼그 )와 ( 넌적스 )가 있다.
코드 문제
1. 주소 '/'에 대한 GET 요청이 올 때 'Hello World'라는 문자열을 응답하는 코드를 작성하시오.
const express = require('express');
const app = express();
app.set('port', process.env.PORT || 3000);
// 여기에 코드를 작성
app.listen(app.get('port'), () => {
console.log(app.get('port'), '번 포트에서 대기 중');
});
답 :
app.get('/', (req, res) => {
res.send(' Hello World');
});
(더보기로 확인)
2. 쿠키에 서명을 추가하기 위한 secret의 값을 작성하시오. ( cookie-parser의 secret과 값이 같도록 설정하시오.)
app.use(session({
resave: false,
saveUninitialized: false,
secret: /* 여기에 코드를 작성 */,
cookie: {
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
답:
process.env.COOKIE_SECRET
(더보기로 확인)
출처: 조현영 , 『Node.js 교과서』 개정판 3판, 길벗, 6.1장 ~ 6.6장
[노드 1팀] 8장. 몽고디비 (1) | 2023.12.01 |
---|---|
[Node.js 1] 7장 MySQL (1) | 2023.11.24 |
[노드1] 4장~5장 http 모듈로 서버 만들기 & 패키지 매니저 (0) | 2023.11.10 |
[Node.js 1] 노드 기능 알아보기 (2) (1) | 2023.11.03 |
[Node.js 1] 1장 ~ 3장 노드 시작하기 (0) | 2023.10.13 |