본문 바로가기

프로그래밍/js

lessons 34. Ajax

2023.03.19

자바스크립트 스터디 9회차

공부 사이트: https://poiemaweb.com/

 

34. Ajax (Asynchronous JavaScript and XML)

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

서버로부터 웹페이지 반환 시 화면 전체를 갱신하지 않고, 일부만을 갱신할 수 있도록 하는 것이 Ajax.

 

1) JSON(Javascript Object Notation)

클라이언트와 서버 간 데이터 교환을 위한 데이터 포맷

{
    "name" : "홍길동",
    "gender" : "male",
    "age" : 20
}

※ 키는 반드시 큰 따옴표를 사용해야 함

 

① JSON.stringify(value [, replacer] [, space] )

객체를 JSON 형식의 문자열로 변환

- value : JSON 문자열로 변환할 값

- replacer : JSON 문자열에 포함될 값 value 객체의 속성을 선택하기 위한 화이트리스트. (String/Number 객체)

- space : 가독성을 목적으로 JSON 문자열 출력 시 공백 삽입할 때 사용되는 String/Number 객체

const o = {
    "name" : "홍길동",
    "gender" : "male",
    "age" : 20
};

// 객체 -> JSON
const strObject = JSON.stringify(o);
console.log(typeof strObject, strObject);

// 공백 옵션 사용
const strPrettyObject = JSON.stringify(o, null, 2);
console.log(typeof strPrettyObject, strPrettyObject);

// replacer로 사용할 함수  (값 타입이 Number면 필터링)
function filter(key, value) {
	return typeof value == 'number' ? undefined :value;
}

const strFilterdObject = JSON.stringify(o, filter, 2);
console.log(typeof strFilterdObject, strFilterdObject);

 

② JSON.parse(text [, reviver])

JSON 데이터를 가진 문자열을 객체로 변환

- text : JSON으로 변환할 문자열

- reviver : 변환 결과를 반환하기 전에 reviver의 인수로 전달하여 변형

const o = {
    "name" : "홍길동",
    "gender" : "male",
    "age" : 20
};

// 객체 -> JSON
const strObject = JSON.stringify(o);
console.log(typeof strObject, strObject);

// JSON -> 객체
const obj = JSON.parse(strObject);
console.log(typeof obj, obj);

※ 배열의 요소가 객체인 경우, 배열의 요소까지 객체로 변환된다.

 

 

 

2) XMLHttpRequest

브라우저는 XMLHttpRequest 객체를 이용하여 Ajax 요청을 생성하고 전송한다.

서버가 브러우저의 요청에 응답 반환 시 같은 XMLHttpRequst 객체가 그 결과를 처리한다.

 

① Ajax request

(1) XMLHttpRequest 객체의 인스턴스를 생성

(2) XMLHttpRequest.open() 를 통해 서버로의 요청 준비

  • XMLHttpRequest.open(method, url [, async])

