
📖 Reference
📎 https://m.blog.naver.com/hj_kim97/222913693753

Express는 현재 가장 인기있는Node.js프레임워크로, 웹 및 모바일 애플리케이션을 위한 강력한 기능을 제공하는 간결하고 유연한 프레임워크입니다. 이를 통해 가볍고 빠르게 서버를 구축할 수 있습니다.
Express는 최소한의 기능만들 탑재하였지만, 개발자들이 거의 모든 웹 개발의 문제를 다루는 호환성 있는 미들웨어 패키지로 쿠키, 세션, 로그인, URL 파라미터, GET/POST 데이터, 보안 헤더와 그 외 많은 라이브러리로 유연한 개발이 가능하다.
Node.js 의 HTTP 내장 모듈을 사용하여 웹 서버를 띄우는 경우와 Express 를 사용하여 웹 서버를 띄우는 경우를 아래에 정리하였다.
// Node.js에서 HTTP 내장 모듈을 사용하여 웹 서버 구축
const http = require('http');
http.createServer((request, response) => {
response.writeHead(200, {'Content-Type':'text/html'});
response.write("Hello HTTP WebServer!");
response.end();
}).listen(8000, () => { console.log("Server is Running!"); });
// Express를 이용한 서버 구축
const express = require('express');
const app = express;
const port = 3000;
app.get('/', (req, res) => {
res.send("Hello Express!");
});
app.listen(port, () => {
console.log(`Express is Listening on port ${port}`)
});
가볍고 유연한 구조 : Express는 최소한의 기능만 탑재되어 있으며, 개발자가 필요한 미들웨어를 선택하여 사용할 수 있다.
미들웨어 지원 : 미들웨어를 활용하여 요청과 응답 과정 중간에 특정한 동작을 수행할 수 있다.
직관적인 라우팅 : URI 와 HTTP 요청 메서드에 따라 클라이언트 요청에 대한 응답을 결정하는 라우팅이 편리하게 구현된다.
Express 는 주로 아래와 같은 경우에 많이 사용합니다.
Express 를 사용하여 구축합니다.
미들웨어(Middleware)는 운영 체제와 해당 운영 체제에서 실행되는 애플리케이션 사이에 존재하는 소프트웨어를 말합니다. 기본적으로 숨겨진변환 계층으로 동작하며, 분산 애플리케이션의 통신 및 데이터 관리를 가능하게 합니다.
미들웨어(Middleware) 의 몇 가지 예시는 아래와 같다.
라우팅(Routing)이란?
URI(또는 경로)및 특정한 HTTP 요청 메소드(GET, POST 등)인 특정 엔드포인트에 대한 클라이언트 요청에 애플리케이션이 응답하는 방법을 결정하는 것을 말한다.

