AJAX란?

AJAX(Asynchronous JavaScript And XML)는 페이지 전체를 새로고침하지 않고 서버와 데이터를 주고받을 수 있는 기법입니다.
지금은 XML 대신 JSON을 주로 사용하며, 구현은 XMLHttpRequest(전통 방식), Fetch API(현대 방식), jQuery $.ajax()(레거시 방식) 등으로 할 수 있습니다.


동작 흐름

  1. JavaScript가 서버로 요청을 전송합니다.
  2. 서버가 요청을 처리한 뒤 응답을 반환합니다.
  3. JavaScript가 응답을 받아 화면 일부만 갱신합니다.

검색 자동완성, 댓글 등록 후 새로 반영, 무한 스크롤 등이 전형적인 사례입니다.

예제들은 모두 공개 테스트 API인 jsonplaceholder를 사용하므로 안전하게 실행할 수 있습니다.


CodePen 예제

예제 1: XMLHttpRequest로 GET 요청 (전통 방식)

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>AJAX - XHR GET</title>
<style>
  body{font-family:system-ui,apple-system,Segoe UI,Roboto,Arial; padding:24px;}
  button{padding:8px 12px; border:1px solid #ccc; background:#fff; cursor:pointer; border-radius:8px;}
  .card{margin-top:16px; padding:16px; border:1px solid #eee; border-radius:12px;}
  .small{color:#666; font-size:12px;}
  .loading{opacity:.6;}
  .error{color:#c0392b;}
</style>
</head>
<body>
  <h1>XMLHttpRequest로 데이터 불러오기</h1>
  <p class="small">엔드포인트: https://jsonplaceholder.typicode.com/posts/1</p>
  <button id="loadBtn">데이터 불러오기</button>

  <div id="result" class="card">아직 불러오지 않았습니다.</div>

  <script>
    const btn = document.getElementById("loadBtn");
    const box = document.getElementById("result");

    btn.addEventListener("click", () => {
      box.textContent = "불러오는 중입니다...";
      box.classList.add("loading");

      const xhr = new XMLHttpRequest();
      xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1", true);

      xhr.onload = function () {
        box.classList.remove("loading");
        if (xhr.status >= 200 && xhr.status < 300) {
          try {
            const data = JSON.parse(xhr.responseText);
            box.innerHTML = `
              <h3>${data.title}</h3>
              <p>${data.body}</p>
              <div class="small">id: ${data.id}</div>
            `;
          } catch (e) {
            box.innerHTML = `<span class="error">JSON 파싱 오류입니다.</span>`;
          }
        } else {
          box.innerHTML = `<span class="error">요청 실패입니다. (status: ${xhr.status})</span>`;
        }
      };

      xhr.onerror = function () {
        box.classList.remove("loading");
        box.innerHTML = `<span class="error">네트워크 오류입니다.</span>`;
      };

      xhr.send();
    });
  </script>
</body>
</html>

예제 2: Fetch API로 GET 요청 (현대적 & 간결)

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>AJAX - Fetch GET</title>
<style>
  body{font-family:system-ui,apple-system,Segoe UI,Roboto,Arial; padding:24px;}
  button{padding:8px 12px; border:1px solid #ccc; background:#fff; cursor:pointer; border-radius:8px;}
  .card{margin-top:16px; padding:16px; border:1px solid #eee; border-radius:12px;}
  .small{color:#666; font-size:12px;}
  .loading{opacity:.6;}
  .error{color:#c0392b;}
</style>
</head>
<body>
  <h1>Fetch API로 데이터 불러오기</h1>
  <p class="small">엔드포인트: https://jsonplaceholder.typicode.com/posts/2</p>
  <button id="fetchBtn">데이터 불러오기</button>

  <div id="fetchResult" class="card">아직 불러오지 않았습니다.</div>

  <script>
    const fetchBtn = document.getElementById("fetchBtn");
    const fetchBox = document.getElementById("fetchResult");

    fetchBtn.addEventListener("click", async () => {
      fetchBox.textContent = "불러오는 중입니다...";
      fetchBox.classList.add("loading");

      try {
        const res = await fetch("https://jsonplaceholder.typicode.com/posts/2");
        if (!res.ok) throw new Error("응답 에러: " + res.status);
        const data = await res.json();
        fetchBox.classList.remove("loading");
        fetchBox.innerHTML = `
          <h3>${data.title}</h3>
          <p>${data.body}</p>
          <div class="small">id: ${data.id}</div>
        `;
      } catch (err) {
        fetchBox.classList.remove("loading");
        fetchBox.innerHTML = `<span class="error">실패했습니다: ${err.message}</span>`;
      }
    });
  </script>
</body>
</html>

예제 3: jQuery AJAX (레거시 방식)

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>AJAX - jQuery GET</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<style>
  body{font-family:system-ui,apple-system,Segoe UI,Roboto,Arial; padding:24px;}
  button{padding:8px 12px; border:1px solid #ccc; background:#fff; cursor:pointer; border-radius:8px;}
  .card{margin-top:16px; padding:16px; border:1px solid #eee; border-radius:12px;}
  .small{color:#666; font-size:12px;}
  .loading{opacity:.6;}
  .error{color:#c0392b;}
</style>
</head>
<body>
  <h1>jQuery.ajax()로 데이터 불러오기</h1>
  <p class="small">엔드포인트: https://jsonplaceholder.typicode.com/posts/3</p>
  <button id="jqBtn">데이터 불러오기</button>

  <div id="jqResult" class="card">아직 불러오지 않았습니다.</div>

  <script>
    $("#jqBtn").on("click", function () {
      const $box = $("#jqResult").text("불러오는 중입니다...").addClass("loading");
      $.ajax({
        url: "https://jsonplaceholder.typicode.com/posts/3",
        method: "GET",
        success: function (data) {
          $box.removeClass("loading").html(`
            <h3>${data.title}</h3>
            <p>${data.body}</p>
            <div class="small">id: ${data.id}</div>
          `);
        },
        error: function (xhr) {
          $box.removeClass("loading").html(`<span class="error">실패했습니다. (status: ${xhr.status})</span>`);
        }
      });
    });
  </script>
</body>
</html>

예제 4: 댓글 등록(POST) + 낙관적 UI 업데이트

<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>AJAX - Fetch POST (댓글)</title>
<style>
  body{font-family:system-ui,apple-system,Segoe UI,Roboto,Arial; padding:24px; max-width:720px; margin:auto;}
  form{display:flex; gap:8px;}
  input, button{padding:10px; border:1px solid #ccc; border-radius:8px; font-size:14px;}
  button{background:#fff; cursor:pointer;}
  ul{margin-top:16px; padding:0; list-style:none;}
  li{padding:10px 12px; border:1px solid #eee; border-radius:10px; margin-bottom:8px;}
  .small{color:#666; font-size:12px;}
  .error{color:#c0392b;}
  .pending{opacity:.6;}
</style>
</head>
<body>
  <h1>댓글 등록 (새로고침 없이)</h1>
  <p class="small">POST: https://jsonplaceholder.typicode.com/comments</p>

  <form id="commentForm">
    <input id="commentInput" type="text" placeholder="댓글 입력" required />
    <button type="submit">등록</button>
  </form>

  <ul id="commentList"></ul>
  <div id="msg" class="small"></div>

  <script>
    const form = document.getElementById("commentForm");
    const input = document.getElementById("commentInput");
    const list = document.getElementById("commentList");
    const msg = document.getElementById("msg");

    form.addEventListener("submit", async (e) => {
      e.preventDefault();

      const text = input.value.trim();
      if (!text) {
        msg.textContent = "댓글을 입력해주세요.";
        return;
      }

      const tempId = "temp-" + Date.now();
      const li = document.createElement("li");
      li.dataset.id = tempId;
      li.classList.add("pending");
      li.textContent = text + " (전송 중...)";
      list.prepend(li);
      input.value = "";
      msg.textContent = "";

      try {
        const res = await fetch("https://jsonplaceholder.typicode.com/comments", {
          method: "POST",
          headers: { "Content-Type": "application/json; charset=UTF-8" },
          body: JSON.stringify({ body: text, postId: 1 })
        });

        if (!res.ok) throw new Error("응답 에러: " + res.status);

        const data = await res.json();
        li.classList.remove("pending");
        li.textContent = data.body + " (id: " + data.id + ")";
        msg.textContent = "등록 성공입니다!";
      } catch (err) {
        li.remove();
        msg.innerHTML = `<span class="error">등록 실패입니다: ${err.message}</span>`;
      }
    });
  </script>
</body>
</html>


정리

  • AJAX는 페이지 전체를 새로고침하지 않고 데이터를 주고받는 기법입니다.
  • XMLHttpRequest, fetch API, jQuery.ajax 등 다양한 방식으로 사용할 수 있습니다.
  • 실무에서는 주로 fetch 또는 Axios를 사용하는 것이 일반적입니다.
  • 로딩 상태, 에러 상태, 빈 상태를 명확히 보여주는 것이 좋은 사용자 경험(UX)을 제공합니다.
profile
html_programming_language

0개의 댓글