Ajax 통신 방식이란?
Ajax(Asynchronous JavaScript and XML): 서버와 브라우저가 데이터를 교환할 수 있도록 지원하는 통신 방식이다.
특징:
1) 페이지 일부만을 갱신하고도 동일한 효과 가능, XHTML, CSS, JavaScript, DOM을 활용해서 동적인 화면 콘텐츠 구현, 빠르고 동적인 web page 생성가능하며 browser나 platform에 독립적임
2)비동기적(Asynchronous) 방식 구현
위 특징들에 대한 상세 설명:
Browser에서 웹페이지 요청시 화면이 전환되는데, 이것은 browser - server 사이의 통신에 의한것이다. 서버로부터 웹페이지가 반환되면 HTML화면 전체를 갱신해야 하는데 ajax를 사용하면 페이지의 일부분만 갱신하고 동일한 결과를 얻을 수 있다.
Fig1. 기존의 web page life cycle
위 그림에서 기존의 통신 및 렌더링 방식을 볼 수 있다. 브라우저에서 웹페이지를 요청하거나 링크를 클릭하면, 브라우저와 서버와의 통신에 의해서 화면 전환이 발생한다. (그림의 "Page Reloaded") 클라이언트의 요청에 따라 서버는 정적인 파일을 반환할 수도 있고 서버 사이드 프로그램이 만들어낸 파일이나 데이터를 반환할 수도 있다. 서버로부터 웹페이지가 반환되면 클라이언트(브라우저)는 이를 모두 렌더링하여 화면에 표시한다. (rendering=렌더링=화면에 구현)
Fig2. Ajax 방식의 web page life cycle
Fig.1 vs. Fig.2에서 비교되는 바와 같이 Ajax 통신 방식을 사용하면, Ajax 요청을 받은 서버가 response를 보낼때 (페이지(HTML)를 반환 + 필요시 CSS or JavaScript파일들도 함께) 페이지 전체를 로드하여 렌더링하지않고 갱신이 필요한 일부만 로드한다. 이렇게 화면을 갱신하면 빠른 퍼포먼스와 부드러운 화면 표시가 가능하다.(그림의 "DOM Manipulation" - 필요한 document 객체만 갱신)
Synchronous방식은 block방식으로서, 하나의 event 작업이 resource(e.g., data from the DB server)를 다 사용하고 끝날때까지 다른 event는 기다려야한다.
반면, asynchronous방식은 non-blocking 방식으로서, 여러 event를 처리할 때에, event를 각각 하나씩 처음부터 끝까지 완료해서 순서대로 진행하지않고, event request가 들어오는데로 각 event가 각 다른 timing에 시작하고 끝나도록 진행한다.
하나의 event를 시작하고, 이 event가 다 끝나기전에 다음 event롤 시작해서 더 빠르게 server의 file을 access하고 client에게 content를 제공할 수 있다. JavaScript의 대부분 DOM event와 timer 함수(setTimer, setTimeout, setInterval), ajax 요청은 비동기적으로 동작한다.
브라우저는 XMLHttpRequest 객체를 이용하여 Ajax 요청을 생성한다. 그리고 이 객체는 서버가 브라우저의 요청에 대해 응답을 반환하면 그 결과를 처리하는 역할도 한다.
아래 요청의 예시를 보여주는 코드를 보면, 먼저 XMLHttpRequest 객체의 인스턴스를 생성한다. 그리고 XMLHttpRequest.open() 메소드를 사용하여 서버의 요청을 준비한다. XMLHttpRequest.send() 메소드로 요청을 서버에게 전달한다.
Ajax 요청 처리의 예)
// XMLHttpRequest 객체의 생성
var req = new XMLHttpRequest();
// 비동기 방식으로 Request를 오픈한다
req.open('GET', 'data/test.json', true);
// Request를 전송한다
req.send();
서버가 요청을 받으면 응답을 반환해주는데, 언제 response(응답)이 클라이언트에 도달하는지는 알 수 없다. 그래서 XMLHttpRequest.onreadystatechange가 응답이 클라이언트에 도달해서 발생된 이벤트를 감지하고 callback함수를 실행해준다. (응답 도달시 발생된 이벤트? =Request의 XMLHttpRequest.readyState 속성(property)이 변경된것. 0,1,2,3,4 총 5개의 값있음. 아래 table 참고)
응답 처리의 예)
// XMLHttpRequest.readyState 프로퍼티가 변경(이벤트 발생)될 때마다 콜백함수(이벤트 핸들러)를 호출한다.
req.onreadystatechange = function (e) {
// readyStates는 XMLHttpRequest의 상태(state)를 반환
// readyState: 4 => DONE(서버 응답 완료)
if (req.readyState === XMLHttpRequest.DONE) {
// status는 response 상태 코드를 반환 : 200 => 정상 응답
if(req.status == 200) {
console.log(req.responseText);
} else {
console.log("Error!");
}
}
};
위 코드의 첫번째줄을 보면(주석 말고!), request의onreadystatechange는 익명함수로 event handler를 작성하는 형태와 같다. 실제로 XMLHttpRequest.onreadystatechange는 XMLHttpRequest.readyState 프로퍼티가 변경될 때마다 호출되는 EventHandler이다.
XMLHttpRequest.readyState의 값이 4인 경우, 정상적으로 Response가 돌아온 경우이다.
XMLHttpRequest의.readyState가 4인 경우, 서버 응답이 완료된 상태이므로 이후 XMLHttpRequest.status가 200(정상 응답)임을 확인하고 정상인 경우, XMLHttpRequest.responseText를 취득한다. XMLHttpRequest.responseText에는 서버가 전송한 데이터가 담겨 있다.
클라이언트쪽이 responseText로 반환받은 데이터는 further 가공이 필요할때가 있다. 그때가 바로 response 데이터가 JSON형식으로 도착했을때이다.
서버는 HTML, XML, JSON등을 반환하는데 Ajax을 위한 데이터 형식은 JSON(JavaScript Object Notation)을 사용하는 것이 일반적이다. 자바스크립트의 객체 리터럴과 매우 흡사하다.(but JSON은 순수 켁스트로만 구성된 데이터임)
JSON형식의 예시)
{
"name": "Lee",
"gender": "male",
"age": 20,
"alive": true
}
note: key는 must be double quoted! (single quotes사용불가!)
사람이 직관적으로 이해하고, 파싱하고 생성하기 쉬우며, 데이터 교환을 위한 형식으로 '미니 XML'이라 불린다. XML보다 가볍고 빨라 효율적이다.
JSON을 다루는 methods:
-JSON.stringify() 메소드는 객체를 JSON 형식의 문자열로 변환한다.
-JSON.parse() 메소드는 JSON 데이터를 가진 문자열을 객체로 변환한다.
서버로부터 브라우저로 전송된 JSON 데이터는 문자열이기때문에 이 문자열을 객체화하여야 하는데, 이를 역직렬화(Deserializing)이라 한다. 역직렬화를 위해서 내장 객체 JSON의 static 메소드인 JSON.parse()를 사용하는 것이다. (**static의 의미: new로 객체를 생성하지않고 별도로 그냥 쓸 수 있는 메소드)
예시)
// JSON 형식의 문자열 => 객체
var obj = JSON.parse(strObject);
console.log(typeof obj, obj); // object { name: 'Lee', gender: 'male' }
// 문자열 => 배열 객체
var objArray = JSON.parse(strArray);
console.log(typeof objArray, objArray); // object [1, 5, "false"]
Node.js를 다운받고 Visual Studio Code에서 이 platform을 활용해 보았다. 실습으로 ajax 통신 방식을 구현했다. 먼저 Ajax 통신 방식에서 필요한 web server를 생성해야한다. require keyword로 express framework을 import해서 node_modules 폴더에 사용할 Javascript library를 확보한다. Node.js 설치할때에 함께 받는 npm(Node package manage)외에 지금과 같이 third party package를 받으면 node_modules 폴더에 저장된다.
(side note: express framework이란? Express Overview: minimal & flexible Node.js web application framework that provies robust set of featurs to develop web and mobile apps)
예전에는 (Node.js가 처음 소개된 2009년 이전에는) JavaScript는 front-end에서만 주로 활용되었었는데, Node.js는 JavaScript가 front-end외에도 server-application을 구현할 수 있게 해준다.
Node.js is a JavaScript runtime environment built on Chrome's V8 JavaScript engine.
Node.js builds scalable network applications and executes JavaScript code outside a web browser.
JavaScript를 interpret하기위해 각 browser는 JavaScript engine을 사용한다. Chrome은 V8 Javascript engine을, Mozilla는 Spidermonkey engine을 사용한다. Node.js는 V8 Javascript engine을 기반으로 한 runtime environment를 제공해서 개발자가 browser밖에서도 JavaScript로 작성된 프로그램을 실행할 수 있게 해준다.
Node.js는 JavaScript 언어 자체의 기존 프로그래밍 방식에 아주 유용한 특징을 하나 추가했다. Event-driven & non-blocking I/O model 방식을 따르는 것이다.
먼저 event란 무엇인가? client가 server에게 request를 보내어서 server가 처리해야하는 task가 발생하는 것을 event가 발생했다고 한다.
Event = client가 요청해서 server가 처리해야하는 작업 (e.g., node.js에 있는 파일을 열거나 닫기, file 쓰기, 등등)
Ajax의 특징을 얘기할때에 나온 concept과 동일하다.
Event-driven & non-blocking 방식은 asynchronous programming을 구현한다는 것인데, Node.js는 여러 event를 처리할 때에, event를 각각 하나씩 처음부터 끝까지 완료해서 순서대로 진행하지않고, event request가 들어오는데로 각 event가 각 다른 timing에 시작하고 끝나도록 진행한다.
하나의 event를 시작하고, 이 event가 다 끝나기전에 다음 event롤 시작해서 single-thread 방식으로 진행하는것이다. (yet, 그 안을 들여다보면 여러 event가 순차적으로 각각 다른 timing에 진행되고있는 multi-threaed 형태) 그래서 더 빠르게 server의 file을 access하고 client에게 content를 제공할 수 있다.
실습에서는 연결 port로는 Localhost:3000을 사용하고 아래와 같은 JavaScript code로 서버를 생성했다.
사용할 folder에 index.js로 아래 코드조각을 저장한다.
const express = require('express')
const app = express()
const port = 3000
const path = require('path');
app.use('/', express.static(path.join(__dirname, 'public')));
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Terminal에서 사용할 폴더(경로 C:|Project|Ajax)를 열고, 위에 작성한 js 파일을 아래와 같이 호출한다.
C:|Project|Ajax> node index.js
Ajax 폴더의 public 디렉토리와 가상 server와의 연결이 형성된다. public 디텔토리안에 tutorial3 폴더를 생성하고, 그 안에 server-client의 ajax 통신방식을 구현 할 index.html과 server가 응답으로 보낼 데이터를 담고있는 jsontext.json을 작성한다.
예시) index.html:
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>튜토리얼3</title>
</head>
<body>
<h1>튜토리얼3 - json파일</h1>
<button id="button">json파일 받아오기</button>
<hr>
<div id="text"></div>
<script>
// 버튼 이벤트 등록
document.getElementById('button').addEventListener('click', requestLoadText);
function requestLoadText() {
// XMLHttpRequest 오브젝트 생성
var xhr = new XMLHttpRequest();
// 초기화
// Method : Http Request Method를 GET으로 설정
// URL : 서버상의 파일위치 설정
// Async : 비동기롤 설정(true는 비동기, false는 동기
xhr.open('GET', 'jsontext.json', true); //connection 준비 using open()
// 리퀘스트가 완료 이벤트 등록// Request finished. Do processing here.
xhr.onload = function () {
// ReadyState체크
// UNSENT : 초기화가 되지 않은 상태
// OPENED : 서버 통신이 시작인 안된 상태
// HEADERS_RECEIVED : 레스폰스 헤더를 받은 상태
// LOADING : 콘텐츠를 다운로드 중인 상태
// DONE : 서버 통신이 종료된 상태
// https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState
if (xhr.readyState != xhr.DONE) {
return;
}
// Http에러 체크
// 200 - 성공
// 403 - "Forbidden"
// 404 - "Not Found"
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status 참고
if (this.status == 200) {
document.getElementById("text").innerHTML = createTable(this.responseText);
return;
}
document.getElementById('text').innerHTML = 'Http' + this.status + '에러가 발생 했습니다';
}
// 리퀘스트 에러 이벤트 등록
xhr.onerror = function () {
document.getElementById('text').innerHTML = '에러가 발생 했습니다';
}
// 리퀘스트를 서버에게 요청
xhr.send();
}
function createTable(jsonText) {
var students = JSON.parse(jsonText);
var table = "<table>"
+ "<tr>"
+ "<th>" + "아이디" + "</th>"
+ "<th>" + "이름" + "</th>"
+ "<th>" + "학년" + "</th>"
+ "<th>" + "이메일" + "</th>"
+ "</tr>";
for (var i = 0; i < students.length; i++) {
var student = students[i];
table += "<tr>"
+ "<td>"
+ student.id
+ "</td>"
+ "<td>"
+ student.name
+ "</td>"
+ "<td>"
+ student.grade
+ "</td>"
+ "<td>"
+ student.email
+ "</td>"
+ "</tr>";
}
table += "</table>";
return table;
}
</script>
</body>
</html>
jsontext.json:
[
{
"id": "10000001",
"name": "홍길동",
"grade": "1학년",
"email": "s10000001@gmail.com"
},
{
"id": "10000002",
"name": "김철수",
"grade": "2학년",
"email": "s10000002@gmail.com"
},
{
"id": "10000003",
"name": "이영희",
"grade": "3학년",
"email": "s10000003@gmail.com"
}
]
Reference
1. 7.2 jQuery Ajax & JSON 비동기식 처리 모델과 Ajax from Poiemaweb
2. Mozilla Web API | XMLHttpRequest
3. What is Node.js? by Olusola Samuel from Medium
4. W3Schools.com | Node.js Introduction