* 위 내용을 정리하였음
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',
}));
const multer = require('multer');
const fs = require('fs');
try {
fs.readdirSync('uploads');
} catch (error) {
console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
fs.mkdirSync('uploads');
}
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 },
});
app.get('/upload', (req, res) => {
res.sendFile(path.join(__dirname, 'multipart.html'));
});
app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file);
res.send('ok');
});
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'), '번 포트에서 대기 중');
});
6.2.1 morgan
- morgan을 사용하면 요청과 응답에 대한 정보를 콘솔에 기록한다.
app.use(morgan('dev'));
- 인수로 dev외에 combined, common, short, tiny 등을 사용할 수 있고 그에 따라 로그가 달라진다.
-[HTTP 메서드] [주소] [HTTP 상태 코드] [응답 속도] - [응답 바이트] 를 의미한다.
6.2.2 static
- 정적인 파일을 제공하는 라우터 역할을 한다.
- 기본으로 제공되기 때문에 따로 설치할 필요 없이 express객체에서 꺼내 쓴다.
app.use('요청 경로', express.static('실제 경로'));
app.use('/', express.static(path.join(__dirname, 'public')));
- 함수의 인수로 정적 파일들이 담긴 폴더(이름)를 지정한다.
- 위 예시는 public이라는 폴더에 정적 파일들이 "public/stylesheets/style.css" 이렇게 담겨 있는 것이다.
- public 폴더 안에 css, js, 이미지 파일들을 넣으면 http://localhost:3000/stylesheets/style.css처럼 주소로 브라우저에서 접근할 수 있는데
- static을 사용하면 요청주소가 '/'임에도 'public'으로 알아서 연결해준다. (서버의 구조를 숨길 수 있다, 보안)
- 이렇게 static으로 경로를 지정하면 정적 파일을 알아서 제공하므로 fs.readFile로 파일을 읽고 보내줄 필요가 없다.
- public 저 경로에 해당하는 파일이 없으면 내부적으로 next를 호출해서 다음 미들웨어를 수행한다. 반대로 원하는 파일을 찾으면 당연히 next를 호출하지 않는다.
6.2.3 body-parser
- 요청 본문에 있는 데이터를 해석해서 req.body 객체로 만들어준다.
- AJAX요청의 데이터를 주로 처리할때 사용한다.
- 익스프레스에 body-parser 미들웨어의 일부 기능이 내장되어 있다.
- 단 요청의 내용이 RAW(버퍼 데이터)거나 Text(문자)일때는 body-parser를 설치하여야 한다.
- 멀티파트(이미지, 동영상, 파일) 데이터는 처리하지 못한다. (multer모듈 사용)
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// raw, text 사용하려면
const bodyParser = require('body-parser');
app.use(bodyParser.raw());
app.use(bodyParser.text());
- 서버에 요청을 할때 데이터를 함께 전달한다면 그리고 그 데이터가 JSON 형식이라면 알아서 req.body에 들어간다.
- URL-encoded는 주소 형식으로 데이터를 보내는 방식이다.
- { extended: false } : 노드의 querystring 모듈을 사용하여 쿼리스트링을 해석
- { extended: true } : qs 모듈을 사용하여 쿼리스트링을 해석. qs 모듈은 내장 모듈이 아니다. querystring 모듈의 기능을 좀 더 확장한 모듈이다.
- 이전 4.2절에서 POST와 PUT 요청의 본문을 전달받으려면 req.on('data')와 req.on('end')로 스트림을 사용했었는데 body-parser가 내부적으로 스트림을 처리해 req.body에 추가한다.
- JSON 형식으로 { name: 'zerocho', book: 'nodejs' }를 본문으로 보낸다면 req.body에 그대로 들어간다.
- URL-encoded 형식으로 name=zerocho&book=nodejs를 본문으로 보낸다면 req.body에 { name: 'zerocho', book: 'nodejs' }가 들어간다.
6.2.4 cookie-parser
- 요청에 동봉된 쿠키를 해석해 req.cookies 객체로 만든다.
app.use(cookieParser(비밀키));
- name=zerocho 쿠키를 보냈다면 req.cookies는 { name: 'zerocho' }가 됩니다. 유효 기간이 지난 쿠키는 알아서 걸러낸다.
- 비밀키를 인수로 넣어주면 쿠키가 내 서버가 만든 쿠키인지 검증할 수 있다.
- 비밀 키로 서명을 만들어 쿠키 값 뒤에 붙여주면 name=zerocho.sign과 같은 모양이 된다. 서명된 쿠키는 req.signedCookies 객체에 들어있다.
- 쿠키 생성/제거를 위해서는 아래 메서드를 사용한다.
res.cookie('name', 'zerocho', {
expires: new Date(Date.now() + 900000),
httpOnly: true,
secure: true,
});
res.clearCookie('name', 'zerocho', { httpOnly: true, secure: true });
- 쿠키를 지우려면, 키와 값 외에 옵션도 정확히 일치해야한다.( expires, maxAge 옵션은 일치할 필요가 없음)
- 옵션중 signed 옵션을 true로 설정해서 쿠키 뒤에 서명이 붙도록 한다.
- 내 서버가 쿠키를 만들었다는 것을 검증할 수 있다.
- 서명을 위한 비밀 키는 cookieParser 미들웨어에 인수로 넣은 process.env.COOKIE_SECRET이 이 된다.
6.2.5 express-session
- 세션 관리용 미들웨어.
- 로그인 구현, 특정 사용자를 위한 데이터를 임시 저장 할때 유용.
- 세션은 사용자 별로 req.session 객체에 유지된다.
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
name: 'session-cookie',
}));
- express-session은 인수로 세션에 대한 설정을 받는다.
- resave : 요청이 올 때 세션에 수정 사항이 생기지 않더라도 세션을 다시 저장할지 설정
- saveUninitialized : 세션에 저장할 내역이 없더라도 처음부터 세션을 생성할지 설정
- express-session은 세션 관리 시 클라이언트에 쿠키를 보낸다. 안전하게 쿠키에 서명을 하기 위해 secret값을 사용한다.
- name : 세션 쿠키의 이름
- cookie : 세션 쿠키에 대한 설정.
- httpOnly를 true로 설정해 클라이언트에서 쿠키를 확인하지 못하도록 한다.
- secure는 false로 해서 https가 아닌 환경에서도 사용할 수 있게 한다.
- 배포 시에는 https를 적용하고 secure도 true로 설정하는 것이 좋다.
req.session.name = 'zerocho'; // 세션 등록
req.sessionID; // 세션 아이디 확인
req.session.destroy(); // 세션 모두 제거
- express-session으로 만들어진 req.session 객체에 값을 대입하거나 삭제해서 세션을 변경할 수 있다.
- express-session에서 서명한 쿠키 앞에는 s:이 붙는다. 실제로는 encodeURIComponent 함수가 실행되어 s%3A가 됩니다.
- s%3A의 뒷부분이 실제 암호화된 쿠키 내용이다. 앞에 s%3A가 붙은 경우, 이 쿠키가 express-session 미들웨어에 의해 암호화된 것이라고 생각하면 된다.
6.2.6 미들웨어 특성 활용하기
app.use((req, res, next) => {
console.log('모든 요청에 다 실행됩니다.');
next();
});
- 에러 처리 미들웨어를 제외하고는 req, res, next를 갖는 함수여야 한다.
- app.use, app.post, app.get 등 에 넣어서 사용한다.
app.use(
morgan('dev'),
express.static('/', path.join(__dirname, 'public')),
express.json(),
express.urlencoded({ extended: false }),
cookieParser(process.env.COOKIE_SECRET),
);
- 이렇게 여러개의 미들웨어를 한번에 장착해도 된다.
- 미들웨어 내부에 next함수를 호출해야 다음 미들웨어로 넘어간다.
- static처럼 next를 호출하지 않는 미들웨어는 res.send, res.sendFile등으로 응답을 보내준다. 따라서 static 이후에는 다음 미들웨어가 실행되지 않는다.
- 미들웨어 장착 순서에 따라 동작이 달라질 수 있다.
- 따라서 next도 호출하지 않고 응답도 보내지 않으면 클라이언트는 응답을 받지 못해 하염없이 기다리게 된다.
- next에 'route'를 인자로 넣어주면 다음 라우터로 이동한다.
- 인자가 없으면 다음 미들웨어로 이동한다.
- 그 외의 경우는 에러 처리 미들웨어로 들어간다.
app.use((req, res, next) => {
req.data = '데이터 넣기';
next();
}, (req, res, next) => {
console.log(req.data); // 데이터 받기
next();
});
- 이렇게 미들웨어간에 데이터를 넘겨줄 수 있다.
- req(요청)가 끝나기 전까지 '데이터 넣기'가 유지된다.
- req 객체는 요청을 보낸 사용자 개개인에게 귀속되므로 req 객체를 통해 개인의 데이터를 전달하는 것이 좋다.
app.use(morgan('dev'));
// 또는
app.use((req, res, next) => {
morgan('dev')(req, res, next);
});
- 미들웨어 안에 미들웨어를 넣을 수 있다.
- 이런 패턴은 자주 사용되는 아래 처럼 기존 미들웨어를 확장할 수 있기 때문이다.
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production') {
morgan('combined')(req, res, next);
} else {
morgan('dev')(req, res, next);
}
});
6.2.7 multer
- 이미지, 동영상 등의 파일들을 멀티파트 형식으로 업로드할 때 사용하는 미들웨어이다.
- 멀티파트 형식이란 다음과 같이 enctype이 multipart/form-data인 <from>을 통해 업로드하는 데이터의 형식을 의미한다.
- 아래 .html파일이 있다면 multipart형식으로 데이터를 업로드할 수 있다.
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
- 이렇게 form을 통해 업로드 되는 파일은 body-parser로 처리할 수 없기 때문에 multer를 사용한다.
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 },
});
- storage 속성에 어디에(destination) 어떤 이름으로(filename) 저장할지를 정해준다.
- req에는 요청에 대한 정보가, file에는 업로드한 파일에 대한 정보가 있다.
- done은 함수로 첫 번째 인수에는 에러가 있다면 에러를 넣고, 두 번째 인수에는 실제 경로나 파일 이름을 넣어준다. 즉 req, file의 데이터를 가공해서 done으로 넘기는 것이다.
- limits 속성에는 업로드에 대한 제한 사항을 설정할 수 있다. fileSize를 5MB로 제한하였다.
- 위의 코드 예시를 사용하기 위해서는 서버에 uploads 폴더가 꼭 존재해야 한다. 서버에 없다면 아래 코드로 꼭 만들도록 한다.
const fs = require('fs');
try {
fs.readdirSync('uploads');
} catch (error) {
console.error('uploads 폴더가 없어 uploads 폴더를 생성합니다.');
fs.mkdirSync('uploads');
}
- 파일을 하나만 업로드하는 경우(multipart 경우)에는 single 미들웨어를 사용한다.
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image" />
<input type="text" name="title" />
<button type="submit">업로드</button>
</form>
app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file, req.body);
res.send('ok');
});
- single 미들웨어의 인수는 form데이터의 키나 input 태그의 name과 일치하게 넣으면 된다. 따라서 'image'라고 넣었다.
- 업로드 성공 시 결과는 req.file 객체 안에 들어 있다.
- req.body에는 파일이 아닌 데이터인 title이 들어 있다.
- req.file 객체는 다음과 같이 생겼다.
{
fieldname: 'img',
originalname: 'nodejs.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: 'nodejs1514197844339.png',
path: 'uploads\\nodejs1514197844339.png',
size: 53357
}
- 여러 파일을 업로드하는 경우 HTML의 input 태그에는 multiple을 쓰면 된다.
<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');
});
- 미들웨어에는 single대신 array를 쓴다.
- 업로드 결과도 req.file 대신 req.files 배열에 들어 있다.
- 파일을 여러 개 업로드하지만 input 태그나 폼 데이터의 키가 다른 경우에는 fields 미들웨어를 사용한다.
<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>
- fields 미들웨어의 인수로 input 태그의 name을 각각 적는다.
app.post('/upload',
upload.fields([{ name: 'image1' }, { name: 'image2' }]),
(req, res) => {
console.log(req.files, req.body);
res.send('ok');
},
);
- 업로드 결과도 req.files.image1, req.files.image2에 각각 들어 있다.
- 특수한 경우지만, 파일을 업로드하지 않고도 멀티파트 형식으로 업로드하는 경우 none 미들웨어를 사용한다.
<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');
});
- 파일을 업로드하지 않았으므로 req.body만 존재한다.
'ComputerScience > NodeJs' 카테고리의 다른 글
node - 6.5 템플릿 엔진 사용하기(pug) (0) | 2022.01.28 |
---|---|
node - 6.3 Router 객체로 라우팅 분리 ~ 6.4 req, res 객체 (0) | 2022.01.25 |
node - 6 Express로 웹 서버 만들기 (0) | 2022.01.22 |
node - 5 Package Manager (0) | 2022.01.15 |
node - 4 http 모듈로 서버 만들기 (0) | 2022.01.12 |