본문 바로가기

Development/Javascript

모던 자바스크립트 입문 - Chapter 16: HTTP 제어 (Ajax) 💻

자바스크립트로 웹 개발을 하다 보면 서버와 데이터를 주고받아야 하는 상황이 반드시 생깁니다.

예전에는 페이지 전체를 새로고침해야 했지만, 이제는 Ajax를 통해 페이지를 새로고침하지 않고도 서버와 통신할 수 있습니다.

이번 포스트에서는 모던 자바스크립트에서 HTTP 통신을 다루는 방법들을 차근차근 알아보겠습니다.

📌 목차

  • HTTP 통신이란?
  • Ajax의 개념과 장점
  • XMLHttpRequest 기본 사용법
  • 서버 응답 처리하기
  • 크로스 오리진 통신 이해하기

📝 HTTP 통신이란?

HTTP(HyperText Transfer Protocol)는 웹 브라우저와 서버가 데이터를 주고받기 위한 규약입니다.

기본 동작 원리

// 전통적인 방식 - 페이지 전체 새로고침
window.location.href = '/new-page';

// 현대적인 방식 - Ajax를 통한 부분 업데이트
fetch('/api/data')
  .then(response => response.json())
  .then(data => {
    // 페이지 새로고침 없이 데이터만 업데이트
    document.getElementById('content').textContent = data.message;
  });

 

HTTP 통신은 요청(Request)과 응답(Response)으로 이루어져 있으며, 주요 메서드로는 GET, POST, PUT, DELETE 등이 있습니다.


📝 Ajax란 무엇인가?

Ajax(Asynchronous JavaScript and XML)는 페이지를 새로고침하지 않고 서버와 비동기적으로 데이터를 주고받는 기술입니다.

Ajax의 장점

  • 페이지 새로고침 없음: 사용자 경험이 부드러워집니다
  • 빠른 응답: 필요한 데이터만 주고받아 속도가 빠릅니다
  • 서버 부하 감소: 전체 페이지가 아닌 데이터만 전송합니다

간단한 Ajax 예시

// 현대적인 fetch API 사용
async function loadUserData() {
  try {
    const response = await fetch('/api/user');
    const user = await response.json();
    
    document.getElementById('username').textContent = user.name;
    document.getElementById('email').textContent = user.email;
  } catch (error) {
    console.error('데이터 로딩 실패:', error);
  }
}

// 버튼 클릭 시 데이터 로드
document.getElementById('loadBtn').addEventListener('click', loadUserData);

📝 XMLHttpRequest 기본 사용법

XMLHttpRequest는 Ajax의 핵심 객체입니다. 현재는 fetch API가 더 널리 사용되지만, 여전히 중요한 개념입니다.

기본 사용 패턴

function sendRequest() {
  // 1. XMLHttpRequest 객체 생성
  const xhr = new XMLHttpRequest();
  
  // 2. 요청 설정
  xhr.open('GET', '/api/data', true); // true는 비동기 설정
  
  // 3. 응답 처리 함수 설정
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
      const data = JSON.parse(xhr.responseText);
      console.log('받은 데이터:', data);
    }
  };
  
  // 4. 요청 전송
  xhr.send();
}

POST 요청 보내기

function sendData(userData) {
  const xhr = new XMLHttpRequest();
  
  xhr.open('POST', '/api/users', true);
  xhr.setRequestHeader('Content-Type', 'application/json');
  
  xhr.onload = function() {
    if (xhr.status === 201) {
      alert('사용자 생성 완료!');
    }
  };
  
  xhr.send(JSON.stringify(userData));
}

// 사용 예시
sendData({ name: '홍길동', age: 25 });

📝 서버 응답 처리하기

서버로부터 받은 응답을 적절히 처리하는 것이 중요합니다.

응답 상태 코드 확인

fetch('/api/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .then(data => {
    // 성공적으로 데이터를 받았을 때
    displayData(data);
  })
  .catch(error => {
    // 오류 처리
    console.error('요청 실패:', error);
    showErrorMessage('데이터를 불러올 수 없습니다.');
  });

