멋쟁이 사자처럼 프론트엔드 스쿨 어느덧 10주차에 이르렀다.
자바스크립트 프로젝트를 앞두고 마지막으로 비동기 v통신을 중심적으로 배우고 있는데,
그에 대한 정리를 해보고자 한다.
동기 통신 과 비동기 통신
Ajax를 다루기에 앞서, 비동기 통신이 왜 필요한 지에 대해 먼저 짚어보고자 한다.
기본적으로,웹에서는 요청과 응답이 순차적으로 이루어지는 방식(동기 통신 방식)을 이룬다.
동기 통신 방식
클라이언트가 서버에 데이터를 요청 => 서버는 해당 요청 처리 후 데이터를 응답 => 클라이언트는 서버의 응답을 받을 때까지 대기
즉, 요청과 응답이 완전히 끝날 때 까지 다음 동작을 실행하지 않고 대기한다.
이런 순서로 작동하는 동기 통신의 단점으로,
1. 응답 대기시간 증가:
서버에서 응답 시간이 오래 걸리는 경우, 클라이언트가 응답을 기다리는 동안 블로킹 상태가 될 수 있다.
2. 페이지 로딩시간 증가:
동기 통신은 요청과 응답이 순차적으로 이루어지기 때문에, 데이터가 많거나 요청이 복잡한 경우 전체 페이지의 로딩 시간이 증가할 수 있다.
3. 사용자 경험(UX) 저하:
동기 통신은 페이지의 일부분만 업데이트하는 것이 아니라 전체 페이지를 다시 렌더링해야 하므로, 사용자가 페이지를 빠르게 변화하는 것을 지켜보기 어렵고, 페이지가 느리게 업데이트되는 느낌을 줄 수 있다.
4. 스레드 관리 문제:
많은 클라이언트가 동시에 서버에 요청을 보내면 서버의 부하가 증가하여 성능 저하가 발생할 수 있다.
이런 단점을 극복하기 위해 비동기 통신 방식인 Ajax를 사용한다.
AJAX(Asyncrhonous Javascript And XML)
AJAX는 웹 페이지에서 비동기적으로 서버와 데이터를 주고받는 기술을 말한다.
AJAX를 이용하면 브라우저에서 필요한 데이터만 요청하고 서버는 그에 대한 일부 데이터만 응답하게 됩는데, 이로 인해 화면이 새로고침 없이 부드럽게 애니메이션을 보여주는 싱글 페이지 어플리케이션(SPA) 등의 기법이 가능하며, 사용자 경험이 개선할 수 있다.
비동기 통신 방식
브라우저는 내장된 AJAX 엔진을 사용하여 필요한 데이터를 요청 => 서버는 해당 데이터의 일부만을 응답 => 서버에 받은 데이터를 브라우저에 전달(주로 JSON 또는 XML 형식으로 문자화) => 브라우저에서는 받은 정보를 AJAX 엔진으로 다시 해석하여 웹 페이지에 렌더링
* XML은 AJAX 탄생 이전에 주로 사용되던 형식이지만, 가독성과, 간결성, 파싱의 용의성 등 많은 면에서 우월한 JSON 형식을 주로 데이터 교환 형식으로 사용하며, 이 때, 브라우저에서 서버로 보내는 데이터는 JSON.stringify 메서드를, 반대로 서버에서 브라우저로 보내는 데이터는 JSON.parse 메서드를 사용
JSON.stringify(): JavaScript 객체를 JSON 형식의 문자열로 변환하는 메서드로, JavaScript 객체를 서버로 전송하거나 로컬 스토리지에 저장하기 위해 JSON 형식의 문자열로 변환하는 데 사용
JSON. parse(): JSON 형식의 문자열을 JavaScript 객체로 변환하는 메서드로, 서버에서 전송된 JSON 형식의 문자열을 JavaScript 객체로 변환하여 사용하거나, 로컬 스토리지에서 저장된 JSON 형식의 문자열을 JavaScript 객체로 변환하는 데 사용
Ajax엔진
Ajax 엔진은 비동기적으로 데이터를 주고 받기 위해, JavaScript를 사용하여 서버와의 데이터 통신을 처리하고, 서버로부터 데이터를 비동기적으로 받아와서 웹 페이지의 일부분만 업데이트하거나 추가로 불러올 수 있게 한다.
Ajax엔진(Ajax API)의 종류로는 XMLHttpRequest(XHR)과 Fetch API가 있다.
- XMLHttpRequest(XHR):
- 오래된 브라우저에서 사용되던 비동기 통신 방식
- 콜백 함수를 사용하여 비동기적으로 서버와 통신할 수 있다.
- 복잡한 코드 구조와 콜백 지옥(callback hell)의 문제가 발생할 수 있다.
- 브라우저 지원이 넓지만 사용하기 어려운 API 구조를 가지고 있다.
function xhr(options) {
const {
method = 'GET',
url = '',
onSuccess = null,
onFail = null,
body = null,
headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
},
} = options;
const xhr = new XMLHttpRequest();
xhr.open(method, url);
Object.entries(headers).forEach(([key, value]) => {
xhr.setRequestHeader(key, value);
});
xhr.addEventListener('readystatechange', () => {
//console.log(xhr.readyState); //* 데이터가 불러와짐에 따라 readyState: 2=>3=>4
const { status, readyState, response } = xhr;
if (status >= 200 && status < 400) {
if (readyState === 4) {
//* 데이터 수신이 완료된 상태로, 정상적으로 데이터를 불러오게 됨!
onSuccess(JSON.parse(response));
}
} else {
onFail('실패');
}
});
xhr.send(JSON.stringify(body));
}
위는 xhr 함수의 선언부로 xhr이라는 XMLHttpRequest 객체를 생성하고, 지정된 설정에 따라 요청을 보내고 응답을 처리한다.
요청이 성공적으로 완료되었을 때는 onSuccess 콜백 함수가 호출되며, 응답 데이터를 인자로 받아 처리한다.
요청이 실패했을 때는 onFail 콜백 함수가 호출되며, 오류 메시지를 인자로 받아 처리한다.
xhr을 사용할 때, 필수요소이자 지켜야할 형식으로
xhr.open(method, url)
xhr.addEventListener(event, 콜백 함수로 status에 따른 분기 처리)
xhr.send(보낼 내용)
를 순차적으로 사용해야한다.
xhr({
url: 'https://jsonplaceholder.typicode.com/users',
onSuccess: (data) => {
console.log('요청 성공!');
console.log('응답 데이터:', data);
},
onFail: (error) => {
console.log('요청 실패!');
console.log('에러 메시지:', error);
},
body: {
name: 'tunar',
},
});
실행부에서는 xhr 함수를 호출하여 'https://jsonplaceholder.typicode.com/users' 주소로 POST 요청을 보내고,
body에 { name: 'tunar' } 데이터를 전송하여,서버는 이 요청을 처리하고 응답 데이터를 전송한다.
onSuccess 콜백 함수는 요청이 성공적으로 완료되었을 때, 응답 데이터를 인자로 받아 '요청 성공!'과 응답 데이터를 로그로 출력하며,
onFail 콜백 함수는 요청이 실패했을 때, 오류 메시지를 인자로 받아 '요청 실패!'와 오류 메시지를 로그로 출력한다.
이렇게 코드가 복잡하기에 xhr 내부에서도 재사용성을 개선하고, HTTP 로직 별로 유지 관리하기 위해 아래처럼 Helper 함수를 사용하기도 한다.
// xhr Helper 함수 선언부
xhr.get = (url, onSuccess, onFail) => {
xhr({
url,
onSuccess,
onFail,
});
};
xhr.post = (url, body, onSuccess, onFail) => {
xhr({
method: 'POST',
url,
body,
onSuccess,
onFail,
});
};
xhr.put = (url, body, onSuccess, onFail) => {
xhr({
method: 'PUT',
url,
body,
onSuccess,
onFail,
});
};
xhr.delete = (url, onSuccess, onFail) => {
xhr({
method: 'DELETE',
url,
onSuccess,
onFail,
});
};
// xhr Helper 함수 실행부
xhr.get(
'https://jsonplaceholder.typicode.com/users',
(result) => {
console.log(result);
},
(err) => {
console.log(err);
}
);
이렇게 helper함수를 만들어서 사용하는데, 여전히 가독성이 좋다고 할 수는 없겠다.
- Fetch API:
- 직관적이고 간결한 API를 제공하여 비동기 통신을 보다 쉽게 구현할 수 있다.
- Promise 기반으로 작동하기 때문에 비동기 요청을 처리할 때 then()과 catch()를 사용하여 데이터를 처리할 수 있어, 코드가 더 간결해지고, 오류 처리도 용이하다.
- 브라우저와 Node.js 환경에서 모두 사용할 수 있기 때문에 클라이언트와 서버 사이의 데이터 통신에 매우 유용하게 사용된다.
다만, fetch는 ES6부터 지원되기에 기존 브라우저에서 지원하지 않을 수 있으며, xhr 객체와 달리 요청(request)를 취소할 수 없는 단점이 이 있다.
export const fetchEx = async (options) => {
const { url, ...restOptions } = {
...defaultOptions,
...options,
headers: {
...defaultOptions.headers,
...options.headers,
},
};
console.log(url);
console.log(restOptions);
const response = await fetch(url, restOptions);
if (response.ok) {
response.data = await response.json();
}
return response;
};
fetchEx 함수를 호출하여 네트워크 요청을 보내고, 응답을 받아와서 JSON 데이터를 추출하는 기본적인 예제로,
요청과 응답은 비동기적으로 처리되므로 await 키워드를 사용하여 코드의 실행을 조절하고, fetch 함수를 통해 네트워크 요청을 보낸다.
응답이 성공적인 경우에는 JSON 데이터를 파싱하여 사용자에게 제공합니다.
fetchEx.get = (url,options)=>{
return tiger({
url,
...options
})
}
fetchEx.post = (url,body,options)=>{
return tiger({
method:'POST',
url,
body:JSON.stringify(body),
...options
})
}
fetchEx.delete = (url,options)=>{
return tiger({
method:'DELETE',
url,
...options
})
}
fetchEx.put = (url,body,options)=>{
return tiger({
method:'PUT',
url,
body:JSON.stringify(body),
...options
})
}
xhr과 마찬가지로, 각각 GET, POST, DELETE, PUT HTTP 메서드를 사용하여 네트워크 요청을 보낼 수 있도록 도와주는 함수를 메서드로 만들어서 쓴다.
xhr에 비해 fetch가 가독성이 훨씬 좋고, 많이 사용하지만 Resopnse Timeout이 없어 네트워크 요청을 보내고 서버로부터 응답이 없거나 지연되는 상황 발생시 계속 대기하게 된다. 이를 위해 setTimeout 함수를 이용하는 방법을 주로 사용한다!
ETC
최근에는 Ajax와는 별개로, ES6에서 독립적으로 개발된 외부 라이브러리인 Axios가 널리 쓰이고 있다.
Axios
- Promise 기반: Axios는 Promise 기반으로 비동기 요청을 처리합니다. 이를 통해 비동기 코드를 보다 간편하고 가독성 있게 작성할 수 있다.
- 브라우저와 Node.js에서 모두 사용 가능: Axios는 브라우저 환경과 Node.js 환경에서 모두 사용할 수 있어서 클라이언트와 서버 간의 데이터 통신에 널리 사용된다.
- 간결하고 직관적인 API: Axios는 간결하고 직관적인 API를 제공하여 개발자가 쉽게 HTTP 요청을 생성하고 응답을 처리할 수 있도록 한다.
- 요청과 응답의 인터셉터: Axios는 요청과 응답에 대한 인터셉터(interceptor)를 제공하여 요청과 응답을 가공하거나 에러 처리를 할 수 있다.
- 자동 JSON 변환: Axios는 자동으로 JSON 데이터를 변환해주기 때문에, 서버와의 데이터 통신에서 JSON 데이터를 쉽게 다룰 수 있다.
- 오류 처리 기능: Axios는 HTTP 요청 중 발생하는 오류를 쉽게 처리할 수 있는 기능을 제공한다.
- 업로드와 다운로드 기능: Axios는 파일 업로드와 다운로드를 지원하여 파일과 함께 데이터를 주고받을 수 있다.
- 브라우저의 XMLHttpRequest와 Fetch API를 래핑하고 있는 라이브러리로, 브라우저의 내장 기능을 활용하여 비동기 통신을 수행한다.
----------------------------------------------------------------------------------------------------------------------
Promise에 관한 내용은 내용이 너무 길어질 것 같아서 다른 글에서 남겨보고자 한다.