JavaScript

[ajax]3. jquery를 이용한 ajax

  • -
반응형

$(document).on({
  // 모든 버튼을 disabled 상태로 바꿈
  ajaxStart: function () {
    $("button").prop("disabled", true);
  },
  // 모든 버튼을 enabled 상태로 바꿈
  ajaxStop: function () {
    $("button").prop("disabled", false);
  },
});

이번에는 jquery를 이용해서 vanilla script에서의 너저분했던 코드를 간략히 처리해보자.

 

AJAX를 위한 jQuery API

 

기본 함수

메서드 설명
$.get() 데이터를 get 방식으로 서버에 전송 후 응답을 받을 때 사용
$.post() 데이터를 post 방식으로 서버에 전송 후 응답을 받을 때 사용
$.getJSON() 데이터를 get 방식으로 서버에 전송 후 JSON형식으로 응답을 받을 때 사용
$.getScript() Ajax를 이용하여 외부 자바 스크립트를 호출 할 때 사용
$.ajax() $.post(), $.get(), $.getJSON()을 하나로 합쳐 놓은 메서드


$.ajax(settings):
settings: JSON 형태로 필요한 property 설정

 

jQuery에는 ajax를 위해 여러 가지 함수를 제공하는데 사실 마지막에 있는 $.ajax 함수 하나만 사용하면 된다. 나머지는 단지 get인지 post인지에 따라 아주 조금 편의성을 제시할 뿐이다.

 

settings 객체 구성

jQuery의 ajax 관련 함수는 어떻게 ajax 요청을 할것인지에 대한 정보를 JSON 객체 형태로 전달한다.(통상 이런 객체를 settings 객체 라고 한다.) 

다음은 settings 객체의 주요 속성들이다.

속성 타입 설명
url String request를 전달할 서버의 url
type String get 또는 post로 기본값은 get
data object, String, Array 서버로 전송할 데이터
object: { key:value, key:value}
Array: [{ key:value, key:value}, { key:value, key:value}]
String: URL 인코딩된
contentType String or Boolean 서버로 전송할 데이터의 content-type
기본 값은 application/x-www-form-uriencoded; charset=UTF-8
false일 경우 어떤 contentType도 설정하지 않음
request bodyjson 객체를 전달 시는 application/json 설정
dataType String 응답 받을 데이터의 타입으로 xml, json, script, html, text
생략 시 MIME 타입에 맞춰 자동 설정
processData Boolean data로 서버로 전송하는 값을 queryString 형태로 변환할 것인지 설정
기본은 true 이며 파일 업로드 등으로 불필요할 경우 false 설정
headers JSON 서버로 전송될 request headerJSON 객체 형태로 작성
success function(res)
전송이 성공했을 경우 호출될 call back function
error function(xhr)
전송이 실패했을 경우 호출될 call back function. xhr에 오류 정보 포함

여러 가지의 속성들이 있지만 필수적으로 사용되는 것은 url, type, success, error 4가지이다. 추가로 요청 시 서버로 전달할 데이터가 있다면 data 속성이 사용된다.

 

jquery를 이용한 목록 조회

앞서 vanilla script를 이용해서 처리했던 내용을 jquery를 이용해서 처리해보자.

let posts;
$("#btnList").on("click", function () {
  $.ajax({
    type: "get",
    url: "https://jsonplaceholder.typicode.com/posts",
    success: function (res) {
      console.log(res);
      if (res) {
        $("#content").empty();
        posts = res;
        $.each(posts, function (idx, post) {
          $("#content").append(`<tr><td>${post.userId}</td>
                                  <td>${post.id}</td>
                                  <td>${post.title}</td>
                                  <td>${post.body}</td>
                                  <td></td></tr>`);
        });
      }
    },
    error: function (xhr) {
      console.log(xhr.status + " : " + xhr.statusText);
    },
  });
});

한눈에 보더라도 코드가 훨씬 간단해졌다. 물론 jquery를 사용하면서 dom을 처리하는 부분이라든가 이벤트를 처리하는 부분들도 간단해졌지만 ajax를 처리하는 부분 역시 간단해진것을 부정할 수 없을 것이다.

 

파라미터 전달

이번에는 요청 시 파라미터를 전달해보자. 이번에 사용할 url은 https://jsonplaceholder.typicode.com/comments?postId=1 이다. 즉 서버로 postId를 key로 1이라는 value를 전달해야 한다.

