안녕하세요. 이번 시간에는 잠시 쉬어가는 시간으로 요즘 웹을 설계할 때 자주 사용되는 REST API를 배워보겠습니다. REST는 REpresentational State Transfer의 약자로 소프트웨어의 네트워크를 구축하는 방법에 대한 겁니다. 저도 뭐라고 딱히 정의하기는 힘드네요.
웹은 명확하게 클라이언트와 서버로 구분되어 있습니다. 클라이언트에서 서버로 요청을 보내고, 서버는 클라이언트에 응답을 보내줍니다. REST API를 사용하는 웹앱은 URI(주소)를 통하여 서버에 요청을 보냅니다. 서버는 html, xml, json 등으로 응답하고요.
웹에서 REST API를 사용하기 위해서 HTTP의 5가지 메소드를 이용합니다. GET, PUT, PATCH, POST, DELETE가 있습니다. GET 메소드는 지난 시간에 봤죠? router.get('/')
, router.get('/about')
등이 바로 서버가 GET 요청을 대기하고 있다는 겁을 나타냅니다. 언제든 해당하는 요청이 오면 그에 따른 응답을 보내기 위해서죠.
아직 PUT, PATCH, POST, DELETE 메소드는 본 적이 없죠? 모든 환경에서 PUT, PATCH와 DELETE 메소드를 사용하려면 method-override 패키지를 설치해야합니다. (없어도 AJAX 요청에서 사용은 가능합니다) 또한 주소와 함께 전송되는 데이터를 받으려면 body-parser 패키지도 필요합니다. PUT은 전체 수정, PATCH는 부분 수정, DELETE는 삭제, POST는 생성 요청입니다. GET은 조회 요청이고요. 단순히 웹 페이지를 불러올 때는 GET 요청을 보냅니다.
서버에 위의 두 패키지를 깔고 다음과 같이 합시다.
const methodOverride = require('method-override');
const bodyParser = require('body-parser');
// ...
app.use(methodOverride()); // PUT, DELETE를 지원 안 하는 클라이언트를 위해
app.use(bodyParser.json()); // body의 데이터를 json형식으로 받음
app.use(bodyParser.urlencoded({ extended: true })); // qs모듈로 쿼리스트링 파싱
만약 Zero라는 사용자를 REST API 스타일로 불러오고 싶다면 어떻게 표현할까요?
HTTP GET '/user/Zero'
이렇게 서버에 해당 주소의 GET 요청을 보내면 됩니다. 서버에 요청을 보내는 방법은 다양합니다. 위와 같은 주소를 가진 링크를 눌러도 되고, jQuery ajax로 호출해도 되고, 직접 코딩해서 보내도 됩니다. 물론 서버는 아래와 같이 미리 해당하는 요청을 대기하고 있어야 하고요.
router.get('/user/:name', (req, res) => {
res.json({ name: req.params.name });
});
이런 식으로요. :name 부분이 req.params
에 저장됩니다. 이렇게 '/user/Zero'로 요청을 보내면, { name: 'Zero' }
하고 응답이 옵니다. 만약 Hero라는 사용자를 생성하고 싶으면 어떻게 할까요?
HTTP POST '/user', data: { name: 'Hero' }
이렇게 주소와 함께 데이터를 보낼 수 있습니다. 데이터를 보내기 위해서는 AJAX나 폼을 사용하는 게 좋습니다. 서버에서는 아래와 같이 요청을 대기해줍니다.
router.post('/user', (req, res) => {
User.insert({ name: req.body.name }, (err, result) => {
if (err) {
return next(err);
}
res.json(result);
});
});
위와 같이 응답할 수 있습니다. 주소와 함께 보낸 데이터는 req.body
에 저장됩니다. body-parser 패키지를 사용해야 가능합니다. 위와 같이 데이터베이스에 Hero라는 이름으로 유저를 생성하고 그 결과를 알려줍니다.
Hero를 Nero로 바꾸고 싶다면
// HTTP PATCH '/change/Zero/name/Nero'
router.patch('/change/:name/name/:new', (req, res) => {
User.update({
name: req.params.name,
}, {
name: req.params.new,
}, (err, result) => {
if (err) {
return next(err);
}
res.json(result);
});
});
PUT과 PATCH가 헷갈리는 분들이 있으실 텐데, 간단하게 PUT은 데이터 전체를 다른 것으로 교체하는 것이고, PATCH는 부분만 수정하는 것이라고 생각하시면 됩니다. 위의 상황에서는 유저 정보 중 name만 교체한 것이기 때문에 PATCH를 썼는데요. 어떻게 보면 유저 정보가 name밖에 없고 name을 교체한 것은 전체를 교체한 것이나 다름없기 때문에 PUT을 써도 될 것 같습니다.
이렇게 주소는 명사 단위로 이해하기 쉽게 만들면 좋습니다. 이름 대신에 나이를 바꾸고 싶으면 '/change/Zero/age/23' 하면 되겠죠? 규칙성 있는 게 관리하기 쉬워보이네요.
마지막으로 정들었던 Nero를 떠나보내고 싶다면
// HTTP DELETE '/user/Nero'
router.delete('/user/:name', (req, res) => {
User.remove({ name: req.params.name }, (err, result) => {
if (err) {
return next(err);
}
res.json(result);
});
});
하면 됩니다. 위에 get 메소드와 'user/:name'으로 주소는 같지만 요청 메소드(get 대신 delete)가 다르기 때문에 다르게 처리됩니다.
Idempotent
HTTP 메소드에는 idempotent라는 개념이 있습니다. 반복해도 결과가 같은 경우 idempotent 하다고 표현합니다. POST를 제외한 나머지 메소드는 idempotent합니다. 100번을 조회해도 결과는 항상 같고요. 100번을 수정하고 삭제해도 결과는 마지막 한 번 한 것과 결과는 같습니다. 다만 POST 요청을 할 때는 계속 새로운 문서가 생기기 때문에 결과가 다릅니다. POST 100번을 하면 100개의 문서가 생기죠.
Idempotent 개념이 왜 중요하냐면, 에러가 발생해서 실행이 되지 않거나, 복구가 필요한 경우 때문입니다. Idempotent한 메소드들은 실행이 되지 않을 경우에는 한 번 더 실행하면 되고, 복구가 필요한 경우에는 해당 요청만 취소하면 되지만, 그렇지 않은 POST 메소드는 복구할 때 더 각별한 주의가 필요합니다.
장점
이렇게 REST API로 하면 무슨 장점이 있는지 궁금하시죠?
- 체계적이라 관리하기 쉽습니다.
- 주소만 봐도 무슨 내용인지 이해할 수 있습니다.
- 서버를 REST API로 만들어 놓으면 언어에 상관없이 HTTP를 사용하는 다양한 플랫폼에서 동시에 사용할 수 있습니다.
- 캐싱이 가능합니다. (GET 요청같은 경우 같은 응답을 할 경우 캐싱으로 더 빨리 로딩합니다)
주의
REST API는 무조건 따라야하는 것이 아니라 하나의 구조입니다. 선택을 하든 안 하든 여러분의 자유입니다. 다만 따르기로 선택을 했다면 몇 가지 상황을 주의하셔야 합니다.
첫 번째는 잘 이해가 가지 않는 주소를 사용한 경우입니다. '/Zero/Nero'같은 주소는 이게 뭘 하라는 요청인지 이해가 잘 안 갑니다. 최대한 주소는 명사 단위로 이해할 수 있게 만듭시다.
또 다른 경우로 HTTP 메소드를 잘못 사용하는 게 있습니다. HTTP GET '/post/Javascript'를 통해 Javascript에 관련된 포스트를 불러오면서 동시에 조회수를 1 올린다면, 이는 REST에 어긋난 겁니다. 차라리 counter을 관리하는 PUT 요청을 따로 하는 게 더 나아 보입니다. HTTP PUT '/count/Javascript', HTTP GET '/post/Javascript' 이렇게 따로 두 번 요청합니다. 요청은 많아졌지만, 하나의 요청에 하나의 기능만 수행하면 돼서 관리하기 좋습니다.
물론 여러분의 서버 환경에 따라 철저하게 지키지 못하는 경우도 생깁니다. 하지만 적어도 GET, PUT, POST, PATCH, DELETE 메소드 정도는 지켜주면 개발자간의 소통도 쉽고 개발 시 혼선도 덜 빚어집니다.
이번 포스팅을 하면서 여러 자료를 조사한 결과, 제 홈페이지의 REST API에도 문제점이 많네요. 저는 이만 고치러 갑니다! 다음 시간에는 쉬어가는 시간 2편으로 HTTP Status Code에 대해 알아보겠습니다.