JavaScript, DOM, Fetch, XMLHttpRequest, HTML 등의 다양한 기술을 사용하는 웹 개발 기법이다.
가장 큰 특징은 웹 페이지에 필요한 부분에 필요한 데이터만 비동기적으로 받아와 화면에 그려낼 수 있다는 것이다.
AJAX를 구성하는 핵심 기술은 JavaScript와 DOM, 그리고 Fetch이다.
Fetch를 사용하면, 페이지를 이동하지 않아도 서버로부터 필요한 데이터를 받아올 수 있다.
Fetch는 사용자가 현재 페이지에서 작업을 하는 동안 서버와 통신할 수 있도록 한다.
브라우저는 Fetch가 서버에 요청을 보내고 응답을 받을 때까지 모든 동작을 멈추는 것이 아니라, 계속해서 페이지를 사용할 수 있게 하는 비동기적인 방식을 사용한다.
자바스크립트에서 DOM을 사용해 조작할 수 있기 때문에, Fetch를 통해 전체 페이지가 아닌 필요한 데이터만 가져와 DOM에 적용시켜 새로운 페이지로 이동하지 않고 기존 페이지에서 필요한 부분만 변경할 수 있다.
Fetch 이전에는 XHR를 사용했다.
오늘날에는 XHR보다 Fetch를 많이 사용한다.
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://52.78.213.9:3000/messages');
xhr.onreadystatechange = function(){
if(xhr.readyState !== 4) return;
if(xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.log('error: ' + xhr.status);
}
}
xhr.send();
Fetch는 XHR의 단점을 보완한 새로운 Web API이며, XML보다 가볍고 JavaScript와 호환되는 JSON을 사용한다.
fetch('http://52.78.213.9:3000/messages')
.then (function(response) {
return response.json();
})
.then(function (json) {
...
});
서버쪽에서 렌더링 준비를 끝마친 상태로 클라이언트에 전달하는 방식이다.
- 사용자가 웹사이트 요청을 보낸다.
- 서버는 리소스를 체크하고 컴파일 후 렌더링 가능한 HTML파일을 만든다.
- 클라이언트에게 전달되는 순간 HTML은 즉시 렌더링 된다. (사용자가 사이트 조작 불가능)
- 클라이언트가 JavaScript를 다운 받는다.
- JavaScript를 다운 받는 중에 사용자는 컨텐츠를 볼 수 있지만 사이트는 조작할 수 없다. 이때의 사용자 조작을 기억하고는 있는다.
- 브라우저가 JavaScript 프레임워크를 실행한다.
- JavaScript 까지 컴파일이 되면 기억하고 있던 사용자 조작이 실행되고 웹 페이지는 상호작용이 가능해진다.
예제
const express = require("express"); const app = express(); const infoArr = [ "information1", "information2" ]; app.get("/", (req, res) => { res.send( "<html><body><h1>" + infoArr[Math.floor(Math.random() * infoArr.length)] + "</h1><h1>SSR</h1>" + "<h2>What is Server Side Rendering?</h2>" + "</body></html>" ); }); app.listen(8080);
서버에서 받은 HTML과 JavaScript를 클라이언트에서 렌더링하는 방식이다.
- 사용자가 웹사이트 요청을 보낸다.
- CDN이 HTML 파일과 JavaScript로 접근할 수 있는 링크를 클라이언트로 보낸다.
- 클라이언트는 HTML과 JavaScript를 다운로드 받는다. (사용자는 웹사이트의 아무것도 볼 수 없다.)
- 다운로드가 완료된 JavaScript가 실행되고 데이터를 위한 API가 호출된다 (사용자는 placeholder를 보게된다.)
- 서버가 API로부터의 요청에 응답한다.
- 클라이언트가 API로 부터 받아온 데이터를 placeholder 자리에 넣고 웹 페이지는 상호작용이 가능해진다.
예제
const express = require("express"); const app = express(); const port = process.env.PORT || 5000; const infoArr = [ "information1", "information2" ]; app.get(`/`, (req, res) => res.send(infoArr[Math.floor(Math.random() * infoArr.length)]) ); app.get(`/csr`, (req, res) => res.send(infoArr[Math.floor(Math.random() * infoArr.length)]) ); app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`) );
다른 도메인으로부터 리소스를 요청할 경우 해당 리소스는 cross-origin HTTP request에 의해 요청된다.
이 때 브라우저는 보안적인 이유로 스크립트 안에서 시작되는 cross-origin HTTP request를 제한한다.
브라우저가 자발적으로 사용자를 보호하기 위한 조치이다.
서버에서 서버로 보내는 요청은 CORS가 적용되지 않는다.
다른 도메인의 img파일이나 css파일을 가져오는 것은 모두 가능하지만
<scpipt></script>
로 감싸진 스크립트에서 생성된 cross-origin HTTP 요청은 Same-origin policy를 적용받아 cross-origin HTTP reauest가 제한된다.
브라우저는 먼저 서버에 사전 요청(Preflight request)을 전송하여 실제 요청을 보내는 것이 안전한지 OPTIONS method로 확인한다.
서버로부터 유효하다는 응답을 받으면 HTTP request method와 함께 본 요청(Actual request)을 보낸다.
서버로부터 유효하지 않다는 응다을 받으면 에러를 발생시키고 본 요청은 서버로 전송하지 않는다.
사전 요청(Preflight request)을 발생시키지 않는 요청을 Simple request라고 한다.
아래 세가지 조건이 모두 만족되는 경우를 말한다.
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
application / x-www-form-urlencoded
multipart/form-data
text/plain