3 NodeJS & Express를 활용한 REST API 만들기
source: categories/study/database-mongodb/database3.md
3.1 NodeJS 설치
LTS 버전과 최신 버전이있는데 아무거나 설치하면된다.
LTS(Long Term Service) 버전이 좀 더 안정적이다.
설치를 다 하면 터미널 창에서 버전을 확인하면된다.
node -v
npm -v
위와 같이 node
와 npm
버전을 확인했을 때 에러없이 제대로 나오면 설치가 잘된 것이다.
위와 같이 잘 설치가 됐으면
node
위 명령어를 입력하면 노드 자바스크립트 런타임이 실행이 된다.
여기서 자바스크립트를 실행할 수 있다.
노드JS는 자바스크립트를 실행시켜주는 런타임이라고 생각하시면 된다.
3.2 npm
npm은 자바스크립트 생태계라고 생각하면된다.
앞으로 저희가 개발할 때 모든 것을 처음부터 끝까지 저희가 스스로 다 개발하는 것이 아니다.
저희는 최대한 비즈니스 로직, 주요 핵심 기능들을 개발하게되고, 그 외에 반복적인 것들은 웬만하면 기존의 개발자들이 만들어 놓았다.
좀 더 편하게 개발하기 위해서 만들어놓은 모듈들, 그런 모듈들을 모아놓은 곳이 npm(node package manager)이다.
npm에 수만개의 오픈 소스 모듈들이 있는데, 그걸 이 npm 사이트에서 검색해서 가져다 쓰면 된다.
예를 들어, 이번에 REST API
서버를 만들어볼건데, 노드에서 단순히 제공되는 기능들로만으로도 만들 수 있지만, 그렇게되면 코드가 좀 더 길어지고 번거로워진다.
그래서 개발자들이 express
라는 모듈을 만들어놨다.
express
라는 프레임워크가 있는데, 이거를 간단하게 설치한 후 사용하면 된다.
각 모듈마다 정보(주간 다운로드횟수, 최신 업데이트한 후 얼마나 지났는지 등)들이 상세하게 명시되어있으므로 이를보고 해당 모듈을 사용할건지 말건지를 판단하면 된다.
3.3 package.json
REST API 서버를 만들어보겠다.
REST API 서버를 만들기 위해서 express
프레임워크를 사용할 것이다.
npm i express
위 명령어로 설치하면된다.
설치 자체는 된다.
하지만 에러가 뜰 것이다.
package.json
이란 파일이 없다는 에러이다.
package.json
파일을 만들기 위해
npm init
npm init -y
위 명령어중 하나를 실행한다.
그러면 아래와 같은 package.json
파일이 생성된다.
{
"name": "mongodb-tutorial",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
package.json
은 정말 많은 정보를 가지고 있다.
- name: 이 프로젝트의 이름이 뭔지
- version: 이 프로젝트의 버전
- description: 이 프로젝트의 설명
- main: 이 프로젝트의 메인 실행파일이 뭔지
-
scripts: 커스텀 명령어
이렇게 package.json
파일이 있는 상태에서 모듈을 설치할 수 있다.
이렇게 설치하면 위와 같이 package.json
에 바뀐 부분이 생긴다.
dependencies
가 추가되면서 그 안에 express
가 추가된 것을 볼 수 있다.
그리고 node_modules
폴더에 express
가 설치된 것을 볼 수 있다.
그리고 express
모듈이 의존하고있는 다른 모듈들도 같이 설치된 것을 볼 수 있다.
3.4 Express 설정하기
// index.js
const express = require('express');
const app = express();
app.get('/', function (req, res) {
return res.send('hello world');
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
Port가 80이면 :80
없이 localhost만으로 접속 가능하다.
그래서 운영에서는 Port 80을 사용한다.
3.5 nodemon
서버가 실행된 상태에서 위와 같이 텍스트를 수정한 후, 새로고침을 눌러도 반영이 안된다.
왜냐면 이미 서버가 실행되어 해당 서버의 내용이 메모리에 저장이 되었기 때문이다. 그리고 그 메모리에 들어간 내용은 업데이트가 안되기 때문이다.
때문에 서버를 종료했다가 다시 실행해야된다.
그런데 이렇게 수정할 때마다 서버를 종료했다가 실행하기 귀찮잖아?
npm i -D nodemon
nodemon
이란 모듈을 사용하면 위와 같은 문제가 해결이 된다.
이 모듈은 서버 코드가 수정되면, 그 서버를 알아서 종료했다가 재실행 시켜준다.
그런데 이 모듈은 저희가 개발할 때만 필요한 모듈이지 운영할 땐 필요가 없잖아?
그래서 설치할 때 -D
플래그를 붙여준다.
이 모듈은 개발용이라는 걸 명시해주는 것이다.
위와 같이 devDependencies
에 nodemon
이라는 모듈이 추가되는 걸 볼 수 있다.
위와 같이 package.json
에 명령어를 추가하겠다.
위에 보면 watching path
이런식으로 나오지?
계속 서버코드를 보고있다는 뜻이다. 변화한 것이 있는지 없는지.
위와 같이 서버 코드를 하나 수정하면 서버가 자동으로 종료되었다가 재실행된다.
그리고 새로고침하면 수정내용이 반영되어있다.
3.6 REST API
- Client: 웹브라우저
클라이언트에서 서버로 요청을 보낸다. (ex.localhost:3000
)
실제 서비스로 올라가면 외부 IP 또는 DNS와 같은 것으로 요청을 보내겠지?
여튼 Client에서 Express 서버로 요청을 보낸 것이다.
요청을 보낼 때 정말 많은 정보들이 같이 전달이되는데 그 중에 메소드 부분을 알아야된다.
3.6.1 메소드
- GET
- POST
- PUT
- DELETE
3.6.2 리소스 앤드 포인트
- POST
/blog
POST
메소드는 보통 데이터를 생성할 때 사용하는데, blog
라는 데이터를 생성해라 라는 요청이다.
blog
를 생성하기 위해서는 그 blog
에 해당하는 정보들이 있겠지?
blog
의 정보를 담고있는 { title: '...', content: '...' }
이러한 리퀘스트 body가 있다.
그 외에도 헤더도 있고 중요한 것들이 많은데 일단은 이렇게 알고있으면된다.
좀 더 세부적으로 들어가면 데이터를 가공할 때 크게 CRUD 4가지가 있다고 했잖아?
CREATE, READ, UPDATE, DELETE 이렇게 4가지가 있는데, REST 메소드(POST, GET, PUT, DELETE)와 1대1 대응이된다.
아까 위에서 localhost:3000
으로 요청준거는 localhost:3000
혹은 localhost:3000/
이렇게 요청을 준 것이다.
즉,
const express = require('express');
const app = express();
app.get('/', function (req, res) {
return res.send('hello world');
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
위 코드에서 /
이 부분으로 요청이 간 것이다.
위와 같이 수정하고 localhost:3000
으로 요청보내면 /
요청에 대한 응답을 받을 수 없다고 나온다.
그런데 /blog
로 요청을하면 제대로 응답이 온다.
그런데 localhost:3000/blog
이렇게 요청보내는 거에서 GET
인지 POST
인지 명시 안해놨잖아?
브라우저 url로 요청을 보내면 그 요청의 디폴트값은 GET
이다.
POST
, PUT
, DELETE
는 호출할 때 따로 실행을한다.
브라우저 url 상으로는 GET
으로만 호출이 가능하다.
-
조금 더 세부적으로가면
POST/blog
, 즉 블로그를 하나 게시하는 요청을 보낼 땐 해당 블로그의title
,content
등의 백엔드에서 필요한 정보를 담고있는 것들을 리퀘스트 바디로 보내게될 것이다. -
PUT/blog/31231
특정 블로그 글을 수정하고 싶다면, 위와 같이PUT
요청.
그리고 그 블로그의 고유값(31231
)을 표시해주고,
그리고 그 블로그의 title이나 content를 어떻게 바꿀 것이냐에 관련된 정보를 리퀘스트 바디에 넣어 보낸다. -
GET/blog/31231
blog 글 중에서 고유값이 31231인 blog를 return해달라. -
GET/blog?page=1&per_page=10
?
뒤에 변수를 같이 보낼 수 있다.
?
뒤에 변수를 위와 같이 보내는 것은 개발자간의 약속이라고 생각하면된다.
이러한 약속이REST API
이다.
page=1
,per_page=10
이 조건으로 응답을 해라 라는 뜻이다.
즉, 페이지당 블로그 개수가 10개일 때 첫번째 블로그를 가져와라 이런 뜻이다. -
DELETE/blog/31231
블로그 중에서 고유값31231
인 것을 삭제해라.
위와 같은 것 말고도 PATCH
도 있고 그외 여러가지가 있는데 대표적으로 위의 것들만 일단 알고계시면된다.
3.7 User GET & POST API 만들기
이전 시간에서 봤던 REST API, 개발자간의 약속을 이용해서 간단한 API를 만들어보겠다.
User 관련 API를 만들건데, USER를 생성할 수 있고, 생성된 USER를 모두 조회할 수 있는 API를 만들 것이다.
const express = require('express');
const app = express();
// REST API 메소드
// 첫번째 인자: End Point
// 두번째 인자: 콜백함수 - 이 함수는 두개의 인자를 받는다.
// '/user'에 get 요청이 오면 아래의 콜백함수가 실행이되는 것이다.
app.get('/user', function (req, res) {
// 첫번째 인자 req: 클라이언트에서 요청이올 때, ReqBody, ReqHeader, url 등등 그런 정보들이 모두 들어있다.
// 두번째 인자 res: 클라이언트에 응답할 때 필요한 모든 정보들이 들어있다. 지금부터 저희가 작성할 내용 외에도 기본적으로 들어가야되는 네트워크 정보라던지 그런 것들이 모두 여기 들어있다.
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
const express = require('express');
const app = express();
// REST API 메소드
// 첫번째 인자: End Point
// 두번째 인자: 콜백함수 - 이 함수는 두개의 인자를 받는다.
// '/user'에 get 요청이 오면 아래의 콜백함수가 실행이되는 것이다.
app.get('/user', function (req, res) {
// 첫번째 인자 req: 클라이언트에서 요청이올 때, ReqBody, ReqHeader, url 등등 그런 정보들이 모두 들어있다.
// 두번째 인자 res: 클라이언트에 응답할 때 필요한 모든 정보들이 들어있다. 지금부터 저희가 작성할 내용 외에도 기본적으로 들어가야되는 네트워크 정보라던지 그런 것들이 모두 여기 들어있다.
console.log(req);
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
위와 같이 req
를 console.log
로 찍어보면
위 코드에서 return
하는 것이 없어서 이렇게 계속 삥글삥글 돌지만, 터미널창을 보면
무언가 이렇게 잔뜩 찍혀있는 것을 볼 수 있다.
url
, parameter
, query
등의 정보들이 담겨있다.
const express = require('express');
const app = express();
// REST API 메소드
// 첫번째 인자: End Point
// 두번째 인자: 콜백함수 - 이 함수는 두개의 인자를 받는다.
// '/user'에 get 요청이 오면 아래의 콜백함수가 실행이되는 것이다.
app.get('/user', function (req, res) {
// 첫번째 인자 req: 클라이언트에서 요청이올 때, ReqBody, ReqHeader, url 등등 그런 정보들이 모두 들어있다.
// 두번째 인자 res: 클라이언트에 응답할 때 필요한 모든 정보들이 들어있다. 지금부터 저희가 작성할 내용 외에도 기본적으로 들어가야되는 네트워크 정보라던지 그런 것들이 모두 여기 들어있다.
res.send({ users: [] }); // 클라이언트에 어떤 정보를 리턴해주고 싶은지를 적는 부분이다.
// 일단 users: [] 이렇게 응답해보도록 하겠다.
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
새로고침을하면 위와 같이 리턴되는 것을 볼 수 있다.
const express = require('express');
const app = express();
const users = [{name: "hyungju-lee", age: 34}];
// REST API 메소드
// 첫번째 인자: End Point
// 두번째 인자: 콜백함수 - 이 함수는 두개의 인자를 받는다.
// '/user'에 get 요청이 오면 아래의 콜백함수가 실행이되는 것이다.
app.get('/user', function (req, res) {
// 첫번째 인자 req: 클라이언트에서 요청이올 때, ReqBody, ReqHeader, url 등등 그런 정보들이 모두 들어있다.
// 두번째 인자 res: 클라이언트에 응답할 때 필요한 모든 정보들이 들어있다. 지금부터 저희가 작성할 내용 외에도 기본적으로 들어가야되는 네트워크 정보라던지 그런 것들이 모두 여기 들어있다.
res.send({ users: users }); // 클라이언트에 어떤 정보를 리턴해주고 싶은지를 적는 부분이다.
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
위와 같이 코드를 수정해준다.
지금까지는 이렇게 데이터를 수동으로 넣어봤다.
이제 자동으로 되도록 해보겠다.
const express = require('express');
const app = express();
const users = [];
// REST API 메소드
// 첫번째 인자: End Point
// 두번째 인자: 콜백함수 - 이 함수는 두개의 인자를 받는다.
// '/user'에 get 요청이 오면 아래의 콜백함수가 실행이되는 것이다.
app.get('/user', function (req, res) {
// 첫번째 인자 req: 클라이언트에서 요청이올 때, ReqBody, ReqHeader, url 등등 그런 정보들이 모두 들어있다.
// 두번째 인자 res: 클라이언트에 응답할 때 필요한 모든 정보들이 들어있다. 지금부터 저희가 작성할 내용 외에도 기본적으로 들어가야되는 네트워크 정보라던지 그런 것들이 모두 여기 들어있다.
return res.send({ users: users }); // 클라이언트에 어떤 정보를 리턴해주고 싶은지를 적는 부분이다.
})
// post 메소드를 사용한다.
app.post('/user', function (req, res) {
users.push({ name: 'hyungju-lee', age: 34 })
// post 요청이 성공하면 아래를 반환한다.
return res.send({success: true}); // return 키워드를 붙여주는 것이 좋다.
// 이렇게 코드 작성할리는 없겠지만
// res.send() 코드를 중복해서 작성해놨을 경우 모든 res.send()가 호출된다.
// 그렇게되는 것을 방지하기 위해서 return 키워드를 작성한다.
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
3.7 Postman
위에 작성한 post API
를 테스트를 해보고싶은데, 문제는 이전에 말씀드렸다시피 url 요청은 default 값이 GET이고 POST 요청을 url로 할 수가 없다.
이럴 때를 대비한 POST를 테스트할 수 있는 방법이 있는데, postman
이란 것을 다운로드한다.
다운받으시고 설치해주시면 된다.
위와 같이 로그인하라고 나오지만 skip
해도된다.
위와 같이 url
요청을 브라우저처럼 똑같이 보낼 수 있다.
그런데 여기서는 위와 같이 방법을 선택할 수 있다.
우리가 작성한 post API
예시 코드에서 위와 같이 작성했으므로, 원래는 postman에서 POST
요청보낼 때 Body 부분을 작성해야되지만, 일단 작성 안하고 보내보도록하겠다.
그러면 이렇게 코드 작성한대로 응답이 잘 온다.
이렇게 두번 요청보내고 다시 GET 방식으로 요청을 보내면,
위와 같이 똑같은 정보가 두개 들어가있는 것을 확인하실 수 있다.
이렇게 GET, POST 요청이 잘 되는 것을 확인해봤다.
3.8 Express Middleware
실제로 데이터를 보내서 생성하는 것을 해보도록 하겠다.
const express = require('express');
const app = express();
const users = [];
// REST API 메소드
// 첫번째 인자: End Point
// 두번째 인자: 콜백함수 - 이 함수는 두개의 인자를 받는다.
// '/user'에 get 요청이 오면 아래의 콜백함수가 실행이되는 것이다.
app.get('/user', function (req, res) {
// 첫번째 인자 req: 클라이언트에서 요청이올 때, ReqBody, ReqHeader, url 등등 그런 정보들이 모두 들어있다.
// 두번째 인자 res: 클라이언트에 응답할 때 필요한 모든 정보들이 들어있다. 지금부터 저희가 작성할 내용 외에도 기본적으로 들어가야되는 네트워크 정보라던지 그런 것들이 모두 여기 들어있다.
return res.send({ users: users }); // 클라이언트에 어떤 정보를 리턴해주고 싶은지를 적는 부분이다.
})
// post 메소드를 사용한다.
app.post('/user', function (req, res) {
users.push({ name: req.body.name, age: req.body.age })
// post 요청이 성공하면 아래를 반환한다.
return res.send({success: true}); // return 키워드를 붙여주는 것이 좋다.
// 이렇게 코드 작성할리는 없겠지만
// res.send() 코드를 중복해서 작성해놨을 경우 모든 res.send()가 호출된다.
// 그렇게되는 것을 방지하기 위해서 return 키워드를 작성한다.
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
자바스크립트와 다르게 JSON은 키도 무조건 따옴표를 해줘야된다.
위 상태에서 SEND 하면 된다.
그런데 위와 같이 오류가 뜬다.
터미널 창에서도 오류가 찍힌 것을 볼 수 있다.
name
프로퍼티를 읽을 수 없다는 에러이다.
3.8.1 에러 원인
users.push({ name: req.body.name, age: req.body.age })
배열로 넣을 땐 자바스크립트 형식이고 데이터를 보낼 땐
{
"name": "hyungju-lee",
"age": 30
}
위와 같은 JSON 형식이기 때문이다.
즉, 현재 req.body
안에 위 데이터가 JSON 형식, 즉, string 형식으로 저장이되어있는데, 이를 자바스크립트가 사용할 수 있는 객체로 Parsing을 해줘야된다.
그거를 하기 위해서 미들웨어라는 것을 사용해야된다.
3.8.2 미들웨어
HTTP Request를 보내면 req
가 클라이언트에서 서버로 넘어온다고 했잖아?
이 넘어오는 정보들을 그대로 사용해도되지만, 중간에 가공을 해줘야하는 경우도 있을 수도 있다.
그래서 중간에 미들웨어라는 것들을 둬서 데이터를 가공해서 최종적으로 req
로 내보내는 것이다.
그래서 미들웨어는 쉽게 생각해서 함수라고 생각하면된다.
하나의 미들웨어를 완료하면 next
함수를 통해서 그 다음 미들웨어를 호출하고.. 그 다음 미들웨어를 완료하면 next
함수를 호출해서 또 그 다음 미들웨어를 호출하고..
3.8.2.1 대표적인 미들웨어
- CORS: 보안을 돕기위한 미들웨어.
우리가 설정하지 않은 도메인, 이상한 외부 도메인에서 오는 요청을 막고싶을 때 사용하는 미들웨어이다.
설정하지 않은 요청은 이 미들웨어에서 튕겨내버린다. - JSON.parse: 우리가 이번에 사용하려고하는 미들웨어.
JSON 형식으로 온 것을 파싱해서 자바스크립트에서 사용할 수 있게 해준다.
req.body
부분을 JSON.parse로 파싱하면된다. - authenticate: HEADER의 인증, 로그인관련 토큰 처리 미들웨어.
- loggin: 어떤 API가 몇번 호출되었는지 기록하는 미들웨어.
- router:
app.get('/', function(){})
이런 것들이 모두 라우터 미들웨어이다.
아까 코드를 아래와같이 수정해준다.
const express = require('express');
const app = express();
const users = [];
app.use(express.json()); // JSON parse 미들웨어 추가
// REST API 메소드
// 첫번째 인자: End Point
// 두번째 인자: 콜백함수 - 이 함수는 두개의 인자를 받는다.
// '/user'에 get 요청이 오면 아래의 콜백함수가 실행이되는 것이다.
app.get('/user', function (req, res) {
// 첫번째 인자 req: 클라이언트에서 요청이올 때, ReqBody, ReqHeader, url 등등 그런 정보들이 모두 들어있다.
// 두번째 인자 res: 클라이언트에 응답할 때 필요한 모든 정보들이 들어있다. 지금부터 저희가 작성할 내용 외에도 기본적으로 들어가야되는 네트워크 정보라던지 그런 것들이 모두 여기 들어있다.
return res.send({ users: users }); // 클라이언트에 어떤 정보를 리턴해주고 싶은지를 적는 부분이다.
})
// post 메소드를 사용한다.
app.post('/user', function (req, res) {
users.push({ name: req.body.name, age: req.body.age })
// post 요청이 성공하면 아래를 반환한다.
return res.send({success: true}); // return 키워드를 붙여주는 것이 좋다.
// 이렇게 코드 작성할리는 없겠지만
// res.send() 코드를 중복해서 작성해놨을 경우 모든 res.send()가 호출된다.
// 그렇게되는 것을 방지하기 위해서 return 키워드를 작성한다.
})
app.listen(3000, function () {
console.log('server listening on port 3000');
})
이번엔 성공했다.
위와 같은 식으로 req.body
가 찍힌다.