비동기식 처리모델 Ajax

728x90

참조:  https://poiemaweb.com/js-ajax

 

Ajax | PoiemaWeb

비동기식 처리 모델(Asynchronous processing model or Non-Blocking processing model)은 병렬적으로 작업을 수행한다. 즉, 작업이 종료되지 않은 상태라도 대기하지 않고 다음 작업을 실행한다는 의미이다. 예를 들어 서버에서 데이터를 가져와 화면에 표시하는 작업을 수행할 경우 서버에 데이터를 요청한 이후 서버로부터 데이터가 전달될 때까지 대기하지 않고(Non-Blocking) 즉시 다음 작업을 수행한다. 이후 서

poiemaweb.com

웹 페이지 전체를 새로 갱신하지않고 일부분만 갱신하고 싶다면 '아마도'(짧은 지식으로 이것이 전부다! 라고 하기에는 아직 겁이 많다.. 아니 10년, 20년이 지나도 확신하듯이 말할 수 있을지는 모르겠다) '비동기식 처리모델'이 필요한 시기인것같다. 하지만 생각보다 쓰는 방법이 쉽지않아 보인다. 실제로 내가 진행하고있는 프로젝트에서도 이 비동기 처리 때문에 꽤 오랫동안 삽질을 했다.(심지어 첫 프로젝트는 그 오류의 원인을 몰라서 임시방편만 처리하고 넘어갔었다) 설명을 진행하면서 삽질했던 부분도 간략히 짚으면서 넘어가겠다. 

1. Ajax (Asynchronous JavaScript and XML) 

브라우저에서 웹 페이지를 요청하거나 링크를 클릭하면 화면 갱신이 발생한다. 이것은 브라우저와 서버의 통신에 의한 것이다.

서버는 요청받은 페이지(HTML)를 반환하는데 이때 HTML에서 로드하는 CSS나 JavaScript 파일들도 같이 반환된다. 클라이언트의 요청에 따라 서버는 정적인 파일을 반환할 수도 있고 서버 사이드 프로그램이 만들어낸 파일이나 데이터를 반환할 수도 있다. 서버로부터 웹페이지가 반환되면 클라이언트(브라우저)는 이를 렌더링하여 화면에 표시한다. 

Ajax는 자바스크립트를 이용해서 비동기적(Asynchronous)으로 서버와 브라우저가 데이터를 교환할 수 있는 통신 방식을 의미한다.

서버로부터 웹페이지가 반환되면 화면 전체를 갱신해야 하는데 일부만을 갱신하고도 동일한 효과를 볼 수 있도록 하는 것이 Ajax이다. 페이지 전체를 로드하여 렌더링할 필요가 없고 갱신이 필요한 일부만 로드하여 갱신하면 되므로 빠른 퍼포먼스와 부드러운 화면 표시 효과를 기대할 수 있다. 

 

2. JSON (JavaScript Object Notation)

클라이언트와 서버 간에는 데이터 교환이 필요하다. JSON은 클라이언트와 서버 간 데이터 교환을 위한 데이터 포맷이다.

JSON은 일반 텍스트 포맷보다효과적인 데이터 구조화가 가능하며 XML 포맷보다 가볍고 사용하기 간편하며 가독성도 좋다.

자바스크립트객체 리터럴과 매우 흡사하다. 하지만 JSON은 순수한 텍스트로 구성된 규칙이 있는 데이터 구조이다.

{
    "name": "Lee",
    "gender": "male",
    "age": 20,
    "alive": true
}

key는 반드시 쌍따옴표(작은따옴표 x)로 둘러싸야한다.

2.1 JSON.stringfy

JSON.stringfy 메서드는 객체를 JSON 형식의 문자열로 변환한다.

const o = { name: 'Lee', gender: 'male', age: 20 };

const strObject = JSON.stringify(o);
console.log(typeof strObject, strObject); 

// string {"name":"Lee","gender":"male","age":20}

// 객체 => JSON 형식의 문자열 + prettify
const strPrettyObject = JSON.stringify(o, null, 2);
console.log(typeof strPrettyObject, strPrettyObject);

/*
string {
  "name": "Lee",
  "gender": "male",
  "age": 20
}
*/

 

2.2 JSON.parse

JSON.parse 메서드는 JSON 데이터를 가진 문자열을 객체로 변환한다.

더보기

서버로부터 브라우저로 전송된 JSON 데이터는 문자열이다. 이 문자열을 객체로서 사용하려면 객체화하여야 하는데 이를 역직렬화(Deserializing)이라 한다. 역직렬화를 위해서 내장 객체 JSON의 static 메서드인 JSON.parse를 사용한다.

 

const o = { name: 'Lee', gender: 'male', age: 20 };

// 객체 => JSON 형식의 문자열
const strObject = JSON.stringify(o);
console.log(typeof strObject, strObject);
// string {"name":"Lee","gender":"male","age":20}

// JSON 형식의 문자열 => 객체
const obj = JSON.parse(strObject);
console.log(typeof obj, obj); // object { name: 'Lee', gender: 'male' }