(3) XMLHttpRequest.setRequestHeader()를 통해 요청 헤더 값 설정

  • open 메소드를 호출한 이후에 호출
  • 자주 사용하는 RequestHeader는 Content-type, Accept
  • Content-type: 요청 바디에 담아 전송할 데이터의 MIME-type 정보를 표현함. (text/plain, text/html, application/json ...)
  • Accept : HTTP 클라이언트가 서버에 요청할 떄 서버가 샌드백할 데이터의 MIME-type 지정 (미설정 시 Accept 헤더가 */*로 전송됨)

(4) XMLHttpRequest.send()를 통해 준비된 요청을 서버에 전달

  • 요청 바디에 담아서 전송할 값을 인수로 작성
  • send('string') / send(new Blob()) / send({ from: 'data'' }) / send(docuument)
  • 요청 메소드가 GET이면 send 메소드의 인수는 무시됨 (요청 바디는 null로 설정)

(5) XMLHttpRequest.onreadystatechange()를 통해 서버의 응답을 감지, 콜백함수 실행

  • 서버의 응답이 클라이언트에 도착하면 이벤트가 발생하는데, 이 이벤트는 XMLHttpRequest.readyState 프로퍼티가 변경되면 발생한다.
  • XMLHttpRequest.readyState의 값이 4이면 정상적으로 응답이 돌아온 것
Value State Description
0 UNSENT XMLHttpRequest.open() 메소드 호출 이전
1 OPENED XMLHttpRequest.open() 메소드 호출 완료
2 HEADERS_RECEIVED XMLHttpRequest.send() 메소드 호출 완료
3 LOADING 서버 응답 중
4 DONE 서버 응답 완료

 

 

 

3) WEB SERVER

ajax는 웹서버와의 통신이 필요하므로, 예제 실행하기 위해서는 웹서버가 필요하다.

> Node.js에서는 Express로 간단한 웹 서버 생성 가능

 

poiemaweb에서는 "git clone https://github.com/ungmo2/webserver-express.git" 하라고 했으나.. 리포가 없어져서 직접 만들었다.

아래는 Ajax를 이용하여 전송받은 데이터를 DOM에 추가하는 예제이다.

/**
1. 프로젝트 폴더 생성
2. npm init 통해 package.json 생성
3. npm install express
4. app.js 생성
**/

// app.js
const express = require('express');
const path = require('path');
const app = express();

app.use(express.static(path.join(__dirname, 'public')));

app.get('/', (req, res) => res.send('Hello World!'));

app.get('/loadhtml', function(req, res) {
    res.sendFile(__dirname + "/public/loadhtml.html")
})

app.listen(3000, () => console.log('Example app listening on port 3000!'));

http://localhost:3000 접속했을 때 "Hello World!" 표시되면 웹서버가 정상적으로 동작하는 것.

 

 

① HMTL 형식의 데이터 추가

<!-- public/loadhtml.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://poiemaweb.com/assets/css/ajax.css">
  </head>
  <body>
    <div id="content"></div>

    <script>
      const xhr = new XMLHttpRequest();
      xhr.open('GET', '/data/data.html');
      xhr.send();

      // Event Handler
      xhr.onreadystatechange = function () {
        if (xhr.readyState !== XMLHttpRequest.DONE) return;

        if (xhr.status === 200) {
          console.log(xhr.responseText);

          document.getElementById('content').innerHTML = xhr.responseText;
        } else {
          console.log(`[${xhr.status}] : ${xhr.statusText}`);
        }
      };
    </script>
  </body>
</html>

 

 

<!-- public/data/data.html -->
<div id="tours">
  <h1>Guided Tours</h1>
  <ul>
    <li class="usa tour">
      <h2>New York, USA</h2>
      <span class="details">$1,899 for 7 nights</span>
      <button class="book">Book Now</button>
    </li>
    <li class="europe tour">
      <h2>Paris, France</h2>
      <span class="details">$2,299 for 7 nights</span>
      <button class="book">Book Now</button>
    </li>
    <li class="asia tour">
      <h2>Tokyo, Japan</h2>
      <span class="details">$3,799 for 7 nights</span>
      <button class="book">Book Now</button>
    </li>
  </ul>
</div>

 

 

② JSON 형식의 데이터 추가

JSON 데이터는 문자열이기에, 웹 페이지에 로드하기 위해서는 역직렬화가 필요하다.

역직렬화를 위해서는 JSON의 static 메소드는 JSON.parse()를 사용한다.

<!-- loadhtml.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://poiemaweb.com/assets/css/ajax.css">
  </head>
  <body>
    <div id="content"></div>
    <script>
      const xhr = new XMLHttpRequest();
      xhr.open('GET', '/data/data.json');
      xhr.send();

      xhr.onreadystatechange = function () {
        if (xhr.readyState !== XMLHttpRequest.DONE) return;

        if (xhr.status === 200) {
          console.log(xhr.responseText);
		  
		  // 역직렬화 (String -> Object)
		  responseObject = JSON.parse(xhr.responseText);
		  
		  // JSON -> HTML String
          let newContent = '<div id="tours"><h1>Guided Tours</h1><ul>';

          responseObject.tours.forEach(tour => {
            newContent += `<li class="${tour.region} tour">
                <h2>${tour.location}</h2>
                <span class="details">${tour.details}</span>
                <button class="book">Book Now</button>
              </li>`;
          });

          newContent += '</ul></div>';

          document.getElementById('content').innerHTML = newContent;
        } else {
          console.log(`[${xhr.status}] : ${xhr.statusText}`);
        }
      };
    </script>
  </body>
</html>
<!-- /data/data.json -->
{
  "tours": [
    {
      "region": "usa",
      "location": "New York, USA",
      "details": "$1,899 for 7 nights"
    },
    {
      "region": "europe",
      "location": "Paris, France",
      "details": "$2,299 for 7 nights"
    },
    {
      "region": "asia",
      "location": "Tokyo, Japan",
      "details": "$3,799 for 7 nights"
    }
  ]
}

 

 

 

③ JSONP 형식의 데이터 추가

JSON with Padding이라고도 함.

 

보통 브라우저에서 보안상의 이유로 교차 출처 HTTP 요청을 제한하는데, 이를 SOP(Same Origin Policy, 동일 출처 원칙)라고 한다.

추가 HTTP 헤더를 사용하여 다른 출처의 자원에 접근할 수 있도록 하는 매커니즘이 CORS(Cross-Origin Resource Sharing, 교차 출처 자원 공유)이다.

XMLHttpRequest는 는 SOP(Same Origin Policy, 동일 출처 원칙)로, 교차 출처 HTTP 요청을 제한하기에, 이를 우회하기 위해 JSONP 형식을 이용한다.

 

<!-- loadhtml.html -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://poiemaweb.com/assets/css/ajax.css">
  </head>
  <body>
    <div id="content"></div>
  <script>
    function showTours(data) {
      console.log(data); // data: object

      // JSON → HTML String
      let newContent = `<div id="tours">
          <h1>Guided Tours</h1>
        <ul>`;

      data.tours.forEach(tour => {
        newContent += `<li class="${tour.region} tour">
            <h2>${tour.location}</h2>
            <span class="details">${tour.details}</span>
            <button class="book">Book Now</button>
          </li>`;
      });

      newContent += '</ul></div>';

      document.getElementById('content').innerHTML = newContent;
    }
    </script>
    <script src='https://poiemaweb.com/assets/data/data-jsonp.js'></script>
  </body>
</html>

참고로 위 코드에서 사용된 https://poiemaweb.com/assets/data/data-jsonp.js 의 코드는 다음과 같다.

showTours({
  "tours": [
    {
      "region": "usa",
      "location": "New York, USA",
      "details": "$1,899 for 7 nights"
    },
    {
      "region": "europe",
      "location": "Paris, France",
      "details": "$2,299 for 7 nights"
    },
    {
      "region": "asia",
      "location": "Tokyo, Japan",
      "details": "$3,799 for 7 nights"
    }
  ]
});

 


[TIP] CORS package

Node.js의 경우 CORS package를 사용하면 CORS를 간단하게 구현할 수 있다.

const cors = require('cors')

 

'프로그래밍 > js' 카테고리의 다른 글

lessons 36. SPA & Routing  (0) 2023.03.26
lessons 35. REST API  (0) 2023.03.23
lessons 32. 이벤트  (0) 2023.03.21
lessons 31. 동기식/비동기식 처리 모델  (0) 2023.03.03
lessons 30. DOM  (0) 2023.03.02