Technology/JavaScript

Ajax의 기본 사용법(Vanilla JavaScript)

ikjo 2021. 12. 26. 22:04

웹 개발 트렌드

최근(정확하게는 몇년 전부터) 웹 개발 트렌드는 jsp를 사용하지 않고 프론트엔드 코드와 API 코드를 따로 구성하여 운영하는 추세이다. 서버 코드가 클라이언트 페이지에 존재하면 유지보수도 어렵고 관리도 힘들기 때문이다. 즉, JSP 페이지를 없애고, 스크립트 단에서 API를 통해 HTML 파일로 데이터를 가져오는 경우가 많아진 것이다. 이때 프론트 엔드 주요 SPA 계열의 자바스크립트 프레임워크인 리액트, 앵귤러, 뷰 등이 사용된다.

 

 

Ajax란?

Ajax란 비동기 자바스크립트와 XML(Asynchronous JavaScript And XML)을 말하는데, 자바스크립트에서 서버와 통신하기 위해 XMLHttpRequest 객체를 사용하는 것을 말한다. 이때 JSON, XML, HTML  등 다양한 형식의 데이터를 주고 받을 수 있는데, JSON이 주를 이룬다.

 

Ajax의 강력한 특징은 페이지 전체를 새로고침 하지 않고서도(모든 데이터를 다시 불러오지 않고서도) 수행 되는 "비동기성"이다. 이러한 비동기성을 통해 사용자의 특정 Event가 있으면 전체 페이지가 아닌 일부분만을 업데이트 할 수 있게 해준다.(서버로부터 데이터를 받고 작업을 수행할 수 있음)

 

본 포스팅은 Ajax를 통해 비동기적으로 통신하는 단계를 크게 ① XMLHttpRequest 객체 생성 ② 요청 ③ 응답 총 3단계로 나누었고 이 순서에 가깝게 해당 내용을 다뤄볼려고 한다.

웹에서 Ajax 통신이 이루어지는 개략도

 

 

Ajax 1단계 : XMLHttpRequest 객체 생성

우선 Ajax를 이용하여 서버에 요청을 보내기 위해서는 XMLHttpRequest 객체를 생성해야한다.

// 서버에 Ajax 요청을 위한 XMLHttpRequest 객체 생성
let xhttp = new XMLHttpRequest();

// XMLHttpRequest 객체를 생성할 수 없는 경우 false 반환(아래 코드 실행 X)
if(!xhttp) {
	alert("XMLHttpRequest 객체를 생성할 수 없습니다.");
	return false;
}

 

또한 서버에 요청 하기 앞서, 서버로 보낸 요청에 대한 응답을 받았을 때 클라이언트가 어떤 동작을 할 것인지(어떤 함수를 호출할 것인지) 정해야 하는데, XMLHttpRequest 객체의 onreadystatechange 속성에 함수명을 기입하거나 직접적인 함수 본체를 기입하면 된다. 또는 XMLHttpRequest 객체(Event target)에 대해 addEventListener 함수를 이용해 'load' Event 발생 시 동작시킬 함수를 호출시키는 로직을 정의해도 무방하다. 이때 중요한 것은 그 함수로 어떠한 변수도 전달하지 않는 점이다.

xhttp.onreadystatechange = updateTodo; // 서버로부터 응답받았을 때 동작하는 함수 설정

// 서버로부터 응답받았을 때 동작하는 함수 로직 정의
function updateTodo() {
    (생략)
}

// 위 코드 대신 다음과 같이 로직을 정의할 수 있다.
/*

xhttp.addEventListener("load", () => {
   (생략)
 });

*/

 

지금까지 다룬 내용을 정리하여 JavaScript 코드로 나타내면 다음과 같다.

// 서버에 Ajax 요청을 위한 XMLHttpRequest 객체 생성
let xhttp = new XMLHttpRequest();

// XMLHttpRequest 객체를 생성할 수 없는 경우 false 반환(아래 코드 실행 X)
if(!xhttp) {
	alert("XMLHttpRequest 객체를 생성할 수 없습니다.");
	return false;
}

xhttp.onreadystatechange = updateTodo; // 서버로부터 응답받았을 때 동작하는 함수 설정

// 서버로부터 응답받았을 때 동작하는 함수 로직 정의
function updateTodo() {
    (생략)
}

// 위 코드 대신 다음과 같이 로직을 정의할 수 있다.
/*

xhttp.addEventListener("load", () => {
   (생략)
 });

*/

 

 

Ajax 2단계 : 요청

XMLHttpRequest 객체를 생성하고 서버로 부터 응답을 받은 후의 동작을 결정 한 뒤에는 XMLHttpRequest 객체의open() 메서드와 send() 메서드를 사용하여 서버에 요청을 보낸다.

 

open() 메서드