const arr = [1, 5, 'false'];

// 배열 객체 => 문자열
const strArray = JSON.stringify(arr);
console.log(typeof strArray, strArray); // string [1,5,"false"]

// 문자열 => 배열 객체
const objArray = JSON.parse(strArray);
console.log(typeof objArray, objArray); // object [1, 5, "false"]

배열이 JSON 형식의 문자열로 변환되어 있는 경우 JSON.parse는 문자열을 배열 객체로 변환한다. 배열의 요소가 객체인 경우 배열의 요소까지 객체로 변환한다. 

const todos = [
  { id: 1, content: 'HTML', completed: true },
  { id: 2, content: 'CSS', completed: true },
  { id: 3, content: 'JavaScript', completed: false }
];

// 배열 => JSON 형식의 문자열
const str = JSON.stringify(todos);
console.log(typeof str, str);

// JSON 형식의 문자열 => 배열
const parsed = JSON.parse(str);
console.log(typeof parsed, parsed);

 

3. XMLHttpRequest

브라우저는 XMLHttpRequest 객체를 이용하여 Ajax 요청을 생성하고 전송한다. 서버가 브라우저의 요청에 대한 응답을 반환하면 같은 XMLHttpRequest 객체가 그 결과를 처리한다.

(아마도 대다수의 개발자들은 jQuery로 Ajax를 처리하고 있지 않을까 생각한다. jQuery 라이브러리에 의존하지 않고 비동기식 처리를 하기 위해서는 XMLHttpRequest 객체가 필요하다.)

3.1 Ajax request

다음은 Ajax 요청 처리의 예제이다.

// XMLHttpRequest 객체의 생성
const xhr = new XMLHttpRequest();
// 비동기 방식으로 Request를 오픈한다
xhr.open('GET', '/users');
// Request를 전송한다
xhr.send();

 

3.1.1 XMLHttpRequest.open

XMLHttpRequest객체의 인스턴스를 생성하고 XMLHttpRequest.open 메서드를 사용하여 서버로의 요청을 준비한다. XMLHttpRequest.open의 사용법은 아래와 같다.

XMLHttpRequest.open(method, url, async);

 

매개변수 설명
method HTTP method (GET, POST, PUT, DELETE 등)
url 요청을 보낼 URL
async 비동기 조작 여부, 옵션으로 default는 true이며 비동기 방식으로 동작한다.

 

3.1.2 XMLHttpRequest.send

XMLHttpRequest.send 메서드로 준비된 요청을 서버에 전달한다.

기본적으로 서버로 전송하는 데이터는 GET, POST 메서드에 따라 그 전송 방식에 차이가 있다.

  • GET 메서드의 경우, URL의 일부분인 쿼리문자열(query String)로 데이터를 서버로 전송한다.
    (ex: www.naver.com?id=myid&password=mypassword) 
  • POST 메서드의 경우, 데이터(페이로드)를 Request Body에 담아 전송한다.

XMLHttpRequest.send 메서드에는 request body에 담아 전송할 인수를 전달할 수 있다.

xhr.send(null);
// xhr.send('string');
// xhr.send(new Blob()); // 파일 업로드와 같이 바이너리 컨텐트를 보내는 방법
// xhr.send({ form: 'data' });
// xhr.send(document);

만약 요청 메서드가 GET인 경우, send 메서드의 인수는 무시되고 request bodynull로 된다.

 

3.1.3 XMLHttpRequest.setRequestHeader

XMLHttpReqest.setRequestHeader 메서드는 HTTP Reqeust Header의 값을 설정한다. setReqeustHeader 메서드는 반드시 XMLHttpRequest.open 메서드 호출 이후에 호출한다. 

자주 사용하는 Request Header인 Content-type, Accept에 대해 알아보자.

Content-type

Content-type은 request body에 담아 전송할 데이터의 MIME-type의 정보를 표현한다. 자주 사용되는 MIME-type은 아래와 같다.

타입 서브타입
text타입 text/plain, text/html, text/css, text/javascript
Application타입 application/json, applicaion/x-www-form-urlencode
File을 업로드하기 위한 타입 multipart/formed-data

 다음은 request body에 담아 서버로 전송할 데이터MIME-type을 지정하는 예이다.

// json으로 전송하는 경우
xhr.open('POST', '/users');

// 클라이언트가 서버로 전송할 데이터의 MIME-type 지정: json
xhr.setRequestHeader('Content-type', 'application/json');

const data = { id: 3, title: 'JavaScript', author: 'Park', price: 5000};

xhr.send(JSON.stringify(data));

 

// x-www-form-urlencoded으로 전송하는 경우
xhr.open('POST', '/users');

// 클라이언트가 서버로 전송할 데이터의 MIME-type 지정: x-www-form-urlencoded
// application/x-www-form-urlencoded는 key=value&key=value...의 형태로 전송
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

const data = { title: 'JavaScript', author: 'Park', price: 5000};

