Middleware란?
API는 요청이 들어왔을 때부터 응답으로 내보내기 까지의 전 과정을 처리합니다.
그 안에서 여러 함수들을 차례대로 거치게 되는데 그 함수 하나를 middleware라고 합니다.
미들웨어에서 다음 기능으로 통과시킬지 말지 결정 할 수 있습니다.
이를 응용하면 미들웨어를 이용하여 여러 기능실행 전 수행해야할 기능을 만들 수 있습니다.
용도
1. req.body 추가 (app.use(express.json());)
postman 사용시 raw - json으로 body 값을 보냈을 겁니다. 하지만 http의 특성상 모든 값들은 string으로 전송됩니다. 그래서 원래라면 string을 json 형태로 바꾸어서 req.body 안에 넣는 기능을 따로 구현했어야 했습니다. 이는 모든 요청에서 사용될 수 있기 때문에 express에서 미들웨어 형태로 제공하게 되었습니다.
2. 인증, 인가
인증 인가시, custom 미들웨어를 통해 인증과 인가 기능을 구현할 수 있습니다. 이는 인증과 인가가 필요한 API 각각 해당 기능을 구현할 필요 없이 인증을 통과하면 해당 API 기능을 실행할 수 있게 만들면 됩니다.
3. CORS
CORS는 브라우저의 보안 정책입니다. 해당 서버의 자원을 들고 올 때, 기본적으로 같은 origin에서 접근하여야 해당 API를 호출할 수 있습니다. 그래서 항시 브라우저한테 해당 서버는 모든 요청에 대해서 개방되어 있다라는 내용을 전달하여야합니다. 모든 요청에서 사용하므로 해당 부분은 미들웨어로 사용할 수 있습니다.
이외에도 여러가지 용도로 사용할 수 있습니다.
예시 코드
- req.body
const express = require('exress');
const app = express();
// bad location
app.post('/bad', (req, res, next) => {
console.log(req.body); // undefined
res.json({ message: "bad" });
});
// express 안에 json 메소드를 이용하여 미들웨어를 구성합니다.
// 아래와 같이 app 초기 부분에 use를 쓰면 모든 요청에 대해서 미들웨어를 수행합니다.
app.use(express.json());
// (err, req, res, next) => { req.body = ~; next(); }
// good location
app.post('/', (req, res, next) => {
console.log(req.body); // json형식으로 보낸 body 객체가 출력됩니다.
res.json({ message: "root" });
});
app.post('/good', (req, res, next) => {
console.log(req.body); // json형식으로 보낸 body 객체가 출력됩니다.
res.json({ message: "root" });
});
app.listen(10010);
2. 인증, 인가
async function authMiddleware(req, res, next) {
const token = req.headers.authorization;
// const { token } = req.headers;
if ( !validToken(token) ) {
res.status(400).json({"message": "invalid token"});
return;
}
const userInfo = await getUser(token.id);
if (userInfo) { // 로그인이 되었을 때
req.userInfo = userInfo; // 다음으로 넘어가는 미들웨어 or 기능에서 req.isLogin 값을 사용할 수 있습니다.
console.log(req.userInfo) // 36
next(); // 다음 미들웨어 or 엔드 포인트로 넘어갑니다.
}
else { // 로그인에 실패하였을 때
// 로그인에 실패했다면 다음 미들웨어로 넘어가지 않고 바로 응답을 보낼 수 있습니다.
res.status(400).json({ message: "로그인할 수 없습니다." });
}
}
3. CORS
const express = require('exress');
const cors = require('cors');
const app = express();
app.use(cors());
// cors 설정들이 적용됩니다.
// 세션때 자세히 다룰 예정입니다.
app.listen(10010);
예시 - 인증 / 인가 middleware
아래 함수(미들웨어)는 인증/인가 로직이 필요한 모든 작업 전에 거쳐야 할 단계입니다.
예를들어
- 글을 쓸 때, 누가 글을 썼는가가 중요하고
- 글을 수정할 때, 누가 글을 수정하는 요청을 했는가가 중요하고
- 글을 삭제할 때, 누가 글을 삭제하는 요청을 했는가가 중요합니다.
유저가 로그인에 성공 했을 시에
우리는 클라이언트에게 토큰을 보냅니다.
그 토큰에는 유저의 고유한 id 를 암호화 해서 보냈었죠.
토큰은 HTTP 요청을 하는 사람이 누구인지를 증명하는 티켓과 같아서,
누구 인지를 밝히려면
매번 토큰을 담아서 요청합니다. (HTTP Stateless 속성 때문에)
그렇다면 인증 / 인가 (누구인지를 밝히는 작업) 로직을
모든 컨트롤러 모듈에 넣어줘야 할까요?
네 그렇게 해도 됩니다.
하지만,
계속 쓰이는 코드를 파일에 넣어서
각 쓰이는 파일에는 연결하는 코드만
이 중간 로직을 모듈화해서 필요한 곳에 맞게 사용하게 된다면 더욱더 효율적이고 좋은 코드가 됩니다.
middlewares 폴더의 auths.js 혹은 validateToken.js 파일
다양한 코드
import { errorGenerator } from '../utils';
import { UserService } from '../services';
import jwt from 'jsonwebtoken';
const { AUTH_TOKEN_SALT } = process.env
const validateToken = async (req, res, next) => {
try {
const token = req.headers.authorization;
const { id } = jwt.verify(token, AUTH_TOKEN_SALT) // 암호화된 토큰을 복호화 합니다.
const foundUser = await UserService.findUser({ id })
if (!foundUser) // 이 토큰을 가진 유저가 데이터베이스에 없으면 404 에러를 냅니다.
errorGenerator({ statusCode: 404, message: 'USER_NOT_FOUND' })
req.foundUser = foundUser // request 객체에 새로운 키값에 찾아진 유저의 정보를 할당하고
next() // next() 함수로 다음 미들웨어로 맥락(context)를 연결합니다.
} catch (err) {
next(err)
}
}
export default validateToken;
미들웨어에 공통 코드 뺀 자리에, 미들웨어 파일 연결하기
위에서 생성한 미들웨어를 Express 라우터에 연결하는 것은 어렵지 않습니다.
HTTP 요청이 컨트롤러로 넘어가기 전에 미들웨어의 이름대로 중간에 함수를 넣어주면 됩니다.
다른 방식의 코드
- routes/ArticleRouter.js
import express from 'express';
const { ArticleController } from '../controllers';
const { validateToken } from '../middlewares'; // 위에서 만든 미들웨어 로직을 임포트 합니다.
const router = express.Router()
router.use(validateToken); // 전체적
app.use(validateToken)
router.get('/', ArticleController.getArticles)
router.get('/:articleId', ArticleController.getOneArticle)
// 아래 세가지 post, put, delete 요청에 대한 컨트롤러는 유저의 인증/인가가 필요한 로직입니다.
router.post('/', validateToken, ArticleController.postOneArticle)
router.put('/:articleId', validateToken, ArticleController.updateOneArticle)
router.delete('/:articleId', validateToken, ArticleController.deleteOneArticle)
// 개별적으로 적용되는 미들웨어
export default router
import express from 'express';
const { ArticleController } from '../controllers';
const { validateToken } from '../middlewares'; // 위에서 만든 미들웨어 로직을 임포트 합니다.
const router = express.Router()
router.use(validateToken); // 전체적
app.use(validateToken)
router.get('/', ArticleController.getArticles)
router.get('/:articleId', ArticleController.getOneArticle)
**// 아래 세가지 post, put, delete 요청에 대한 컨트롤러는 유저의 인증/인가가 필요한 로직입니다.**
router.post('/', validateToken, ArticleController.postOneArticle)
router.put('/:articleId', validateToken, ArticleController.updateOneArticle)
router.delete('/:articleId', validateToken, ArticleController.deleteOneArticle)
// 개별적으로 적용되는 미들웨어
export default router
이 때 HTTP 요청의 맥락이 이어지게 하는 것이 핵심입니다.
그것을 가능하게 하는 것이 express 의 next() 함수이며, next() 에 의해 연결된 모든 함수는 요청이 끝날때(응답을 보낼 때)까지 동일한 request 객체를 공유하게 됩니다.
따라서 validateToken 함수에서 req 객체에 찾아진 유저의 정보를 저장해서 next() 함수로 보내는 것 입니다.
req.foundUser = foundUser // request 객체에 새로운 키값에 찾아진 유저의 정보를 할당하고
next() // next() 함수로 다음 미들웨어로 맥락(context)를 연결합니다.
에러 핸들링 w/ [asyncwrap, next, ErrorHandler.js ]
동기 분께서 설명해주신 부분 1) async를 쓸 때 controller.js 에서 try, catch 지우고 errorHandler.js 사용 ( 에러 시 일로 감) 1-1) errorHandler.js 에서 next 사용 X 1-2) errorHandler.js 에서 next 사용 2) async를 안 쓸 때
pm-developer-justdoit.tistory.com
middlewware 공부 , errorhandler, async
에러 핸들링 2 [적용] 1. throw로 에러 던져보기 에러를 던지는 방법으로 throw 이는 개발자가 작성하는 모듈에서 발생가능한 에러 상황에서 던지게 되며 상위 계층이나 호출하는 곳에서 모듈의 에
pm-developer-justdoit.tistory.com
에러 핸들링 [개념] 1
일반적으로 Javascript 에서는 try-catch문과 throw를 이용하여 에러를 처리 에러 핸들링 미들웨어는 에러를 처리하기 위해 반복적으로 진행되는 코드를 모듈화하여 소프트웨어의 확장성과 생산성을
pm-developer-justdoit.tistory.com
Project 3: [주문 api] - if 에러 핸들링, transaction
에러 핸들링 1. 2. 2-1) 느낌표 위치 2-2) 공백 주의 참고 3. 수량이 36개 남았을 때 남은 36개 다 넣었을 때 장바구니 0개가 됨 장바구니 carts 테이블에 원래 있던 carts id 2 가 사라짐 --> 재고가 없기에 3
pm-developer-justdoit.tistory.com
Project 3 - [주문/결제 api] 에러 핸들링 코드 및 과정
3가지 에러 1. // 에러 핸들링: carts 에 담기지 않은 product를 주문할 때 -> productId 라서 for()안에 들어가야 함 const isProductInCarts = await orderDao.isProductInCarts(userId, productId); // await: userId, productId,(orderDetai
pm-developer-justdoit.tistory.com
'Wecode - Project 2 (부트캠프) > 독학' 카테고리의 다른 글
query string ** (0) | 2023.09.29 |
---|---|
NoSQL 종류, 활용, 모델 (비관계형 데이터베이스 관리 시스템(DBMS)) (0) | 2023.09.28 |
Project 2 - json 복습 ** (0) | 2023.09.26 |
Project2- try catch 문법 복습 ** (0) | 2023.09.26 |
Project 2 - 5일 차: 깃허브의 매력 세계로 풍덩. remote (0) | 2023.09.22 |