open() 메서드의 첫번째 파라미터에는 HTTP 요청 메서드(request method)가 기입하는데, 보통 서버의 데이터를 조회할 때는 GET, 서버에 데이터를 생성할 때는 POST, 서버의 데이터를 수정할 때는 PUT, 서버의 데이터를 삭제할 때는 DELETE를 기입한다. 여기서 요청 메서드는 대문자로 기입해야한다. 두번째 파라미터에는 요청하고자하는 URL을 기입한다. 세번째 파라미터에는 생략 가능한데(Default true), 요청을 비동기식으로 할지(true), 동기식으로 할지(false) 결정하는 것이다.

xhttp.open("PUT", "type"); // 서버에 PUT 방식으로 'type' URL을 요청(비동기식)

 

send() 메서드

send() 메소드의 파라미터에는 POST 요청 시 서버에 전송할 데이터를 기입하는데, JSON, multipart/form-data 등의 형식(format)으로 작성한다. 단, send()를 호출하기 전에는 전송하고자 하는 데이터 유형에 따른 MIME(Multipurpose Internet Mail Extensions)-Type을 먼저 설정 해야한다. 이때 XMLHttpRequest 객체의 setRequestHeader 메서드를 이용하는데, 첫번째 파라미터에 Content-Type을 기입하고 두번째 파라미터에는 문서, 파일 등의 유형에 따른 MIME-Type을 작성한다. 

※ GET 요청 시에는 send() 메서드 파라미터는 공란이며, 데이터를 전송할 때는 open() 메서드 두번째 파라미터 URL을 기입할 때 ?와 & 기호를 이용해서 파라미터 네임과 밸류를 작성한다.

xhttp.setRequestHeader("Content-type", 'application/x-www-form-urlencoded'); // Content type 헤더에 application/x-www-form-urlencoded를 설정함
xhttp.send("type=TODO&id=" + todoId); // application/x-www-form-urlencoded 형식의 데이터(Key=Value&...)

※ application/x-www-form-urlencoded MIME-Type은 &으로 분리되고, "=" 기호로 Key와 Value를 연결하는 형식의 데이터를 인코딩하는 방식이다. 이때 영어 알파벳이 아닌 문자들은 퍼센트 인코딩(인코딩 형식에 %가 들어감)되므로 주의해야한다.(근래 인코딩 형식은 국제 표준 UTF-8로 통일되는 추세이다.)

 

MIME-Type

MIME-Type은 문서, 파일 등 각각의 형식별 데이터를 텍스트로 인코딩한 것으로, 이를 통해 서버와 클라이언트간 문서, 파일 등 다양한 데이터를 주고받을 수 있을 뿐만 아니라 해당 데이터의 형식을 식별가능할 수 있도록 해준다. MIME-Type은 type(유형)/subtype(하위유형)의 구조로 되어있으며, 대소문자를 구분하지는 않으나 일반적으로 소문자를 사용한다. 웹에서는 수 많은 종류의 문서, 파일 등을 주고 받으므로 수 많은 종류의 MIME-Type들이 존재한다. 문서, 파일 등의 유형에 따른 MIME-Type의 종류는 아래와 같다.

유형 설명 MIME-Type
text 텍스트를 포함하는 모든 문서 text/plain, text/html, text/css, text/javascript 등
image 모든 종류의 이미지 image/gif, image/png, image/jpeg, image/bmp, image/webp 등
audio 모든 종류의 오디오 파일 audio/midi, audio/mpeg, audio/webm, audio/ogg, audio/wav 등
video 모든 종류의 비디오 파일 video/webm, video/ogg 등
application 모든 종류의 이진 데이터 application/json, application/octet-stream, application/pkcs12, application/vnd.mspowerpoint, application/xhtml+xml, application/xml,  application/pdf 등

 

지금까지 다룬 내용을 정리하여 JavaScript 코드로 나타내면 다음과 같다.

// 서버에 Ajax 요청을 위한 XMLHttpRequest 객체 생성
let xhttp = new XMLHttpRequest();

// XMLHttpRequest 객체를 생성할 수 없는 경우 false 반환(아래 코드 실행 X)
if(!xhttp) {
    alert("XMLHttpRequest 객체를 생성할 수 없습니다.");
    return false;
}


xhttp.onreadystatechange = updateTodo; // 서버로부터 응답받았을 때 동작하는 함수 설정
xhttp.open("PUT", "type"); // 서버에 PUT 방식으로 'type' URL을 요청  
xhttp.setRequestHeader("Content-type", 'application/x-www-form-urlencoded'); // Content type 헤더에 application/x-www-form-urlencoded를 설정함
xhttp.send("type=TODO&id=" + todoId); // application/x-www-form-urlencoded 형식의 데이터(Key=Value&...)

// 서버로부터 응답받았을 때 동작하는 함수 로직 정의
function updateTodo() {
    (생략)
}

// 11, 17~19 line 대신 다음과 같이 로직을 정희할 수 있다.
/*

xhttp.addEventListener("load", () => {
    (생략)
 });

*/

 

 

Ajax 3단계 : 응답