get 방식에서 파라미터를 전달할 경우는 위 경로처럼 url 뒤에 ?를 붙인 후 파라미터를 기입해주면 된다.

 

하지만 post 방식에서는 url을 사용할 수 없고 data라는 속성을 이용해야 한다. 물론 get 방식에서도 data 속성을 이용할 수 있다. data에는 객체 또는 배열 또는 문자열을 사용할 수 있다.

속성 타입 설명
data object, String, Array 서버로 전송할 데이터
object: { key:value, key:value}
Array: [{ key:value, key:value}, { key:value, key:value}]
String: URL 인코딩된

 

이제 1번 글에 대한 comment 들만 조회해서 가져와보자.

$("#btnComment").on("click", function () {
  $.ajax({
    type: "get",
    url: "https://jsonplaceholder.typicode.com/comments",
    data: {
      postId: $("#id").val() || "1",
    },
    success: function (res) {
      if (res) {
        $("#comments").empty();
        $.each(res, function (idx, elem) {
          $("#comments").append(`<tr><td>${elem.postId}</td>
                                    <td>${elem.id}</td>
                                    <td>${elem.name}</td>
                                    <td>${elem.email}</td>
                                    <td>${elem.body}</td></tr>`);
        });
      }
    },
    error: function (xhr) {
      console.log(xhr.status + " : " + xhr.statusText);
    },
  });
});

 

비동기 호출과 callback hell

 

비동기 작업의 결과를 받아서 사용할 때 주의 사항

이제 다시 첫 번째 예 즉 post 목록을 가져오는 경우를 살펴보자.

post의 목록에 글 작성자 정보 즉 user_id가 있는데 이 부분에 작성자의 이름이 없기 때문에 사용자들이 보기에 아름답지 않은것 같다. 따라서 추가적으로 https://jsonplaceholder.typicode.com/users/{user_id}로 요청해서 사용자 정보를 역시 ajax로 받아서 업데이트 해보고자 한다. 이를 위해 아래와 같이 작업해보면 어떨까?

결론은 제대로 동작하지 않는다. 위의 코드는 3가지 흐름으로 진행된다.  먼저 1번은 sync 적으로 동작한다. 그 과정에서 2번에서 비동기적으로 post의 목록을 가져오라고 요청한 상태에서 이 동작이 끝나는것과 상관없이 1번이 다시 3번을 동작시킨다. 3번이 동작하려면 2번의 동작이 완료되어야 의미가 있는데 현재는 2번의 완료가 보장되지 않았기 때문에(사실 시작도 안했을 수 있다) 사용자 정보를 업데이트 할 수가 없게 된다.

따라서 실제로 동작시키기 위해서는 아래와 같은 구조가 되어야 한다.

 

callback hell

위와 같은 형태로 작성을 해보면 아래와 같은 코드가 작성된다.

$("#btnList2").on("click", function () {
  $.ajax({
    type: "get",
    url: "https://jsonplaceholder.typicode.com/posts",
    success: function (res) {
      console.log(res);
      if (res) {
        $("#content").empty();
        posts = res;
        $.each(posts, function (idx, post) {
          $("#content").append(`<tr><td class="user_${post.userId}">${post.userId}</td>
                                  <td>${post.id}</td>
                                  <td>${post.title}</td>
                                  <td>${post.body}</td>
                                  <td></td></tr>`);
        });
      }
      
      // 사용자 정보 업데이트
      $.each(posts, function (idx, post) {
        $.ajax({
          type: "get",
          url: `https://jsonplaceholder.typicode.com/users/${post.userId}`,
          success: function (res) {
            $(".user_" + post.userId).html(res.username);
          },
        });
      });
    },
    error: function (xhr) {
      console.log(xhr.status + " : " + xhr.statusText);
    },
  });
});

위의 출력 결과와 같이 멋지게 동작했지만 코드를 보면 점점 머리가 아파지기 시작한다. 비 동기 내에서 또다른 비 동기를 호출하다 보니 callback 내부에서 또다른 callback을 계속해서 작성하게 된다.

그리고 이런 상황이 심각해지면 그것을 callback 지옥이라고 한다. 대충 아래와 같은 모습니다.

 

jqXHR과 promise pattern

 

위와 같은 callback hell을 없애기 위한 노력의 일환으로 promise pattern을 들 수 있다. 자세한 내용은 아래 포스트를 참조하자.

[es2015]-[Promise]02. Promise 기본 사용법 (tistory.com)

 

