Ajax, Fetch의 동기 및 비동기 방식 알아보기

안녕하세요. 오늘은 웹 개발자들 사이에서 꾸준한 관심을 받고 있는 두 가지 웹 통신 기술, AJAX와 Fetch를 예제를 통해 비교해 볼 거예요. 이 두 기술은 서버와 통신하는 데 있어 각각 동기와 비동기 방식을 지원하는데요. 이번 포스팅에서는 서로의 차이를 살펴보고, 다양한 호출법에 대해 예제를 통해 알아보도록 하겠습니다.

동기(Sync) 방식은 코드가 순차적으로 실행되며, 요청이 완료될 때까지 다음 작업이 대기하는 방식이에요. 이 방식은 코드가 직관적이지만, 요청에 시간이 걸릴 경우 애플리케이션의 성능이 저하될 수 있죠. 동기 방식은 AJAX를 사용해 구현할 수 있습니다.

비동기(Async) 방식은 코드가 병렬로 실행되며, 요청이 완료되기 전에 다음 작업을 처리할 수 있는 방식이에요. 이 방식은 애플리케이션의 성능을 향상시키지만, 코드가 복잡해질 수 있죠. 하지만, 최근의 자바스크립트 기능들(예: async/await, Promise) 덕분에 비동기 코드를 더 간결하고 이해하기 쉽게 작성할 수 있어요. 비동기 방식은 AJAX와 Fetch 모두를 사용해 구현할 수 있습니다.

 

AJAX : 웹 개발의 근본, XMLHttpRequest

  • 웹 페이지를 새로고침하지 않고도 서버와 통신할 수 있는 강력한 기술이에요. 이 기술은 웹 개발의 근본적인 요소 중 하나인 XMLHttpRequest 객체를 사용해요. AJAX의 장점은 동기 및 비동기 방식 모두를 지원한다는 것이죠. 이로 인해 개발자들은 상황에 따라 유연하게 웹 애플리케이션의 동작 방식을 결정할 수 있습니다.

Fetch : 신규 강자, Promise 기반 세련된 방식

  • 웹 개발의 새로운 기준이 되고 있는 비동기 네트워크 통신 기술이에요. Promise 기반으로 작성된 Fetch는 코드가 간결하고 가독성이 좋기 때문에 비동기 처리의 복잡함을 크게 줄일 수 있으며, 에러 처리 또한 간편하게 할 수 있어요. 그러나 단점은 동기 방식을 지원하지 않는다는 것이죠.

 

이제 Fetch와 AJAX의 동기 및 비동기 방식으로 각각 GET/POST 요청하는 예제를 만들어볼게요. Fetch의 경우 기본적으로 비동기 방식만 가능하지만 async와 await를 사용해서 *동기적인 거처럼 처리하도록 할게요. 왜 동기처럼 인지는 Fetch 예제와 함께 설명할게요.

AJAX 비동기 방식 (Asynchronous)

Async  Get 요청

//******* AJAX aSync GET 요청 *******
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/users", true); // 동기(false) ,비동기(true)
//헤더 정보가 필요한 경우에만 추가
xhr.setRequestHeader("x-requested-with", "XMLHttpRequest");
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) { //GET 요청에 대해 성공적인경우
        console.log(JSON.parse(xhr.responseText)); // 서버로부터 받은 데이터를 출력
    }
};
xhr.send();

 

Async  Post 요청

//******* AJAX aSync POST 요청 *******
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://jsonplaceholder.typicode.com/users", true); // 동기(false) ,비동기(true)
//POST 요청 시 일반적으로 Content-Type은 세팅
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
//서버쪽 에서 요청 정보를 확인하는 경우에만 추가
xhr.setRequestHeader("x-requested-with", "XMLHttpRequest");
xhr.onreadystatechange = function () {
  if (xhr.readyState == 4 && xhr.status == 201) { // POST 요청에 대해 성공적인경우
    console.log(JSON.parse(xhr.responseText)); // 서버로부터 받은 데이터를 출력
  }
};
//POST 요청에 보낼 데이터 작성
const data = {
  name: "John Doe",
  username: "john.doe",
  email: "john.doe@example.com",
};
xhr.send(JSON.stringify(data)); //JSON 형태로 변환하여 서버에 전송

비동기 방식의 XMLHttpRequest에서 주로 사용되는 onreadystatechange 이벤트는 비동기 요청 처리가 완료되면 이벤트가 발생하고, 지정된 콜백 함수가 실행돼요. 동기 요청의 경우, 요청이 완료되기 전에 코드가 차단되기 때문에 사용되지 않아요.

 

AJAX 동기 방식 (Synchronous)

Sync  Get 요청

//******* AJAX Sync GET 요청 *******
const xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/users", false); // 동기(false) ,비동기(true)
//헤더 정보가 필요한 경우에만 추가
xhr.setRequestHeader("x-requested-with", "XMLHttpRequest");
xhr.send();
if (xhr.status == 200) { //GET 요청에 대해 성공적인경우
    console.log(JSON.parse(xhr.responseText)); // 서버로부터 받은 데이터를 출력
} else {
    console.error("요청 실패"); // 오류 발생 시 메시지를 출력
}

 

Sync  Post 요청

