express로 서버를 만들기 전에 우선 express의 특징부터 알아보자.
const express = require('express');
const app = express();
app.get('/posts', (req, res, next) => {
res.send(...);
})
app.post('/posts', (req, res, next) => {
res.send(...);
})
app.put('/posts/:id', (req, res, next) => {
res.send(...);
})
app.get('/posts/:id', (req, res, next) => {
res.send(...);
})
app.listen(8080);
위의 코드의 내용은 아래와 같다.
request
, response
, next
순으로 넘겨준다.request
: 요청할 때 받은 request의 정보가 담긴 객체response
: 응답으로 정의해야 할 response 객체next
: 다음 (미들웨어) 로 넘어갈 수 있는 메서드이렇듯 express의 사용법은 매우 간단하다.
exrpess 공식 문서
in API Reference
express에 어떤 메서드가 있는지
request, response 객체에 어떤 프로퍼티와 메서드가 있는지 알 수 있다.
(사진출처: https://frontend.turing.edu/lessons/module-4/express-middleware.html)
express 앱의 메서드에 작성하는 콜백함수를 미들웨어라고 한다.
express는 미들웨어의 연속(체인)이다. 앱에 use
, get
, post
등과 같은 메서드를 걸어두면 요청이 해당 정의를 체인을 타면서 진행이 된다.
next를 이용해서 다음 미들웨어로 넘어갈 수 있다. 미들웨어 체인중에 res.send()
를 이용하여 응답을 보내게되면 그 즉시 미들웨어 체인이 종료된다.
이제 본격적으로 express를 사용하여 api 서버를 만들어 보자.
우선 express와 nodemon을 설치하자.
$ npm i express
$ npm i -D nodemon
// app.js
import express from 'express';
const app = express();
app.get('/', (req, res, next) => {
res.send('<h1>Index page</h1>');
});
app.listen(8080);
앱을 생성하고 해당 서버에서 몇번 포트에서 요청을 들을지 설정해준다.
/
경로로 get요청을 보내면 응답으로 html코드를 반환한다.
서버를 실행하는 것은 nodemon
을 이용하여 앱 파일을 실행 시켜주면 된다.
$ nodemon app.js
혹은 package.json
에서 아래와 같이 설정을 하고 npm start
로 스크립트를 예약어로 등록하여 실행해보자.
{
"name": "1-start",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"start": "nodemon app", // 이거 추가!
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"express": "^5.0.0-alpha.8"
}
}
콜백함수의 첫번 째 인자로 request 정보를 담은 객체를 넘겨준다.
request의 대표적인 프로퍼티인 파라미터와 쿼리 프로퍼티에 대해서 알아보자.
파라미터 res.param
import express from 'express';
const app = express();
app.get('/posts/:id', (req, res, next) => {
res.setHeader('Content-Type', 'application/json');
res.status(200).json({ postId: req.params.id });
});
app.listen(8080);
path에 /:키워드
로 작성하여 파라미터를 키워드와 매핑한다.
req.param
에 객체로 키워드를 키로 하고 실제 요청의 값을 밸류로 하는 객체의 형태를 갖는다.
쿼리 res.query
import express from 'express';
const app = express();
app.get('/posts', (req, res, next) => {
res.setHeader('Content-Type', 'application/json');
res.status(200).json({ ...req.query });
});
app.listen(8080);
요청을 보낼때 url에 경로?keyword1=값1&keyword2=값2
라는 형태로 작성을 하게되면 각 키워드 값들을 객체로 하여 res.query
에 담긴다.
콜백함수의 두번 째 인자로 response 정보를 담은 객체를 넘겨준다.
res.send(데이터); // 데이터 보내기
res.json(JSON데이터); // JSON 데이터 보내기
res.sendStatus(상태코드); // 상태코드만 보내기
res.status(201).send('created'); // 상태코드를 담고 데이터 보내기
res.setHeader('key', 'value'); // 헤더에 키,밸류 설정하기
express앱의 메서드에 콜백은 여러개를 담을수 있다. 또한 express앱의 메서드들은 같은 메서드라도 중복으로 여러번 사용할 수도 있다. 중복으로 여러번 사용할 경우 미들웨어 체인으로 먼저 정의해준 미들웨어부터 체인이 진행된다.
express앱의 메서드의 콜백함수는 하나의 미들웨어이다. 그리고 콜백함수를 등록한 순서대로 미들웨어 체인의 순서가 결정된다.
import express from 'express';
const app = express();
app.get(
'/',
(req, res, next) => { // *1번째 미들웨어
console.log('first');
next();
},
(req, res, next) => { // *2번째 미들웨어
console.log('second');
next();
}
);
app.get('/', (req, res, next) => { // *3번째 미들웨어
console.log('third');
res.send('done');
});
app.listen(8080);
예시 코드를 보면 /
의 경로에 대한 get요청의 미들웨어가 총 3개 존재한다.
각 미들웨어안에서 next()
를 사용해야 다음 미들웨어로 넘어간다.
각 미들웨어는 다음 중 꼭 하나로 끝나야 한다.
next();
next('router');
next(new Error('error'));
res.send(...);
미들웨어의 가장 마지막에는 에러처리와 NotFound처리를 넣어주어야한다.
앞의 미들웨어들에서 발생한 에러를 가장 마지막에 처리해주고
만야 미들웨어 없는 요청이라면 마지막에 404 NotFound를 처리하기 위함이다.
import express from 'express';
const app = express();
app.get('/', (req, res, next) => {
console.log('first');
next();
});
app.get('/', (req, res, next) => {
next(new Error('error'));
});
// 404 NotFound
app.use((req, res, next) => {
res.status(404).send('404 Not Found');
});
// 에러처리
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Sorry, try later!');
});
app.listen(8080);
참고로 미들웨어의 인자를 4개르로 명시하면 첫번째 인자에는 error개체를 넘겨준다.
app.all('/path', 미들웨어)
: 딱 /path
의 경로에 대해서만 미들웨어를 등록한다.
app.use('/path', 미들웨어)
: /path
이하에 이어지는 어떤 경로에 대해서 미들웨어를 등록한다.
애스터리스크(*
)를 사용하면 모든 경로를 의미한다.
app.all('/path/*', 미들웨어)
와 app.use('/path', 미들웨어)
은 동일하게 동작한다.
post 요청은 사용자가 무언가를 만들기를 원하기 때문에 requset의 body를 읽어와서 처리해줘야 한다.
import express from 'express';
const app = express();
app.post('/', (req, res, next) => {
console.log(req.body); // undefined 출력
});
app.listen(8080);
위와 같이 코드를 작성하고 postman으로 http://localhost
post 요청을 보내면 undefined
가 출력된다.
요청의 body를 정상적으로 읽어오려면 express에서 제공해주는 express.json()
미들웨어를 사용하면 JSON형태의 body를 읽어올수 있다.
import express from 'express';
const app = express();
app.use(express.json()); // express.json() 미들웨어 사용
app.post('/', (req, res, next) => {
console.log(req.body); // 올바르게 동작
});
app.listen(8080);
위와 같이 앱의 모든 요청에 대해 body를 파싱해주면 다음 미들웨어에서부터 요청의 바디를 읽어올 수 있다.