서버로 요청을 보낸 후에는 해당 요청이 제대로 보내졌는지 검사해야 한다. 이때 XMLHttpRequest 객체의 readyState 속성을 이용할 수 있는데, 이는 현재 Ajax 통신 상태에 따른 값으로 readyState가 가질 수 있는 상태값은 다음과 같다. 또한 XMLHttpRequest 객체는 각각의 상태를 속성으로 값는데 이 값은 readyState 상태값과 동일하다. 예를 들어 XMLHttpRequest.DONE의 값은 4를 갖는다.

상태 설명
UNSENT 0 초기화 안됨(XMLHttpRequest 객체 생성 O, open 메서드 호출 X)
OPENED 1 서버 연결 완료(open 메서드 호출 O, send 메서드 호출 X)
HEADERS_RECEIVED 2 서버가 요청을 받음(send 메서드 호출 O)
LOADING 3 서버가 요청을 처리하는 중
DONE 4 서버가 요청을 모두 처리했으며 응답할 준비가 완료됨

 

그 다음 모든 웹 통신이 그러하듯, Ajax 통신 역시 응답을 제대로 받았는지 즉, HTTP 응답 상태코드를 검사해야 한다. 이때 XMLHttpRequest 객체의 status 속성을 이용할 수 있므며, status의 속성값이 200이면 서버로부터 응답을 정상적으로 받은 것이다.

 

요청과 응답에 대한 검사가 끝난 후에는 서버로부터 응답받은 데이터를 통해 원하는 작업을 할 수 있는데, 해당 데이터에 접근하는 방법은 XMLHttpRequest 객체의 responseText 속성과 responseXML 속성이 있다. responseText 속성은 서버의 응답을 텍스트 문자열로 받아오며, responseXML 속성은 서버의 응답을 XMLDocument 객체로 받아온다. 이제 서버로부터 원하는 데이터를 응답받았고 해당 데이터에 접근하여 이를 처리할 수 있는 환경이 됐다

 

지금까지 다룬 내용을 정리하여 JavaScript 코드로 나타내면 다음과 같다.

// 서버에 Ajax 요청을 위한 XMLHttpRequest 객체 생성
let xhttp = new XMLHttpRequest();

// XMLHttpRequest 객체를 생성할 수 없는 경우 false 반환(아래 코드 실행 X)
if(!xhttp) {
    alert("XMLHttpRequest 객체를 생성할 수 없습니다.");
    return false;
}


xhttp.onreadystatechange = updateTodo; // 서버로부터 응답받았을 때 동작하는 함수 설정
xhttp.open("PUT", "type"); // 서버에 PUT 방식으로 'type' URL을 요청  
xhttp.setRequestHeader("Content-type", 'application/x-www-form-urlencoded'); // Content type 헤더에 application/x-www-form-urlencoded를 설정함
xhttp.send("type=TODO&id=" + todoId); // application/x-www-form-urlencoded 형식의 데이터(Key=Value&...)

// 서버로부터 응답받았을 때 동작하는 함수 로직 정의
function updateTodo() {
    if (xhttp.readyState === XMLHttpRequest.DONE) { // 서버가 모든 요청을 처리했고 응답할 준비가 되었는지?
        if (xhttp.status === 200) { // 서버로부터 정상적인 응답을 받았는지?
            let response = JSON.parse(xhttp.responseText);
            // 서버로부터 응답받은 데이터가 JSON 형식이라고 가정함
            // 응답받은 데이터를 JSON 형식의 객체로 사용하기 위해 파싱함
            // 위 작업을 하지 않으면 데이터가 모양만 JSON 형식이고 텍스트 그 자체이기 때문에, 이를 객체로 사용할 수 없게 된다.
        }
    }
}

// 11, 17~26 line 대신 다음과 같이 로직을 정희할 수 있다.
/*

xhttp.addEventListener("load", () => {
    let response = JSON.parse(xhttp.responseText);
 });

*/

 

 

Ajax 효용성

서두에서 언급했듯이 Ajax 통신의 이러한 일련의 과정들은 모두 새로고침 없이 비동기적으로 실시간으로 이루어지게 되며 이 덕에 모든 데이터를 다시 불러올 필요가 없어 사용자 입장에서는 짧은 로딩 시간으로 원하는 데이터를 조회할 수 있고 서버 입장에서도 트래픽을 절감시킬 수 있다. 또한 사용자 브라우저로부터 오는 각종 EVENT에 따라 유기적으로 Ajax를 통해 새로운 데이터를 불러오거나, UI 등의 디자인(색, 크기 등)을 변경하거나, 특정 DOM을 조작한다면 단지 동기적으로만 실행되는 웹 서비스 대비 보다 나은 사용자 UX를 제공할 수 있을 것이다.

 

 

 

참고 자료

  • https://developer.mozilla.org/ko/docs/Web/Guide/AJAX
  • https://developer.mozilla.org/ko/docs/Web/HTTP/Basics_of_HTTP/MIME_types