//******* AJAX Sync POST 요청 *******
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://jsonplaceholder.typicode.com/users", false); // 동기(false) ,비동기(true)
//POST 요청 시 일반적으로 Content-Type은 세팅
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
//서버쪽 에서 요청 정보를 확인하는 경우에만 추가
xhr.setRequestHeader("x-requested-with", "XMLHttpRequest");
//POST 요청에 보낼 데이터 작성
const data = {
  name: "John Doe",
  username: "john.doe",
  email: "john.doe@example.com",
};
xhr.send(JSON.stringify(data)); //JSON 형태로 변환하여 서버에 전송
if (xhr.status == 201) { // POST 요청에 대해 성공적인경우
    console.log(JSON.parse(xhr.responseText)); // 서버로부터 받은 데이터를 출력
} else {
  console.error("요청 실패"); // 오류 발생 시 메시지를 출력
}

위 예제는 동기 방식으로 동작하며, 요청이 완료될 때까지 코드 실행이 차단돼요. 따라서 요청이 완료된 후 결과를 처리하게 됩니다. 동기 방식을 사용할 때는 웹 애플리케이션의 성능이 저하될 수 있으므로 주의가 필요해요.

Fetch 비동기 방식 (Asynchronous)

Async  Get 요청

//******* Fetch Async GET 요청 *******
fetch("https://jsonplaceholder.typicode.com/users")
  .then((response) => response.json()) // 서버로부터 받은 데이터를 JSON 형태로 변환
  .then((data) => console.log(data)) // 변환된 데이터를 출력
  .catch((error) => console.error("Error fetching data:", error)); // 오류 발생 시 메시지 출력

 

Async  Post 요청

//******* Fetch Async POST 요청 *******
fetch("https://jsonplaceholder.typicode.com/users", {
  method: "POST",
  //헤더 정보가 필요한 경우에만 추가
  headers: {
    "Content-Type": "application/json;charset=UTF-8",
    "x-requested-with": "XMLHttpRequest",
  },
  //POST 요청에 보낼 데이터 작성
  body: JSON.stringify({
    name: "John Doe",
    username: "john.doe",
    email: "john.doe@example.com",
  }),
})
  .then((response) => response.json()) // 서버로부터 받은 데이터를 JSON 형태로 변환
  .then((data) => console.log(data)) // 변환된 데이터를 출력
  .catch((error) => console.error("Error fetching data:", error)); // 오류 발생 시 메시지 출력

Fetch는 기본적으로 몇 가지 헤더가 자동으로 설정돼요. 가장 일반적인 헤더 중 하나는 User-Agent인데요. 이 헤더는 클라이언트(웹 브라우저 등)에 대한 정보를 제공해요. 그 외에도 Accept 헤더가 자동으로 설정되어 응답에서 받아들일 수 있는 콘텐츠 유형을 지정합니다.

Fetch 동기 방식 (Synchronous)

Sync Get 요청 

//******* Fetch sync GET 요청 *******
async function fetchData() {
    try {
        const response = await fetch("https://jsonplaceholder.typicode.com/users");
        const data = await response.json(); // 서버로부터 받은 데이터를 JSON 형태로 변환
        console.log(data);  // 변환된 데이터를 출력
    } catch (error) { 
        console.error("Error fetching data:", error); // 오류 발생 시 메시지 출력
    }
}
fetchData();

 

Sync  Post 요청

//******* Fetch sync POST 요청 *******
async function postData() {
  //헤더 정보가 필요한 경우에만 추가
  const headers = {
    "Content-Type": "application/json;charset=UTF-8",
    "x-requested-with": "XMLHttpRequest",
  };
  //POST 요청에 보낼 데이터 작성
  const body = {
    name: "John Doe",
    username: "john.doe",
    email: "john.doe@example.com",
  };
  try {
    const response = await fetch("https://jsonplaceholder.typicode.com/users", {
      method: "POST",
      headers: headers,
      body: JSON.stringify(body), 
    });
    const data = await response.json(); // 서버로부터 받은 데이터를 JSON 형태로 변환
    console.log(data); // 변환된 데이터를 출력
  } catch (error) {
    console.error("Error fetching data:", error);  // 오류 발생 시 메시지 출력
  }
}
postData();

위 예제에서 fetchData 함수는 비동기 함수이나, await 키워드를 사용하여 동기적인 거처럼 처리하고 있어요. 이렇게 async와 await를 사용하면, 비동기 작업을 동기적으로 처리하는 것처럼 코드를 작성할 수 있습니다. 그러나 실제로는 내부적으로 비동기 방식으로 처리되고 있어요. 따라서, 다른 작업을 차단하지 않는답니다!

그럼에도 fetch에서 await를 사용하는 이유는 비동기 코드를 더 간결하고 가독성 좋게 작성하기 위함이에요. 비동기 코드를 작정할 때, Primise 체인을 사용하면 코드가 복잡해지고 가독성이 떨어질 수 있지만, async/await를 사용하면 이런 단점을 해결할 수 있습니다.

오늘은 이렇게 다양한 예제들을 통해 AJAX와 Fetch를 사용한 동기, 비동기 방식을 비교해 봤는데요. 대부분의 경우, 비동기 방식이 성능에 더 좋은 영향을 미치지만 특정 상황에서는 동기 방식이 필요할 수 있어요. 이때는 AJAX를 사용하여 동기 방식을 구현하면 됩니다. 웹에서 서버와 통신하는 방식을 선택할 때 이러한 기술의 특성을 고려하여 상황에 맞는 적절한 기술을 사용하는 것이 중요합니다. - 끝 -

Ajax_fetch_예제