이미지 출처 : https://expressjs.com/ko/quide/writing-middleware.html
app._method(_path, _handler)
app : express의 인스턴스_method : HTTP 요청 메소드(GET, POST 등)_path : 서버에서의 경로_handler : 라우트(Route) 가 일치할 때 실행되는 함수Express 는 아래와 같은 HTTP 메소드 에 해당하는 라우팅 메소드를 지원한다.
get, post, put, head, delete, options, trace, copy,
lock, mkcol, move, purge, propfind, proppatch, unlock,
report, mkactivity, checkout, merge, m-search, notify,
subscribe, unsubscribe, patch, search, connect
// HTTP GET Request
app.get('/', function (req, res) {
res.send('Hello World!');
});
// HTTP POST Request
app.post('/', function (req, res) {
res.send('Got a POST request');
});
// HTTP PUT Request
app.put('/user', function (req, res) {
res.send('Got a PUT request at /user');
});
// HTTP DELETE Request
app.delete('/user', function (req, res) {
res.send('Got a DELETE request at /user');
});
// 모든 HTTP Request
app.all('/customer', function (req, res) {
res.send('Got a ALL request at /customer');
});
라우트(Route) 경로는 요청 메소드와의 조합을 통해 요청이 이루어질 수 있는 엔드포인트를 정의한다. 문자열, 문자열 패턴, 정규식 등을 이용하여 만들 수 있다.
// 문자열
app.get('/about', function (req, res) {
res.send('about');
});
// 문자열 패턴
app.get('/ab?cd', function(req, res) {
res.send('ab?cd');
});
// 정규식
app.get(/a/, function(req, res) {
res.send('/a/');
});
// Path Variable
app.get('/user/:userId/item/:itemId', (req, res) => {
const { userId, itemId } = req.params;
res.send(`userId: ${userId}, itemId: ${itemId}`);
});
Route Handler는 클라이언트 요청에 따라 라우트가 일치할 때 실행되는 콜백 함수를 말한다.
req: Request로 클라이언트의 요청 정보를 담고 있다.res: Response로 클라이언트에게 응답하기 위한 정보를 담고 있다.next: 다음으로 실행 될 미들웨어 함수를 가리키는 오브젝트이다.
// 콜백 함수만 있는 경우
app.get('/home/a', (req, res) => {
res.send("Hello World!");
});
// 콜백 함수에 next를 적용하여 2개 이상의 콜백 함수 실행
app.get('/home/b', (req, res, next) => {
console.log("첫 번째 cb");
next();
}, (req, res) => {
res.send("두 번째 cb");
});
// 콜백 함수를 배열로 처리하여 라우트 처리 예시
var firstCallback = (req, res, next) => {
console.log("First Callback");
next();
}
var secondCallback = (req, res, next) => {
console.log("Second Callback");
next();
}
var thridCallback = (req, res) => {
console.log("Thrid Callback");
}
app.get('/home/c', [firstCallback, secondCallback, thridCallback]);
Express는 클라이언트로부터 요청이 오면 라우트에서는 다음 메소드 중 한 가지 방법으로 응답을 전송하고
요청(Reqeust) <-> 응답(Response)주기를 종료할 수 있다.
💀 만약, 라우트 핸들러 함수로부터 아래의 메소드 중 어느 하나라도 호출되지 않는 경우, 클라이언트 요청은 정지된 채로 방치된다.
res.download() : 파일이 다운로드 되도록 프롬프트res.end() : 응답 프로세스 종료res.json() : JSON 응답을 전송res.jsonp() : JSONP 지원을 통해 JSON 응답 전송res.redirect() : 요청의 경로 재지정res.render() : 뷰(View) 템플릿 렌더링res.send() : 다양한 유형의 응답을 전송 (가장 많이 사용)res.sendFile() : 파일을 옥텟(octet, 8비트로 된 데이터) 스트림 형태로 전송res.sendStatus() : 응답 상태 코드를 설정한 후 해당 코드를 문자열로 표현한 내용을 응답 Body에 담아 전송Express에서
app.route()를 이용하면 라우트 경로에 대하여 체인(Chain, 연결) 가능한 라우트 핸들러를 작성할 수 있다.
❕경로는 한 곳에 지정되어 있기에 모듈식 라우트를 작성한다면 중복성이 감소하여 코드를 효율적으로 관리할 수 있다.
// Router Chain : 하나의 라우트로 각 라우트의 메소드 처리
app.route('/customer')
.get((req, res) => { // GET Request 처리
res.send("고객 정보 조회");
})
.post((req, res) => { // POST Request 처리
res.send("신규 고객 추가");
})
.put((req, res) => { // PUT Request 처리
res.send("고객 정보 수정");
})
.delete((req, res) => { // DELETE Request 처리
res.send("고객 정보 삭제");
});
app.use()
app.use()함수는 Express 애플리케이션에서 항상 실행하는 미들웨어의 역할을 수행한다.app.get(),app.post()등과 달리 요청 URL을 지정하지 않아도app.use()를 사용할 수 있으며, 해당 경우 URL에 상관 없이 애플리케이션이 요청을 수신 할 때마다 매번 실행된다.
app.use()는 지정된 경로에 미들웨어 함수를 마운트 하는데 사용한다.- 모든(또는 특정) 요청에 대한 공통 로직을 처리하기 위해 사용한다.
- 주로 애플리케이션에 대한 미들웨어를 설정하는데 사용된다. (모듈식 라우터, 에러 핸들링, 공통 미들웨어 등)
// app.use() 기본 문법
app.use(_path, _callback)
// _path : 미들웨어 함수가 호출되는 경로. (경로, 경로패턴 또는 경로와 일치하는 정규식 패턴을 나타냄)
// _callback : 미들웨어 함수 또는 미들웨어 함수의 시리즈/배열
// 모든 요청에 대한 핸들링 (경로, path를 지정하지 않은 경우 모든 요청에 대한 공통 핸들링 지정 가능
app.use((req, res, next) => {
console.log("현재시간 : ", Date.now());
next();
});
// 특정 요청에 대한 핸등링 (URL 파라미터)
app.use('/page/:id', (req, res, next) => {
... // 해당 요청에 대한 처리 로직
next();
});
Express에 존재하는Router()클래스를 사용하면 라우트 처리를 하나의 파일에서 하는 것이 아니라 여러 개의 파일로 분리하여 마치 컴포넌트처럼 각각의 용도에 맞게 구현하여 사용할 수 있다.
😲 분리한 요청 라우터 모듈은 웹 서버를 구동하는 app.js 파일에 app.use() 함수를 이용하여 연결할 수 있다.
→ app.use(_path, _라우터 객체)
// customer.js
const express = require('express');
const router = express.Router();
router
.get((req, res) => {
res.send("고객 정보 조회");
})
.post((req, res) => {
res.send("신규 고객 추가");
})
.put((req, res) => {
res.send("고객 정보 수정");
})
.delete((req, res) => {
res.send("고객 정보 삭제");
});
module.exports = router;
// product.js
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.send("상품 정보 조회");
});
router.post('/insert', (req, res) => {
res.send("신규 상품 추가");
});
router.put('/update', (req, res) => {
res.send("상품 정보 수정");
});
router.get('/delete', (req, res) => {
res.send("상품 정보 삭제");
});
module.exports = router;
// app.js
const express = require('express');
const customerRouter = require('../customer');
const productRouter = require('../product');
const app = express();
const port = 3000;
// 클라이언트 요청 body를 json으로 파싱
app.use(express.json({
limit: '50mb', // 최대 용량
}));
app.listen(port, () => {
console.log(`서버가 ${port}에서 실행됩니다.`);
});
// 라우터 지정 후 기본 경로 지정
app.use('/customer', customerRouter);
app.use('/product', productRouter);
Express에는 애플리케이션에서 발생할 수 있는 모든 에러를 처리하는 에러 핸들러가 내장되어 있다. 이를 사용하면 애플리케이션에서 에러가 발생했을 때 특정 위치에서 에러를 처리할 수 있다.
→ Express에서 일반적인 미들웨어 함수를 정의하는 것처럼 에러 처리를 위한 미들웨어 함수를 정의하면 된다.
→ 에러 처리 미들웨어는 err, req, res, next 총 4개의 매개변수를 사용한다.
⚡ 에러 처리 코드는 라우트 정의 마지막에 진행한다. 또한, err, req, res, next 라는 인자가 있는 함수를 Express에서 에러를 처리하기 위한 함수로 약속되어 있다.
const express = require('exress');
const app = express();
app.get('/error/a', (req, res, next) => {
// 라우터에서 에러가 발생하면 Express가 알아서 이를 캐치한다.
throw new Error("에러 발생");
});
app.get('/error/b', (req, res, next) => {
// next를 이용하여 에러 전달
next(new Error("에러 발생"));
});
// 에러 처리 미들웨어
// 아무 경로도 지정되지 않으면 지정된 URL 이외에 모든 에러를 처리한다.
app.use((err, req, res, next) => {
// State code 500, 에러 코드 전달
res.status(500).json({
statusCode: res.statusCode,
errMessage: err.message
});
});
// 에러 처리 예시
const express = require('exress');
const app = express();
app.get("*", (req, res, next) => {
const error = new Error("에러 발생");
error.status = 500;
next(error);
});
// 에러 발생시 logHandler 실행 이후 errorHandler 실행
app.use(logHandler);
app.use(errorHandler);
// Logger Middleware
const logHandler = (err, req, res, next) => {
console.error('[' + new Date() + ']\n' + err.stack);
next(err);
}
// Error Handler
const errorHandler = (err, req, res, next) => {
res.status(err.status || 500);
res.send(err.message || "Error");
}
app.listen(3000, () => {
console.log("Express Server listening on port " + 3000);
});