[es2015]-[Promise]02. Promise 기본 사용법

이번 포스트에서는 Promise객체를 이용해서 Callback Hell에서 탈출해보자. Promise Promise의 상태 Promise 객체는 크게 Pending과 Settled의 상태를 갖고 Settled는 다시 fullfiled와 rejected를 갖는다. 그리고..

goodteacher.tistory.com

 

jqXHR의 promise pattern 지원

jQuery에서도 promise pattern을 적용한 api들이 제공된다. jQuery의 ajax 함수는 리턴 값으로 jqXHR 객체를 반환해주는데 이 객체는 기존의 callback인 success, error, complete를 대체하는 done, fail, always를 제공한다. 그리고 선행 작업이 끝나고 나서 후행 작업을 수행하게 할때 사용할 수 있는 then 함수를 제공한다. then 함수는 chaining이 되므로 기존의 callback hell을 훨씬 가독성 있는 코드로 개선할 수 있다.

 

개선된 코드

이제 우리의 코드를 promise pattern으로 변경해보자.

$("#btnList3").on("click", function () {
  $.ajax({
    type: "get",
    url: "https://jsonplaceholder.typicode.com/posts",
  })
    .then(function (res) {
      console.log(res);
      if (res) {
        $("#content").empty();
        posts = res;
        $.each(posts, function (idx, post) {
          $("#content").append(`<tr><td class="user_${post.userId}">${post.userId}</td>
                                  <td>${post.id}</td>
                                  <td>${post.title}</td>
                                  <td>${post.body}</td>
                                  <td></td></tr>`);
        });
      }
    })
    .done(function () {
      // 사용자 정보 업데이트
      $.each(posts, function (idx, post) {
        const jqxhr = $.ajax({
          type: "get",
          url: `https://jsonplaceholder.typicode.com/users/${post.userId}`,
          success: function (res) {
            $(".user_" + post.userId).html(res.username);
          },
        });
      });
    })
    .fail(function (xhr) {
      console.log(xhr.status + " : " + xhr.statusText);
    });
});

 

global ajax 이벤트

 

비동기 호출 처리 시 주의사항

ajax를 사용하다보면 주의할 점이 호출과 응답이 별개라는 점이다. 이것은 개발자 뿐 아니라 사용자에게도 혼돈을 줄 수 있다. ajax 요청 후 서버의 응답이 느려서 화면이 갱신되지 않으면 사용자는 어떻게 행동할까? 아마도 다시한버 요청을 날릴 확률이 높다. 이는 결과적으로 서버에서 처리해야할 작업이 하나 더 생기기 때문에 좋지 않다.

따라서 이런 경우는 ajax 요청이 시작하면 완전히 종료되기 전까지는 다른 요청을 하지 못하도록 화면을 막는 작업이 일반적이다. 

 

global ajax event

페이지에서 발생하는 ajax 요청을 모니터링하기 위해서 jquery에서는 global ajax event를 제공한다. 역시 event callback 기반으로 동작하는데 여기서는 두 가지 이벤트만 살펴보자.

이벤트 설명
ajaxStart(function(){}) 처리중인 aJax 요청이 없는 상태에서 처음 요청이 있을 때 발생하는 이벤트
기존에 진행되던 aJax 처리가 있을 경우는 발생하지 않음
ajaxStop(function(){}) 처리중인 모든 aJax 요청이 완료된 경우 발생하는 이벤트

따라서 어떤 ajax 요청이 시작하면 ajaxStart가 호출될 테니 사용자의 클릭을 막은 후 모든 ajax 요청이 종료되는 ajaxStop 상황에서 다시 클릭을 허용하면 된다.

 

이상으로 jquery를 이용한 ajax 처리에 대해서 살펴보았다.  지금은 비록 jquery가 과거의 기술로 치부되어가고 있으나 여전히 많이 사이트들에서 jquery의 흔적들이 활동하고 있는바 아직 무시할 수 없는 기술이다.

 

하지만 SPA 기반의 사이트를 기획하고 있다면 jquery를 사용하지 않기 때문에 axios 등을 사용하는 방법도 고려해보자.

Axios (tistory.com)

 

Axios

Axios 이번 포스트에서는 Ajax 처리를 위한 비동기 처리 라이브러리인 Axios에 대해 알아보자. https://github.com/axios/axios axios/axios Promise based HTTP client for the browser and node.js - axios/axio..

goodteacher.tistory.com

 

반응형
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.