function displayData(data) {
  const container = document.getElementById('data-container');
  container.innerHTML = `<p>${data.message}</p>`;
}

function showErrorMessage(message) {
  const errorDiv = document.getElementById('error-message');
  errorDiv.textContent = message;
  errorDiv.style.display = 'block';
}

로딩 상태 표시

async function loadDataWithLoading() {
  const loadingElement = document.getElementById('loading');
  const contentElement = document.getElementById('content');
  
  try {
    // 로딩 시작
    loadingElement.style.display = 'block';
    contentElement.style.display = 'none';
    
    const response = await fetch('/api/slow-data');
    const data = await response.json();
    
    // 데이터 표시
    contentElement.innerHTML = `<h3>${data.title}</h3><p>${data.content}</p>`;
    
  } catch (error) {
    contentElement.innerHTML = '<p>오류가 발생했습니다.</p>';
  } finally {
    // 로딩 완료
    loadingElement.style.display = 'none';
    contentElement.style.display = 'block';
  }
}

📝 크로스 오리진 통신 이해하기

크로스 오리진 통신은 다른 도메인의 서버와 통신하는 것입니다.

보안상의 이유로 브라우저에서 제한하지만, CORS를 통해 해결할 수 있습니다.

같은 오리진과 다른 오리진

// 같은 오리진 (허용됨)
// 현재 사이트: https://mysite.com
fetch('/api/data') // ✅ 허용

// 다른 오리진 (기본적으로 차단됨)
fetch('https://api.other-site.com/data') // ❌ CORS 오류 가능

CORS 해결 방법

// 서버에서 CORS 헤더를 설정해야 함
// 클라이언트에서는 credentials 옵션 사용 가능
fetch('https://api.external-site.com/data', {
  method: 'GET',
  headers: {
    'Content-Type': 'application/json',
  },
  credentials: 'include' // 쿠키 포함
})
.then(response => response.json())
.then(data => console.log(data));

JSONP 방식 (구식 방법)

// JSONP는 script 태그를 이용한 우회 방법
function loadJSONP(url, callback) {
  const script = document.createElement('script');
  script.src = url + '?callback=' + callback;
  document.head.appendChild(script);
}

// 사용 예시 (현재는 거의 사용하지 않음)
function handleData(data) {
  console.log('JSONP 데이터:', data);
}

loadJSONP('https://api.example.com/data', 'handleData');

📚 마무리 정리

오늘 배운 HTTP 제어와 Ajax 내용들을 정리하면:

HTTP 통신은 브라우저와 서버 간의 데이터 교환 규약이며, Ajax를 통해 페이지 새로고침 없이 서버와 통신할 수 있습니다. XMLHttpRequest는 전통적인 Ajax 방법이고, 현재는 fetch API가 더 널리 사용됩니다.

서버 응답 처리에서는 상태 코드 확인과 오류 처리가 중요하며, 크로스 오리진 통신은 CORS 정책을 이해하고 적절히 설정해야 합니다.

💡 오늘 새롭게 알게 된 것

  • Ajax를 사용하면 페이지 전체를 새로고침하지 않고도 데이터를 업데이트할 수 있다는 점입니다.
  • fetch API가 XMLHttpRequest보다 더 간단하고 직관적이라는 점입니다.
  • CORS 정책으로 인해 다른 도메인과의 통신이 제한될 수 있다는 점입니다.

🤔 어려웠던 점

  • XMLHttpRequest의 readyState와 status 값들을 구분해야하는 것 입니다.
  • 크로스 오리진 문제와 CORS 해결 방법을 이해하는 것 입니다.
  • 비동기 통신에서 오류 처리를 적절히 하는 방법입니다.

🎯 다음 학습 계획

다음 포스팅에서는 MVC 모델에 기반을 둔 프로그램 설계에 대해 알아보겠습니다.