REST는 Representational State Tranfer의 약자로, 월드와이드웹(www)와 같은 하이퍼미디어 시스템을 위한 소프트웨어 아키텍쳐 중 하나의 형식이다. REST 서버는 클라이언트의 요청으로 하여금, HTTP 프로토콜을 사용해 서버의 정보에 접근 및 변경을 가능케 한다. 이때 주고 받는 정보의 형식은 Text, XML, JSON 등이 있으나, 현재 가장 널리 쓰이는 형식은 JSON 이다.
이번 메모에서는 REST 기반의 HTTP 통신으로 서버의 데이터에 접근하는 API 를 만들어보려고 한다.
HTTP 메소드
HTTP/1.1 에서 제공되는 메소드는 보통 알려져 있는 GET/POST 이외에도 여러가지가 있다. REST 기반 아키텍쳐에서 주로 사용되는 4가지 메소드는 다음과 같다.
- GET - 조회
- PUT - 생성 및 업데이트(idempotent)
- DELETE - 제거
- POST - 생성(non-idempotent)
- PATCH - 업데이트(non-idempotent)
PUT과 POST의 차이
PUT은 몇 번을 다시 실행해도 결과가 같은 action 에 사용하며, 그렇지 않은 경우에 POST를 사용하는 것이 일반적이다. 가령 DB에 insert 쿼리를 실행하는 메소드일 경우, 계속 실행이 지속된다면, 계속해서 튜플이 생성되는 결과가 일어나기 때문에 이는 non-idempotent 하므로 POST 를 사용하는 것이 맞다. 두번 째의 경우로, DB에 update 쿼리를 실행하는 경우, 연산식을 사용하지 않고 정적인 값을 대입한다면 몇 번을 실행시켜도 같은 결과가 나오기 때문에 idempotent 하므로 PUT 을 사용하는 것이 맞다.
서버 데이터 생성
아직 Node.js를 활용한 데이터베이스를 구축하지 않았기 때문에, 파일 시스템 모듈을 활용한 서버 데이터를 생성하겠다. 프로젝트 디렉토리 내에 /data/user.json 을 작성하자
/data/user.json
{
"first_user": {
"password": "first_pass",
"name": "abet"
},
"second_user":{
"password": "second_pass",
"name": "betty"
}
}
API-1 : GET /list
module.exports = function(app, fs)
{
app.get('/',function(req,res){
res.render('index', {
title: "MY HOMEPAGE",
length: 5
})
});
app.get('/list', function (req, res) {
//__dirname : 이 파일(라우터 코드)의 현재 디렉토리
fs.readFile( __dirname + "/../data/" + "user.json", 'utf8', function (err, data) {
console.log( data );
res.end( data );
});
})
}
API-2 : GET /getUser/:username
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]);
});
});
API-3 : POST /addUser/:username body:{“password”:”__”, “name”:”__”}
app.post('/addUser/:username', function(req, res){
var result = { };
var username = req.params.username;
// Request 의 유효성을 검사함
// password와 name 항목 필수:
if(!req.body["password"] || !req.body["name"]){
//실패 Response
result["success"] = 0;
result["error"] = "invalid request";
res.json(result);
return;
}
// 데이터 읽음
fs.readFile( __dirname + "/../data/user.json", 'utf8', function(err, data){
//JSON 파일을 JS 객체 형식으로 파싱하여 대입
var users = JSON.parse(data);
//중복 검사
if(users[username]){
//실패 Response
result["success"] = 0;
result["error"] = "duplicate";
res.json(result);
return;
}
// JS 객체에 데이터 추가
users[username] = req.body;
// JS 객체를 JSON 형식으로 데이터 파일에 작성
fs.writeFile(__dirname + "/../data/user.json", JSON.stringify(users, null, '\t'), "utf8", function(err, data){
//성공을 Client에게 Response 함
result = {"success": 1};
res.json(result);
});
});
});
API-4 : PUT /updateUser/:username body:{“password”:”__”, “name”:”__”}
위에서도 언급 했듯이, PUT 메소드는 idempotent한 결과를 보장해야 한다.
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);
})
})
});
API-5 : DELETE /deleteUser/:username
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;
})
})
});