지난 포스팅에서 Node Server를 만들어 보았다.
이번 포스팅에서는 지난 시간에 만든 것을
프레임워크인 express를 사용해서 리팩토링 해보려고 한다.
Node.js 환경에서 웹 서버, 또는 API 서버를 제작하기 위해 사용되는 인기 있는 프레임워크이다.
Node.js HTTP 모듈로 작성한 서버와 다른 점은 다음과 같다.
1. 미들웨어를 추가할 수 있다.
2. 라우터를 제공한다.
npm install express
설치//nodeJS환경에서는 commonJS를 사용
//import가 아니라 require임
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
앱은 서버를 시작하며 3000번 포트에서 연결을 청취합니다. 앱은 루트 URL(/) 또는 라우트에 대한 요청에 “Hello World!”로 응답합니다. 다른 모든 경로에 대해서는 404 Not Found로 응답합니다.
클라이언트는 특정한 HTTP 요청 메서드(GET, POST 등)와 함께 서버의 특정 URI(또는 경로)로 HTTP 요청을 보낸다.
라우팅은 클라이언트의 요청에 해당하는 Endpoint에 따라 서버가 응답하는 방법을 결정하는 것이다.
구조 : app.METHOD(PATH, HANDLER)
//라우팅 설정
const router = express.Router()
router.get('/lower', (req, res) => {
res.send(data);
})
router.post('/lower', (req, res) => {
// do something
})
참고 : express 간단한 서버 만들기 예제
참고 : Express 라우팅 기본
Express는 미들웨어를 추가 할 수 있다고 했다.
미들웨어는 Express의 핵심 이라고 할 수 있다.
요청과 응답의 중간(middle, 미들)에 위치하여
요청과 응답을 조작하여 기능을 추가하기도 하고, 나쁜 요청을 걸러내기도 한다.
즉, 프로세스 중간에 관여하여 특정 역할을 수행한다.
익스프레스 내에서 웹 요청과 응답에 대한 정보를 사용해서 필요한 처리를 진행할 수 있도록 분리된 독립적인 함수이다.
그리고 각각의 미들웨어는 next() 메소드를 호출해서 그 다음 미들웨어가 작업을 처리할 수 있도록 순서를 넘길 수 있다.
위 사진에서는 function(req, res, next) {} 부분이 바로 미들웨어 인 것이다.
이 경우, 위의 함수가 익스프레스 전용 미들웨어 함수이며
이 함수는 next()를 통해 작동한다.
위의 사진과 글만 보면 이해하기 어려울 수 있다.
아래 예제 코드를 보며 미들웨어가 무엇인지, 흐름을 파악해보자.
var express = require('express');
var app = express();
// app.use가 미들웨어가 아니라 app.use()의 인자안의 함수가 미들웨어
app.use(function (req, res, next) {
req.requestTime = Date.now(); // req라는객체에 requestTime 키와 밸류를 래퍼로 등록. requestTime는 사용자가 정한 값이다.
next(); // 다음 미들웨어 함수를 작동
});
// app.get이 미들웨어가 아니라 app.get()의 인자안의 함수가 미들웨어
app.get('/', function (req, res) { // 위에서 next()가 호출되면 이 콜백함수가 작동
res.send(req.requestTime); // 위 미들웨어에서 requestTime 객체를 등록했고 next()를 사용했기 때문에 호출해서 데이터 사용 가능
});
// 서버 실행
app.listen(3000);
app.use() 참고
- app.use() 는 Express 앱에서 항상 실행하는 미들웨어 역할
- app.get(), app.post()등과 달리 요청 URL을 지정하지 않아도 app.use()를 사용할 수 있으며 해당 경우에는 URL에 상관없이 매번 실행된다.
(공통 미들웨어를 만들어 처리를 하기 위해 사용된다는 말이다.)- app.use() 및 app.Method() 함수를 이용해 응용프로그램 수준의 미들웨어를 app객체의 인스턴스에 바인딩Method = get or post
미들웨어를 사용하는 상황의 경우
여러 경우가 있지만 아래를 살펴보자.
이전 포스트에서 Node.js로 HTTP body(payload)를 받을 때에는 Buffer를 조합해서 다소 복잡한 방식으로 body를 얻었다.
네트워크 상의 chunk를 합치고, buffer를 문자열로 변환하는 작업이 필요했었다.
하지만
1. body-parser 미들웨어를 사용하거나// npm install body-parser 설치가 필요 const bodyParser = require('body-parser'); const jsonParser = bodyParser.json(); // 생략 app.post('/users', jsonParser, function (req, res) { })
- Express v4.16.0 의 Express 내장 미들웨어인 express.json()을 사용하면 쉽게 구조화 할 수 있다.
const jsonParser = express.json(); // 생략 app.post('/api/users', jsonParser, function (req, res) { })
만약 express.json() 미들웨어 사용에 에러가 난다면? express.json([options])의 options에 해답이 있다.
Object 형태가 아닌 String 형태도 받을 수 있게 하려면 아래처럼 처리를 해줘야 한다.
왜냐하면 express.json 속성 중 strict의 디폴트 값은 true로 되어있어 Object만 허용되어 있는 상태이기 때문이다.const jsonParser = express.json({strict:false})
추가적으로 res.json 과 res.send([body])의 차이
둘의 공통점은 클라이언트의 request에 대한 데이터를 포함해서 response를 보내는 기능이다.
단, send는 json과 다르게 보낼 수 있는 종류의 제한이 적다.
Node.js HTTP 모듈을 이용한 코드에 CORS 헤더를 붙일 때 응답 객체의 writeHead 메서드를 이용 했었다.
Node.js에서는 이 메서드 등을 이용하여 라우팅마다 헤더를 매번 넣어주어야 하며 OPTIONS 메서드에 대한 라우팅도 따로 구현해야 했었다.
하지만 cors 미들웨어를 사용하면 이 과정을 간단하게 처리할 수 있다.
//npm install cors 설치 필요
const cors = require("cors");
const app = express();
//모든 도메인
app.use(cors());
//특정 도메인
const options = {
origin: "https://corsxxx.com", // 접근 권한을 부여하는 도메인
credentials: true, // 응답 헤더에 Access-Control-Allow-Credentials 추가
optionsSuccessStatus: 200, // 응답 상태 200으로 설정
};
app.use(cors(options));
//특정 요청
app.get("/example/:id", cors(), function (req, res, next) {
res.json({ msg: "example" });
});
위에서 살펴 보았듯 특정 enpoint가 아니라, 모든 요청에 동일한 미들웨어를 적용하려면 어떻게 해야 app.use
를 사용하면 된다.
간단하게 단순한 미들웨어 로거(logger)를 예로 들어보자.
아래 코드는 모든 요청에 대해 메서드와 url을 출력하기 위한 미들웨어를 작성한 코드이다.
const express = require('express');
const app = express();
const port = 3000;
const myLogger = function (req, res, next) {
console.log(`http request method is ${req.method}, url is ${req.url}`); // 이 부분을 req, res 객체를 이용해 고치면, 모든 요청에 대한 로그를 찍을 수 있다.
next();
};
app.use(myLogger);
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.get('/favicon.ico', function (req, res) {
res.send('GET favicon.ico');
});
app.get('/upper', function (req, res) {
res.send('GET upper');
});
app.options('/upper', function(req, res){
res.send('OPTIONS upper');
})
app.post('/upper', function(req, res){
res.send('POST upper');
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
HTTP 요청에서 토큰이 있는지를 판단하여, 이미 로그인한 사용자일 경우 성공, 아닐 경우 에러를 보내는 미들웨어 예제이다.
app.use((req, res, next) => {
// 토큰이 있는지 확인, 없으면 받아줄 수 없음.
if(req.headers.token){
req.isLoggedIn = true;
next();
} else {
res.status(400).send('invalid user')
}
})
시작전
npm install express
express 설치
npm install cors
설치
const express = require('express');
const cors = require('cors');
const app = express(); //express 사용
app.use(cors()); // 모든 요청/응답에 CORS 처리
app.use(express.json({ strict: false })); // 자동 바디 파싱
const PORT = 4999;
const ip = 'localhost';
app.post('/upper', function (req, res) {
let result = req.body;
result = result.toUpperCase();
res.json(result); // 다시 데이터를 보내줄 때 json화 해서 보내주는 메소드
});
app.post('/lower', function (req, res) {
let result = req.body;
result = result.toLowerCase();
res.json(result);
});
app.use(function (req, res, next) {
// 400에러 즉, 클라이언트의 잘못으로 인한 에러는 인자에 err를 넣지 않는다.
res.status(404).send('404 Error');
});
app.use(function (err, req, res, next) {
//에러 처리
console.error(err.stack);
res.status(500).send('500 Error');
});
app.listen(PORT, ip, () => {
console.log(`http server listen on ${ip}:${PORT}`);
});