xhr.send(Object.keys(data).map(key => `${key}=${data[key]}`).join('&'));
// escaping untrusted data
// xhr.send(Object.keys(data).map(key => `${key}=${encodeURIComponent(data[key])}`).join('&'));

 

Accept

HTTP 클라이언트가 서버에 요청할 때 서버가 돌려줄 데이터의 MIME-type을 Accept로 지정할 수 있다.

다음은 서버가 센드백할 데이터의 MIME-type을 지정하는 예다.

// 서버가 센드백할 데이터의 MIME-type 지정: json
xhr.setRequestHeader('Accept', 'application/json');

 만약 Accept 헤더를 설정하지 않으면, send 메서드가 호출될 때 Accept 헤더가 */*으로 전송된다.

 

3.2 Ajax response

다음은 Ajax 응답 처리의 예다.

// XMLHttpRequest 객체의 생성
const xhr = new XMLHttpRequest();

// XMLHttpRequest.readyState 프로퍼티가 변경(이벤트 발생)될 때마다 onreadystatechange 이벤트 핸들러가 호출된다.
xhr.onreadystatechange = function (e) {
  // readyStates는 XMLHttpRequest의 상태(state)를 반환
  // readyState: 4 => DONE(서버 응답 완료)
  if (xhr.readyState !== XMLHttpRequest.DONE) return;

  // status는 response 상태 코드를 반환 : 200 => 정상 응답
  if(xhr.status === 200) {
    console.log(xhr.responseText);
  } else {
    console.log('Error!');
  }
};

위 코드를 자세히 보자. (이 부분을 제대로 읽지 않으면 나와 같은 삽질을 할 것이다!) 

XMLHttpRequst.send 메서드를 통해 서버에 Request를 전송하면 서버는 Response를 반환한다. 하지만 언제 Response가 클라이언트에 도달할 지는 알 수 없다. XMLHttpRequest.onreadystatechangeResponse가 클라이언트에 도달하여 발생된 이벤트를 감지하고 콜백함수를 실행하여 준다. 이때 이벤트는 Request에 어떤 변화가 감지된 경우 즉 XMLHttpRequest.readyState 프로퍼티가 변경된 경우 발생한다. 

// XMLHttpRequest 객체의 생성
var xhr = new XMLHttpRequest();
// 비동기 방식으로 Request를 오픈한다
xhr.open('GET', 'data/test.json');
// Request를 전송한다
xhr.send();

// XMLHttpRequest.readyState 프로퍼티가 변경(이벤트 발생)될 때마다 콜백함수(이벤트 핸들러)를 호출한다.
xhr.onreadystatechange = function (e) {
  // 이 함수는 Response가 클라이언트에 도달하면 호출된다.
};

XMLHttpRequest 객체는 response가 클라이언트에 도달했는지를 추적할 수 있는 프로퍼티를 제공한다. 이 프로퍼티는 XMLHttpRequest.readyState 다. 만일 XMLHttpReqeust.readyState의 값이 4인 경우, Response가 돌아온 경우이다. (이 설정을 제대로 안한다면 비동기 처리는 4회를 반복하게 되고 (2번은 정보를 받아오지 못함) 같은 데이터를 2번 받아오게 된다)

readXMLHttpRequest.readyState의 값은 아래와 같다.

Value State Description
0 UNSET XMLHttpRequest.open() 메소드 호출 이전
1 OPENED XMLHttpRequest.open() 메소드 호출 완료
2 HEADERS_RECEIVED XMLHttpRequest.send() 메소드 호출 완료
3 LOADING 서버 응답 중(XMLHttpRequest.responseText 미완성 상태)
4 DONE 서버 응답 완료

 

// XMLHttpRequest 객체의 생성
var xhr = new XMLHttpRequest();
// 비동기 방식으로 Request를 오픈한다
xhr.open('GET', 'data/test.json');
// Request를 전송한다
xhr.send();

// XMLHttpRequest.readyState 프로퍼티가 변경(이벤트 발생)될 때마다 콜백함수(이벤트 핸들러)를 호출한다.
xhr.onreadystatechange = function (e) {
  // 이 함수는 Response가 클라이언트에 도달하면 호출된다.

  // readyStates는 XMLHttpRequest의 상태(state)를 반환
  // readyState: 4 => DONE(서버 응답 완료)
  if (xhr.readyState !== XMLHttpRequest.DONE) return;

  // status는 response 상태 코드를 반환 : 200 => 정상 응답
  if(xhr.status === 200) {
    console.log(xhr.responseText);
  } else {
    console.log('Error!');
  }
};

 

XMLHttpReqeust의 readyState가 4인 경우, 서버 응답이 완료된 상태이므로 이후 XMLHttpReqeust.status가 200(정상 응답)임을 확인하고 정상인 경우, XMLHttpRequest.responseText(= 받아온 데이터)를 취득한다. XMLHttpRequestText에는 서버가 전송한 데이터가 담겨 있다. 

 

728x90

'프로그래밍 공부 > JavaScript' 카테고리의 다른 글

js - ES6 문법 정리  (0) 2020.02.15
js - 메서드 정의  (0) 2020.02.13