Express
는Node.js
의 핵심 모듈인http
와Connect
컴포넌트를 기반으로 하는 웹 프레임워크
이전 포스팅에서 Node.js
는 Chrome
의 V8
엔진을 이용해 Javascript
로 서버를 구축하고, 서버에서 JavaScript
가 작동되도록 해주는 Runtime
이라고 설명했다.
Express
는 이런 Node.js
의 원칙과 방법을 이용하여 웹애플리케이션을 만들기 위한 프레임워크이다.
Express
는 프레임워크이므로 웹 애플리케이션을 만들기 위한 각종 라이브러리와 미들웨어 등이 내장돼 있어 사용성이 좋고, 개발 규칙을 적용하여 코드의 통일성을 향상시킨다.
즉 Express
는 기존 Node.js
의 http
코드를 보다 간결하고 구조적으로 사용한다.
REST API
는 클라이언트와 서버 간에 데이터를 주고받는 방식이다.
REST API
는 HTTP URI(Uniform Resource Identifier)
를 통해 데이터(자원)를 명시하고, Method
를 통해 해당 데이터를 CRUD(Create, Read, Update, Delete)
한다.
자주 사용되는 REST Method
의 종류는 다음과 같다.
Method | 용도 |
---|---|
get | 조회 |
post | 생성 |
put | 전체 수정 |
delete | 제거 |
patch | 부분 수정 |
options | 응답 가능여부 |
Express
를 사용해 서버를 구축하는 방법은 다음과 같다.
Express
를 설치한다.npm i express
Node.js
서버를 다음과 같이 Express
를 사용해 수정한다.const express = require('express');
const app = express();
// localhost:3065
app.get('/', (req, res) => {
res.send('Hello Express'); // http의 end가 express에서는 send
});
// localhost:3065/api
app.get('/api', (req, res) => {
res.send('Hello API');
});
// localhost:3065/api/posts
// 게시글 가져오기, json으로 표현
app.get('/api/posts', (req, res) => {
res.json([ // 응답 데이터
{ id: 1, content: 'Hello1' },
{ id: 2, content: 'Hello2' },
{ id: 3, content: 'Hello3' },
]);
});
// 게시글 작성, json으로 표현
app.post('/api/post', (req, res) => {
res.json({ id: 1, content: 'Hello1' });
});
// 게시글 삭제, json으로 표현
app.delete('/api/post', (req, res) => {
res.json({ id: 1 });
});
app.listen(3065, () => {
console.log('서버 실행 중');
});
결과를 확인해보면 다음과 같이 정상적으로 서버가 동작하는 것을 확인할 수 있다.
브라우저의 주소창은 get
요청이므로 위 예제의 post
, delete
…등과 같은 요청은 다음과 같이 브라우저에서는 불가능하다.
그래서 이러한 문제를 해결하기 위해 Front
에서 axios
로 요청을 보내거나, Postman
이라는 Tool
을 사용한다.
Front axios
요청// front/sagas/post.js
function addPostAPI(data) {
return axios.post('/api/post', data);
}
Postman Tool
사용Postman
은 API
를 테스트하고, 결과를 공유하여 API
개발의 생산성을 높여주는 플랫폼이다.
Postman
은 해당 사이트에서 설치가능하며, get
이외에 post
, put
, patch
…등과 같은 Method
를 전부 사용할 수 있다.
Node.js
서버로 구성된 웹 환경에서는 로그(log)
를 관리하기 위해 별도의 서드파티 라이브러리, 또는 툴을 사용한다.
그 중 대표적으로 많이 사용되는 것이 Morgan
이다, Morgan
은 Node.js
에서 사용되는 로그 관리를 위한 Middleware
이다.
Morgan
을 사용하면 Front Server
에서 Backend Server
로의 요청, 즉 로그를 기록하여 디버깅 작업에 도움을 준다.
아래 npm명령어를 통해 Morgan
을 설치한 뒤, app.js
파일에서 설정한다.
npm i morgan
// app.js
const morgan = require('morgan');
app.use(morgan('dev'));
동적 주소라우팅(Dynamic Address Routing)
은 파라미터(Parameter)
라는 변수 값을 이용해 사용자와 상호작용하는 요청 방식이다.
Browser
에서 전달받은 파라미터는 Router
에서 req.params
라는 request
속성으로 사용할 수 있다.
아래 예제는 동적 주소라우팅의 예시로, Browser
에서 게시글의 ID를 사용하여 각각의 게시글의 정보를 Backend Server
에 요청한다.
// sagas/post.js
function addPostAPI(data) {
// POST /post/1
return axios.post(`/post/${data.postId}`, data);
}
// routes/post.js
router.post('/:postId', (req, res) => { // POST /post/1
try {
// DB에서 ID를 이용하여 게시글을 검색
const post = await Post.findById(req.params.id);
res.json(post);
} catch (err) {
res.status(500).send("Server Error");
}
});
Query String
은 사용자가 path
로 접근한 뒤, 해당 경로에서 제공하는 리소스의 조건을 부여하기 위해 사용되는 구문이다.
그래서 주로 하나의 path
에서 데이터에 맞게 동적으로 다른 결과를 보여주기 위해 사용된다.
REST API
의 GET
메서드는 두번째 인자에 withCredentials
를 사용하기 때문에 데이터를 전달하지 못한다.
function loadPostsAPI(data) {
return axios.get('/posts', {
withCredentials = true;
});
}
이 때 Query String
을 사용하면 다음과 같이 GET
메서드에 데이터를 전달할 수 있다.
데이터는 기본적으로 key=value
형태로 작성하며, 다중 데이터시 구분자(&)
를 사용하여 여러 데이터를 포함한다.
function loadPostsAPI(lastId) {
return axios.get(`/posts?lastId=${lastId}`); // 단일 data전달
}
function loadPostsAPI(lastId, limit) {
return axios.get(`/posts?lastId=${lastId}&limit=${limit}&offset=10`); // 다중 data전달
}
이렇게 전달된 데이터는 라우터에서 req.query
로 접근할 수 있다.
// routes/posts.js
const express = require('express');
const { Op } = require('sequelize');
const { Post } = require('../models');
const router = express.Router();
router.get('/', async (req, res, next) => {
try {
const where = {};
if (parseInt(req.query.lastId, 10)) { // 초기 로딩이 아닐 때
where.id = { [Op.lt]: parseInt(req.query.lastId, 10)} // lastId보다 작은 10개의 게시글을 불러오기
}
const posts = await Post.findAll({
where,
limit: 10,
res.status(200).json(posts);
} catch (error) {
console.error(error);
next(error);
}
});
module.exports = router;
서버요청시 주소에 한글, 특수문자를 포함하면 에러가 발생한다.
encodeURIComponent
, decodeURIComponent
는 이러한 문제를 해결하기 위해 사용되는 JavaScript의 내장 함수이다.
encodeURIComponent
는 URI
로 데이터를 전달하기 위해서 문자열을 인코딩하며, decodeURIComponent
는 인코딩된 문자열을 정상적인 문자열로 되돌려주는 역활을 한다.
해당 함수를 사용하여 문자열이 포함된 요청을 처리하는 방법은 다음과 같다.
function loadHashtagPostsAPI(data) { // data: '떡볶이'
return axios.get(`/hashtag/${encodeURIComponent(data)}`);
}
router.get('/hashtag/:tag', async (req, res, next) => {
try {
const where = { name: decodeURIComponent(req.params.tag) };
res.status(200).json(posts);
} catch (error) {
console.error(error);
next(error);
}
});
이전 포스팅에서 설명했듯 요청과 응답은 1:1 대응되어야 한다.
하지만 이로 인해 다양한 Router
들이 생성되고 코드의 양이 길어져 가독성 측면에서 악영향을 끼친다.
그래서 다음과 같이 routes
폴더를 생성하여 Router
들을 개별적으로 관리한다.
// app.js
const express = require('express');
const postRouter = require('./routes/post');
const app = express();
// app.post('/api/post', (req, res) => {
// res.json({ id: 1, content: 'hello1' });
// });
// app.delete('/api/post', (req, res) => {
// res.json({ id: 1 });
// });
app.use('/post', postRouter);
server.listen(3065, () => {
console.log('서버 실행 중');
});
// routes/post.js
const express = require('express');
const router = express.Router();
router.post('/', (req, res) => { // POST /post
res.json({ id: 1, content: 'Hello1' });
});
router.delete('/', (req, res) => { // DELETE /post
res.json({ id: 1 });
});
module.exports = router;
Node.js 공식문서
Node.js 교과서 - 조현영
React로 NodeBird SNS 만들기 - 제로초