로그의 자세한 정도 선택 가능(dev, tiny, short, common, combined)
개발 환경에서는 dev
, 배포환경에서는 combined
를 애용
- combined는 정확한 시간까지 나옴
const morgan = require('morgan');
app.use(morgan('dev'));
HTTP요청 요청주소 상태코드 응답속도 - 응답바이트
parseCookies
함수와 기능 비슷res.cookies
안에 쿠키들이 들어있음 const cookieParser = require('cookie-parser');
app.use(cookieParser(process.env.COOKIE_SECRET));
app.get('/', (req, res, next) => {
//쿠키 설정
res.cookie('name', encodeURIComponent(name), {
expires: new Date(),
httpOnly:true,
path: '/',
})
//쿠키 지우기
res.clearCookie('name', encodeURIComponent(name),
httpOnly:true,
path: '/',
})
res.sendFile(path.join(__dirname, 'index.html'));
});
writeHead
문자열로 쭉 쓰는 것 보다 method
사용해 더 편리app.use(cookieParser('암호화키'));
app.get('/', (req, res, next) => {
res.signedCookies;
...
signedCookies
라고 써 주어야 함json
미들웨어는 요청 본문이 json
인 경우 해석, urlencoded
미들웨어는 폼 요청 해석put
이나 patch
, post
요청 시에 req.body
에 프런트에서 온 데이터를 넣어 줌qs
가 querystring
보다 훨씬 강력하니까 true
로 쓰는 것을 추천 👍app.use(express.json());
app.use(express.urlencoded({ extended: false }));
//true면 qs모듈사용, false이면 querystring모듈 사용
// 요청의 body를 다 받은 후 실행됨
return req.on('end', () => {
console.log('POST 본문(Body):', body);
const { name } = JSON.parse(body);
const id = Date.now();
users[id] = name;
res.writeHead(201, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('ok');
});
app.get('/', (req, res, next) => {
req.body.name ~~~
//그냥 이렇게 접근해서 사용 가능함!
});
app.use(bodyParser.raw()); //바이너리 데이터
app.use(bodyParser.text()); //문자열
app.use('요청경로', express.static('실제경로');
를 통해 경로 유출 방지 (보안에 도움)app.use('/', express.static(path.join(__dirname, 'public-3030')); //예시
next
를 실행하는데 static
은 파일을 못 찾으면 종료되어야 하기 때문에 morgan
아래에 써 주는 것이 좋음app.use(morgan('dev'));
app.use('/', express.static(path.join(__dirname, 'public'))); //요기!
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true, //true해야 js공격 안당함
secure: false,
},
name: 'connect.sid', //기본값은 이거임
}));
req.session.name = 'seohyun'; //세션등록
req.sessionID; //세션 아이디 확인
req.session.destroy(); //세션 모두 제거
resave
: 요청이 왔을 때 세션에 수정 사항이 생기지 않아도 다시 저장할 지 여부saveUninitialized
: 세션에 저장할 내역이 없더라도 세션을 저장할지req.session.save
로 수동 저장도 가능하지만 할 일 거의 없음req
나 res
객체 안에 값을 넣어 데이터 전달 가능
app.use((req, res, next) => {
req.data = '데이터 넣기';
next();
}, (req, res, next) => {
console.log(req.data); //데이터 받기
next();
});
app.set
과의 차이점: app.set
은 서버 내내 유지 / req
, res
는 요청 하나 동안만 유지req.body
나 req.cookies
같은 미들웨어의 데이터와 겹치지 않게 조심주의 사항 🚨
let password;
app.use((req, res, next) => {
password = '알려지면 안되는 비밀번호';
});
app.get('/', (req, res, next) => {
console.log(password); // 안돼!!!
res.sendFile(path.join(__dirname, 'index/html'));
});
app.use((req, res, next) => {
app.set('password', '알려지면 안되는 비밀번호');
});
app.get('/', (req, res, next) => {
app.get('password'); // 진쨔로 큰일남...!
res.sendFile(path.join(__dirname, 'index/html'));
});
req.data
app.use((req, res, next) => {
req.data = '비밀번호';
});
app.get('/', (req, res, next) => {
req.data //비밀번호
res.sendFile(path.join(__dirname, 'index/html'));
});
req.session
app.use((req, res, next) => {
req.session.data = '비밀번호';
});
app.get('/', (req, res, next) => {
req.session.data // 비번
res.sendFile(path.join(__dirname, 'index/html'));
});
app.use('/', (req, res, next) => {
if(req.session.id){
express.static(__dirname, 'public') (req, res, next)
}else {
next();
}
});
(req, res, next)
만 붙여주면 됨enctype
이 multipart/form-data
인 경우multer
패키지 필요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);
//데이터가 덮어씌워지는 것을 막고자 현재 시간 date 넣음
},
}),
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); // 이미지 1개일때 req.file로 씀
res.send('ok');
});
multer
함수 호출storage
는 저장할 공간에 대한 정보 (업로드한 파일 어디에 저장할 거냐~)diskStorage
는 하드디스크에 업로드 파일을 저장한다는 것destination
은 저장할 경로filename
은 저장할 파일명 (파일명+날짜+확장자 형식)Linits
는 파일 개수나 파일 사이즈를 제한할 수 있음single
: 하나의 파일을 업로드 할 때app.post('/upload', upload.single('image'), (req, res) => {
console.log(req.file); // 이미지 1개일때 req.file로 씀
res.send('ok');
});
none
: 파일을 업로드 하지 않을 때 (이미지는 없는데 enctype이 form-data일 때)array
는 하나의 요청 body 이름 아래 여러 파일이 있는 경우app.post('/upload', upload.array('many'), (req, res) => {
console.log(req.files, req.body);
res.send('ok');
});
fields
는 여러 개의 요청 body이름 아래 파일이 하나씩 있는 경우app.post('/upload',
upload.fields([{ name : 'image1'}, { name : 'image2'}]), (req, res) => {
console.log(req.files, req.body);
res.send('ok');
});
req.file
안에 업로드 정보 저장 미들웨어는 아니지만 비밀 키 같은 것들을 관리할 수 있음
사용 예제
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',
}));
COOKIE_SECRET=mycookiesecret
DB_PASSWORD=mydbpw
process.env.COOKIE_SECRET
에 cookiesecret 값이 할당됨 (키=값 형식)최대한 위에서 dotenv 파일을 불러 오는 게 좋음
const dotenv = require('dotenv');
dotenv.config();
소스 코드에는 절대 비밀 key를 넣지마라. ⇒ dotenv 같은 것을 사용해라
2차 적으로 개개인 별로 권한을 다르게 해서 접근 가능한 범위를 나눠라
app.js 가 길어지는 것을 막을 수 있음
사용예시
const indexRouter = require('./routes');
const userRouter = require('./routes/user');
app.use('/', indexRouter);
app.use('/user', userRouter);
const express = require('express');
const router = express.Router();
// GET / 라우터
router.get('/', (req, res) => {
res.send('Hello, Express');
});
module.exports = router;
const express = require('express');
const router = express.Router();
// GET /user 라우터
router.get('/', (req, res) => {
res.send('Hello, User');
});
module.exports = router;
GET/user/
가 됨:id
를 넣으면 req.params.id
로 받을 수 있음router.get('/user/:id', function(req, res) => {
console.log(req.params, req.query);
});
router.get('/user/:id', function(req, res) => {
console.log("얘만 실행됨");
});
router.get('/user/like', function(req, res) => {
console.log("전혀 실행되지 X");
});
/users/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');
});
⇒ router.route
로 묶음
req.app
: req 객체를 통해 app 객체에 접근할 수 있습니다.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
: req.app처럼 res 객체를 통해 app 객체에 접근할 수 있음res.cookie(키, 값, 옵션)
: 쿠키를 설정하는 메서드res.clearCookie(키, 값, 옵션)
: 쿠키를 제거하는 메서드res.end()
: 데이터 없이 응답을 보냄res.json(JSON)
: JSON 형식의 응답을 보냄res.redirect(주소)
: 리다이렉트할 주소와 함께 응답을 보냄res.writeHead(302, {
Location: '/',
'Set-Cookie': `session=${uniqueInt}; Expires=${expires.toGMTString()}; HttpOnly; Path=/`,
});
res.status(302).redirect('/');
res.render(뷰, 데이터)
: 템플릿 엔진을 렌더링해서 응답할 때 사용하는 메서드res.send(데이터)
: 데이터와 함께 응답을 보냅니다.res.sendFile(경로)
: 경로에 위치한 파일을 응답res.setHeader(헤더, 값)
: 응답의 헤더를 설정res.status(코드)
: 응답 시의 HTTP 상태 코드를 지정app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
const nunjucks = require('nunjucks');
res.locals.변수명으로도 템플릿 엔진 변수 생성 가능
process.env.NODE_ENV는 개발환경인지 배포환경인지 구분해주는 속성
app.use((req, res, next) => {
const error = new Error(`${req.method} ${req.url} 라우터가 없습니다.`);
error.status = 404;
next(error);
});
//404에러도 여기서 같이 처리
app.use((err, req, res, next) => {
res.locals.message = err.message;
res.locals.error = process.env.NODE_ENV !== 'production' ? err : {};
// 개발용이면 에러를 넣어줌, 배포시에는 넣지 X
res.status(err.status || 500); //에러상태 구분
res.render('error');
});
인프런 Node.js 강의
Zerocho 님의 "Node.js 교과서 - 기본부터 프로젝트 실습까지" 강의를 기반으로 작성한 문서입니다.