Ajax란 — 비동기 웹 통신
이 토픽을 마치면
페이지 새로고침 없이 서버와 데이터를 주고받는 Ajax의 원리를 이해하고, fetch API로 GET/POST 요청을 보낼 수 있습니다.
새로고침의 문제
전통적인 웹에서는 서버에 뭔가를 요청하면 페이지 전체가 새로 불러와졌습니다. 검색어를 입력하고 Enter를 누르면 화면이 하얗게 깜빡이면서 결과 페이지가 통째로 로딩됩니다.
구글 검색은 다릅니다. 검색어를 입력하면 추천 검색어가 밑에 쏙쏙 뜹니다. 페이지가 새로고침되지 않습니다. 이게 Ajax입니다 — 페이지를 새로 불러오지 않고, 필요한 데이터만 서버에서 가져옵니다.
Ajax는 Asynchronous JavaScript and XML의 약자인데, 지금은 XML 대신 JSON을 씁니다. 이름만 남아있는 셈입니다.
옛날 방식 — XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log(data);
}
};
xhr.send();보시다시피 번거롭습니다. readyState 값을 확인하고, 상태 코드를 체크하고, 응답을 직접 파싱해야 합니다. 이 코드가 콜백 안에 콜백으로 중첩되면 소위 "콜백 지옥"이 됩니다.
현재 표준 — fetch API
const response = await fetch('/api/users');
const data = await response.json();
console.log(data);세 줄입니다. fetch는 Promise를 반환하니까 async/await과 자연스럽게 결합됩니다.
GET — 데이터 가져오기
async function getUsers() {
const res = await fetch('/api/users');
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
const users = await res.json();
return users;
}주의할 점: fetch는 404나 500에서도 reject하지 않습니다. 네트워크 에러(서버 연결 실패)에서만 reject합니다. 그래서 res.ok(200~299인지)를 직접 확인해야 합니다.
POST — 데이터 보내기
async function createUser(name, email) {
const res = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name, email }),
});
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return await res.json();
}body에 넣을 데이터를 JSON.stringify로 문자열로 바꾸고, Content-Type을 application/json으로 설정합니다. 이 헤더가 없으면 서버가 본문을 JSON으로 파싱하지 않습니다.
실전 패턴 — 로딩/에러/성공
const resultDiv = document.getElementById('result');
async function loadUsers() {
resultDiv.textContent = '로딩 중...';
try {
const res = await fetch('/api/users');
if (!res.ok) throw new Error(`서버 오류: ${res.status}`);
const users = await res.json();
resultDiv.textContent = users
.map(u => `${u.name} (${u.email})`)
.join('\n');
} catch (err) {
resultDiv.textContent = `오류: ${err.message}`;
}
}이 세 가지 상태(로딩/성공/에러)를 관리하는 건 프론트엔드 개발에서 반복되는 패턴입니다. React에서는 useState로, Vue에서는 ref로 같은 구조를 만듭니다.
fetch vs XMLHttpRequest 비교
| XMLHttpRequest | fetch | |
|---|---|---|
| 문법 | 콜백 기반, 장황함 | Promise 기반, 간결 |
| JSON 파싱 | JSON.parse(xhr.responseText) 수동 | .json() 메서드 제공 |
| 에러 처리 | readyState + status 직접 확인 | res.ok + try/catch |
| 스트리밍 | 불가 | ReadableStream 지원 |
| 취소 | xhr.abort() | AbortController |
| 쿠키 전송 | 기본 포함 | credentials: 'include' 명시 필요 |
자주 하는 실수
- Content-Type 누락 — POST에서
headers를 안 넣으면 서버가req.body를 파싱 못합니다 - 이중
.json()호출 —res.json()은 한 번만 호출 가능합니다. body stream은 한 번 읽으면 소진됩니다 - CORS 에러 — 다른 도메인으로 fetch하면 브라우저가 차단합니다. 서버에서
Access-Control-Allow-Origin헤더를 설정해야 합니다
핵심
Ajax는 페이지 새로고침 없이 서버와 데이터를 주고받는 기술입니다. 현대 웹에서는
fetchAPI +async/await이 표준입니다.fetch는 네트워크 에러에서만 reject하므로,res.ok를 반드시 확인하세요.