이 강좌는 강좌 10-1편과 이어지는 강좌입니다.
강좌를 작성하다가 글이 너무 길어져서 3편으로 나누어 작성한 점 유의해주세요 🙂
10-1. EJS
10-2. Restful API
10-3. Session
3. RESTful API
REST 는 Representational State Transfer 의 약자로서, 월드와이드웹(www) 와 같은 하이퍼미디어 시스템을 위한 소프트웨어 아키텍쳐 중 하나의 형식입니다. REST 서버는 클라이언트로 하여금 HTTP 프로토콜을 사용해 서버의 정보에 접근 및 변경을 가능케 합니다. 여기서 정보는 text, xml, json 등 형식으로 제공되는데, 요즘 트렌드는 json이죠.
HTTP 메소드
HTTP/1.1 에서 제공되는 메소드는 여러개가 있는데요,
REST 기반 아키텍쳐에서 자주 사용되는 4가지 메소드는 다음과 같습니다.
- GET – 조회
- PUT – 생성 및 업데이트
- DELETE – 제거
- POST – 생성
여기서 잠깐, POST 와 PUT 좀 헷갈리지 않나요? 둘다 생성을 한다면..
어느 상황에 무엇을 써야하는거지? PUT vs POST, REST API (1ambda Blog) 여기서 궁금증을 해소하세요!
데이터베이스 생성
JSON 기반의 사용자 데이터베이스를 만들어보겠습니다.
Node.js 와 궁합이 잘 맞는 MongoDB를 사용했더라면 좋았겠지만
이 포스트의 초점은 Express 이므로 다음으로 미루도록 하겠습니다.
2016/3/3 EDITED: 미룬 강좌가 작성되었습니다.
[Node.JS] 강좌 11편: Express와 Mongoose를 통해 MongoDB와 연동하여 RESTful API 만들기
data 폴더를 만들고 그 안에 user.json 파일을 생성해주세요:
{ "first_user": { "password": "first_pass", "name": "abet" }, "second_user":{ "password": "second_pass", "name": "betty" } }
(보안을 생각한다면 패스워드는 Encrypt를 하거나 Hash 를 쓰는게 좋겠지만 이건 예제니까 PASS!
그 부분은나중에 한번 다루도록 하겠습니다.)
첫번째 API: GET /list
모든 유저 리스트를 출력하는 GET API 를 작성해보겠습니다.
우선, user.json 파일을 읽어야 하므로, fs 모듈을 사용하겠지요?
module.exports = function(app, fs) { app.get('/',function(req,res){ res.render('index', { title: "MY HOMEPAGE", length: 5 }) }); app.get('/list', function (req, res) { fs.readFile( __dirname + "/../data/" + "user.json", 'utf8', function (err, data) { console.log( data ); res.end( data ); }); }) }
__dirname 은 현재 모듈의 위치를 나타냅니다.
router 모듈은 router 폴더에 들어있으니, data 폴더에 접근하려면
/../ 를 앞부분에 붙여서 먼저 상위폴더로 접근해야합니다.
자 서버를 실행해서 http://localhost/list 에 접속해보세요.
두번째 API: GET /getUser/:username
이번엔 특정 유저 username의 디테일한 정보를 가져오는 GET API 를 작성해보도록 하겠습니다.
다음 코드를 router/main.js 의 list API 아래에 작성해주세요.
app.get('/getUser/:username', function(req, res){ fs.readFile( __dirname + "/../data/user.json", 'utf8', function (err, data) { var users = JSON.parse(data); res.json(users[req.params.username]); }); });
파일을 읽은후, 유저 아이디를 찾아서 출력해줍니다.
유저를 찾으면 유저 데이터를 출력하고 유저가 없다면
{} 을 출력하게 됩니다.
fs.readFile()로 파일을 읽었을 시엔 텍스트 형태로읽어지기 때문에, JSON.parse() 롤 해야합니다.
서버를 다시 실행 후 http://localhost:3000/getUser/first_user 에 접속해보세요.
세번째 API: POST addUser/:username
body: { “password”: “_____”, “name”: “_____” }
이번 API는 첫째 둘째와 다르게 POST 메소드를 사용합니다.
편한 테스팅을 위하여 구글 크롬 익스텐션 Postman 을 사용하겠습니다.
HTTP 패킷을 요청하고 분석 할 수 있는 툴 입니다. 정말 괜찮은 앱이니 받아두세요. 물론 비슷한 프로그램이 이미 설치되있는사람들은 생략하셔도 됩니다.
다음 코드를 router/main.js 의 getUser API 하단에 작성해주세요:
app.post('/addUser/:username', function(req, res){ var result = { }; var username = req.params.username; // CHECK REQ VALIDITY if(!req.body["password"] || !req.body["name"]){ result["success"] = 0; result["error"] = "invalid request"; res.json(result); return; } // LOAD DATA & CHECK DUPLICATION fs.readFile( __dirname + "/../data/user.json", 'utf8', function(err, data){ var users = JSON.parse(data); if(users[username]){ // DUPLICATION FOUND result["success"] = 0; result["error"] = "duplicate"; res.json(result); return; } // ADD TO DATA users[username] = req.body; // SAVE DATA fs.writeFile(__dirname + "/../data/user.json", JSON.stringify(users, null, '\t'), "utf8", function(err, data){ result = {"success": 1}; res.json(result); }) }) });
JSON 형태가 INVALID 하다면 오류를 반환하고, VALID 하다면 파일을 열어서 username의 중복성을 확인 후
JSON 데이터에 추가하여 다시 저장합니다.
34번줄에서 stringify(users, null, 2) 은 JSON 의 pretty-print 를 위함 입니다.
Postman을 통하여 API를 테스트해볼까요?
body 에서 Content-type를 JSON 으로 하셔야 정상적으로 처리됩니다.
네번째 API: PUT updateUser/:username
body: { “password”: “_____”, “name”: “_____” }
이 API는 위 API와 비슷합니다. 사용자 정보를 업데이트 하는 API 이구요, PUT 메소드를 사용합니다.
PUT API 는 idempotent 해야 합니다, 쉽게말하자면 즉 요청을 몇번 수행하더라도, 같은 결과를 보장해야합니다.
마지막 API: DELETE deleteUser/:username
유저를 데이터에서 삭제하는 API 입니다. DELETE 메소드를 사용합니다.
네번째 API 아래에 다음 코드를 작성해주세요:
app.delete('/deleteUser/:username', function(req, res){ var result = { }; //LOAD DATA fs.readFile(__dirname + "/../data/user.json", "utf8", function(err, data){ var users = JSON.parse(data); // IF NOT FOUND if(!users[req.params.username]){ result["success"] = 0; result["error"] = "not found"; res.json(result); return; } delete users[req.params.username]; fs.writeFile(__dirname + "/../data/user.json", JSON.stringify(users, null, '\t'), "utf8", function(err, data){ result["success"] = 1; res.json(result); return; }) }) })
router/main.js 전체 코드
module.exports = function(app, fs) { app.get('/',function(req,res){ res.render('index', { title: "MY HOMEPAGE", length: 5 }) }); app.get('/list', function (req, res) { fs.readFile( __dirname + "/../data/user.json", 'utf8', function (err, data) { console.log( data ); res.end( data ); }); }); app.get('/getUser/:username', function(req, res){ fs.readFile( __dirname + "/../data/user.json", 'utf8', function (err, data) { var users = JSON.parse(data); res.json(users[req.params.username]); }); }); app.post('/addUser/:username', function(req, res){ var result = { }; var username = req.params.username; // CHECK REQ VALIDITY if(!req.body["password"] || !req.body["name"]){ result["success"] = 0; result["error"] = "invalid request"; res.json(result); return; } // LOAD DATA & CHECK DUPLICATION fs.readFile( __dirname + "/../data/user.json", 'utf8', function(err, data){ var users = JSON.parse(data); if(users[username]){ // DUPLICATION FOUND result["success"] = 0; result["error"] = "duplicate"; res.json(result); return; } // ADD TO DATA users[username] = req.body; // SAVE DATA fs.writeFile(__dirname + "/../data/user.json", JSON.stringify(users, null, '\t'), "utf8", function(err, data){ result = {"success": 1}; res.json(result); }) }) }); app.put('/updateUser/:username', function(req, res){ var result = { }; var username = req.params.username; // CHECK REQ VALIDITY if(!req.body["password"] || !req.body["name"]){ result["success"] = 0; result["error"] = "invalid request"; res.json(result); return; } // LOAD DATA fs.readFile( __dirname + "/../data/user.json", 'utf8', function(err, data){ var users = JSON.parse(data); // ADD/MODIFY DATA users[username] = req.body; // SAVE DATA fs.writeFile(__dirname + "/../data/user.json", JSON.stringify(users, null, '\t'), "utf8", function(err, data){ result = {"success": 1}; res.json(result); }) }) }); app.delete('/deleteUser/:username', function(req, res){ var result = { }; //LOAD DATA fs.readFile(__dirname + "/../data/user.json", "utf8", function(err, data){ var users = JSON.parse(data); // IF NOT FOUND if(!users[req.params.username]){ result["success"] = 0; result["error"] = "not found"; res.json(result); return; } // DELETE FROM DATA delete users[req.params.username]; // SAVE FILE fs.writeFile(__dirname + "/../data/user.json", JSON.stringify(users, null, '\t'), "utf8", function(err, data){ result["success"] = 1; res.json(result); return; }) }) }) }
이렇게 Express 응용 RESTful API 편을 마치도록 하겠습니다.
다음 편에서는 세션을 다루도록 하겠습니다.