워드프레스 사이트에 새로운 포스트 타입(post type)과 텍소노미(taxonomy)를 추가하기
HATEOAS를 알아보다 알게 된 것들… (REST API, Charset, Link)
2019-12-05
Explanation
오늘은 HATEOAS(Hypermedia As The Engine Of Application State)에 대해 알아보다가 알게된 그 밖의 것들에 대해 정리해 보려고 합니다.
HATEOAS로 시작했지만, 오늘의 포스팅은 HATEOAS에 대한 글은 아닐 거 같아요.
(사실 HATEOAS에 대해 글을 적을 만큼 아직 잘 알지 못해요..)
작성할 글의 키워드를 미리 요약하면 REST API, Charset 그리고 HTTP Header의 Link에 대해 이야기해보려 해요.
‘HATEOAS’는 어떻게 읽는 걸까요..? 헤이트이오아스..??
누군가 만약 저에게 API는 어떻게 만드냐고 물어봤다면, 아마도 전 REST API로 만든다고 말 했을 거 같아요.
그동안 많은 API를 꽤 오랜시간을 상의하며 고민하며 만들어 왔거든요.
그런데 HATEOAS를 알아보던 중 발견한 https://blog.npcode.com/(Yi EungJun님)의 글들을 읽어보며..
아… 내가 그동안 하던 방식이 REST API가 아니였구나…
(아주 REST에 대해 자세하고 친절하게 적어주셔서 한번 읽어보시면 정말 좋을 거 같아요.)
그래서 사람들이 RESTfull api를 사용한다고 하는 거 였군요.
그리고 같은 https://blog.npcode.com/(Yi EungJun님)의 슬라이드를 보다가 갸우뚱한 부분이 있었어요.
슬라이드: https://slides.com/eungjun/rest#/47
1 |
charset은 "character set"이 아님에도 불구하고 그런 오해를 계속해서 불러일으키고 있다. |
두둥. charset이 케릭터 셋이 아니였단 말인가?!
그리고 슬라이드에 첨부된 참고 링크를 찾아가 보았답니다.
http://w3-org.9356.n7.nabble.com/How-to-pronounce-quot-charset-quot-td254585.html#message254629
1 |
Many people pronounce "charset" as "character set" but I think that is incorrect because the word does not mean "character set". |
그래서 믿을 만한 우리의 MDN 형님에게 가서 확인해 보았답니다.
https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Accept-Charset
1 2 |
Accept-Charset 요청 HTTP 헤더는 클라이언트가 이해할 수 있는 캐릭터셋이 무엇인지를 알려줍니다. ... |
혼란스러웠지만, 혹시 하는 마음으로 언어를 영어로 변경하여 확인해 보았답니다.
1 2 |
The Accept-Charset request HTTP header advertises which character encodings the client understands. ... |
짜잔!
아.. 그렇습니다. Charset은 ‘character set’의 약자로 만들어진건 맞는 거 같은데, 하지만 정확하게 여기서 Charset은 ‘character set’란 이름과 다르게 ‘character encodings’를 의미하고 있던 것입니다.
‘character set’과 ‘character encodings’의 차이는 아래의 블로그 글을 보시면 자세히 알아보실 수 있답니다.
https://seowoosung.github.io/CharacterSet/
위 블로그에서 정의한 부분만 살짝 옴겨보면,
character encoding: 문자나 기호들의 집합을 컴퓨터에서 표현하는 방법.(UTF8, UTF16)
character set: 정보를 표현하기 위한 글자들의 집합을 정의한 것. 한 문자 집합을 여러 문자 인코딩에서도 사용가능(ex: 유니코드)
(굉장히 조심스럽지만) HATEOAS의 장점 중 한가지는 이후 요청에 대한 URI 정보를 서버에서 알려주고 클라이언트에서는 서버에서 받은 URI를 동적으로 받아서 사용하기 때문에 이후에 URI 정보가 변경되어도 클라이언트에서는 수정이 필요하지 않는 점이 있는데요.
그렇다면 서버에서 다음 URI 정보를 클라이언트에게 알려주어야 하는데, 엄청 간단하게는 ‘response body’에 ‘{ links : … }’ 뭐 이렇게 줄 수도 있겠지만 HTTP1.1에 추가된 Link라는 Header가 이럴때 쓰라고 있는거 같아서 한번 간단하게 테스트 코드를 만들어 보려합니다.
예시 코드: https://github.com/falsy/blog-post-example/tree/master/http-response-header-link
아주 간단하게 nodejs, koa서버를 만들거에요.
1 |
$ npm init |
1 |
$ npm install --save-dev @koa/cors koa koa-body koa-logger koa-router |
기본적인 아이들을 설치해주고
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// index.js const koa = require('koa'); const cors = require('@koa/cors'); const koaBody = require('koa-body'); const logger = require('koa-logger'); const router = require('./router'); const run = async () => { const app = new koa(); const _router = router(router); app.use(cors()); app.use(logger()); app.use(koaBody()); app.use(_router.routes()); const port = 9999; const server = await app.listen(port); console.log(`server run ${port}`); return server; } run(); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// router.js const Router = require('koa-router'); module.exports = () => { const router = new Router(); router.get('/link-test', ctx => { ctx.set('Link', `<https://falsy.me>; rel="home"`); ctx.body = { results: 'ok' }; }); return router; }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!-- index.html --> <!DOCTYPE html> <html lang="ko"> <head> <title>HTTP-RESPONSE-HEADER-LINK</title> <meta charset="UTF-8" /> </head> <body> <script> fetch('http://localhost:9999/link-test', { method: 'GET' }).then(res => { res.headers.forEach((value, name) => { console.log(`${name }: ${ value}`); }); return res.json(); }) </script> </body> </html> |
이제 ‘$ node index.js’ 로 서버를 실행시켜주고 index.html 을 브라우저로 실행하고 개발자도구를 켜고 콘솔란을 보면 Link 속성이 딱!
나와야 하는데 나오지 않습니다.. 분명 개발자도구의 네트워크 탭에는 Response Header에 Link 값이 있는데, 클라이언트는 받을 수가 없더라고요.
분명히 CORS도 허용해 주었는데.
그래서 왜 그런지 구글 형, 누나에게서 알아보니.
https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types
If a request is made for a resource on another origin which returns the CORs headers, then the type is cors. cors and basic responses are almost identical except that a cors response restricts the headers you can view to ‘Cache-Control’, ‘Content-Language’, ‘Content-Type’, ‘Expires, ‘Last-Modified’, and ‘Pragma’.
CORS로 허용한 통신의 response header 값은 ‘Cache-Control’, ‘Content-Language’, ‘Content-Type’, ‘Expires, ‘Last-Modified’, ‘Pragma’ 로 제한 된다고 하네요.
아.. 뭔가 이상하다 싶어서 스텍오버플로 형, 누나에게 알아보니.
access-control-expose-headers worked for me for headers returned from server …
아하! header에 acces-control-expose-header 값에 Link 값을 허용하면 된다고 합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// router.js const Router = require('koa-router'); module.exports = () => { const router = new Router(); router.get('/link-test', ctx => { ctx.set('Link', `<https://falsy.me>; rel="home"`); ctx.set('Access-Control-Expose-Headers', 'Link'); ctx.body = { results: 'ok' }; }); return router; }; |
위와 같이 header에 ‘Access-Control-Expose-Headers’, ‘Link’를 허용해주고 서버를 재실행 후 index.html을 새로고침하면!
1 2 3 |
content-length: 16 content-type: application/json; charset=utf-8 link: <https://falsy.me>; rel="home" |
header에 link 값이 오는 것을 확인 할 수 있습니다.