Javascript : 싱글 스레드 기반 비동기통신
( 자바스크립트 자체는 비동기 X, 웹 브라우저가 비동기 통신을 하게 도와주는 것)
스레드 : 하나의 프로그램 실행 작업
- 싱글 스레드 : 한 번에 하나의 작업만 수행 가능
- 멀티 스레드 : 한 번에 여러 개의 작업을 수행 가능
JavaScript에서는 멀티스레드 프로그래밍을 지원하지 않으므로, 비동기 프로그래밍을 통해 여러 작업을 병렬로 처리할 수 있는 것으로 대체하고 있음
// 비동기 프로그래밍 예시
console.log("1")
setTimeout(() => {
console.log("2");
}, 0)
console.log("3")
// 1
// 3
// 2
일반적인 코드 실행 시,
- 기본적으로 Call Stack에 쌓임
- 후에 Browser Console에 가는 형식
비동기 코드(setTimeout 등) 실행 시,
- Web API로 이동
- 0초(위 예시에서 0ms로 설정) 간 대기를 했다가 Callback Queue로 감
- Event loop를 계속 감시하면서 Call Stack이 비어있는 지 확인
- 비어 있으면 Callback Queue에서 Call Stack으로 이동
❓ setTimeout는 정확한 시간을 표시하지 않음 → 일련의 이동 과정이 복잡하기 때문
setTimeout({} , 1000) → 정확한 1000ms가 아닌, 약 1000ms 라고 생각하면 됨
위의 예시에서 알 수 있듯이, 비동기 프로그래밍은 시점 파악이 안 된다는 한계가 있음
비동기 프로그래밍 발전 과정
- Callback Function (es5)
- 비동기 상에서 동기처럼 시점파악을 하기 위해 나온 개념
- 비동기 작업이 완료되면 실행할 콜백 함수를 지정하여 한계를 해결할 수 있다.
function getData(callback) { setTimeout(() => { // setTimeout() : 지정된 시간(ms) 후에 특정 함수 실행하는 내장 함수 const data = "callback data"; callback(data); }, 2000); } function processData(data, callback) { setTimeout(() => { const processedData = data + " processed"; callback(processedData); }, 2000); } getData((data) => { console.log(data); processData(data, (processedData) => { console.log(processedData); }); }); // 2초 뒤 callback data // 4초 뒤 callback data processed
- 단점 : Callback Hell
- 비동기 작업의 결과에 따라 처리할 다음 작업을 지정하는 콜백 함수의 중첩이 깊어져 코드가 복잡하고 가독성이 떨어지는 현상
getData(function(data) { if (data instanceof Error) { // handle error } else { processData(data, function(processedData) { if (processedData instanceof Error) { // handle error } else { finalTask(processedData, function(result) { if (result instanceof Error) { // handle error } else { // use result } }); } }); } });
- Promise (es6)
- Callback hell을 해결하기 위해 등장
- 비동기 작업의 결과를 나타내는 Promise 객체를 생성하여 한계를 해결할 수 있다.
- Promise에서 resolve를 호출 시 then으로 조회 가능 → fulfilled state
- Promise에서 reject를 호출 시 catch로 조회 가능 → rejected state
const obj1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('Hello, Promise!'); // resolve() : }, 1000); // Promise의 상태를 "완료"로 변경 후 결과값 전달 }); obj1 .then((message) => { // .then() : Promise가 "완료"할 경우, console.log(message); // 성공 상태에서의 결과 값을 매개변수에 전달 }) .catch((error) => { // .catch() : Promise가 "실패"할 경우, console.error(error); // 실패 이유를 나타내는 값이 매개변수에 전달 }); // console.log는 일반적인 메시지를 출력, 정상 작동하는 지 확인하는 데 사용 // console.error는 오류 메시지를 출력하는 데 사용, 오류 메시지는 파란색으로 표시됨
- 단점 : 결과를 받아오는 과정에서 복잡한 코드가 필요하고, 에러 처리가 복잡함
// Callback 문제 간단하게 getData() .then(processData) .then(finalTask) .then((result) => { // use result }) .catch((error) => { // handle error });
- async/await (es8)
- try-catch로 쉽게 에러 처리가 가능하고, await를 통해 비동기 처리 결과를 쉽게 받아올 수 있다.
- 비동기 작업을 동기적으로 처리하는 것처럼 코드를 작성할 수 있도록 해주는 async/await 구문을 사용할 수 있다.
- 사용하기 위한 조건
- Promise로 return되는 것들에 한해서만 가능
- 앞에 async 함수를 붙여줘야 await 사용이 가능
async function getDatas(){
// 동기처럼 동작 --> promise처럼 깊게 쌓을 필요가 없다.
const aData = await axios.get("A");
// B에 요청을 하는데, A의 데이터를 보낼 수 있다.
const bData = await axios.get("B", aData);
// C에 요청을 하는데, B의 데이터를 보낼 수 있다.
const cData = await axios.get("C", bData);
// Promise 객체를 이용하는 방식이기 때문에, 위의 Promise 방식으로도 사용 가능
const aData = await axios.get("A");
aData.then(data => {}).catch(error => {});
Ajax (Asynchronous Javascript and XML)
브라우저와 웹 서버 간에 데이터를 교환하는 비동기 통신 기술
- 사용자의 Event가 있으면 전체 페이지가 아닌 일부분만을 업데이트 할 수 있게 해줌
- 자바스크립트에서는 Ajax 통신을 위해 XHR 객체를 사용
과거에는 무조건 데이터 갱신 시 깜빡임 (새로고침) 이 발생
→ 브라우저는 java 해석능력이 없음
→ 서버에서 이 java 형식을 읽어서 html 형식으로 만들어서 return
→ 이 과정 중에 새로고침이 일어난다
XHR (XMLHttpRequest)
- Ajax방식으로 서버와 브라우저가 데이터를 주고받을때 사용하는 API
- Promise가 아닌, XMLHttpRequest 객체를 사용하여 비동기 데이터를 가져옴
- 콜백 함수를 사용하여 비동기 데이터 처리를 수행
fetch API
- XHR보다 더 간단한 API
- Promise 기반으로 동작 - async/await 구문으로 코드 가독성 높일 수 있음
- XHR에 비해 더 많은 요청 타입(GET, POST, PUT, DELETE 등)을 지원
개발자도구(F12) - [네트워크] - [Fetch/XHR] 에서 비동기 방식으로 얻어온 값을 확인 가능
fetch('<https://jsonplaceholder.typicode.com/todos/1>')
.then(response => response.json())
.then(json => console.log(json))
async function getData(){
try{
// 1. fetch로 비동기 요청을 해 Promise 객체인 Response 객체를 받아옴
const fetchData = await fetch("<https://jsonplaceholder.typicode.com/todos>");
// 2. await에 의해 fetchData가 resolve되면, response 객체에서 json 형식으로 데이터를 가져옴
const data = await fetchData.json();
console.log(data);
} catch (error) {
console.log(error); // Promise -> try-catch 문으로 error 출력
}
}
getData();
// json 형식은 바로 나타낼 수 없어서
// JSON.stringfy -> JSON 형식을 문자열로 치환
async function getData(){
try{
const fetchData = await fetch("<https://jsonplaceholder.typicode.com/todos>");
const data = await fetchData.json();
console.log(data);
document.querySelector('div').insertAdjacentHTML('beforeend', JSON.stringfy(data));
} catch (error) {
console.log(error);
document.querySelector('div').textContent = "데이터를 가져올 수 없습니다.";
}
}
getData(); // error 시 "데이터를 가져올 수 없습니다" 가 웹에 표시
axios
브라우저와 Node.js 환경에서 사용하는 HTTP 통신 라이브러리
다음과 같은 면에서 fetch API보다 더 나은 기능을 제공하고 있음
- 비동기 처리
- fetch API : 여러 개의 요청을 관리하고, 에러 처리, 결과 값을 쉽게 얻을 수 있는 방법이 없음.
- axios : Promise 기반의 API를 제공하여, 비동기 처리와 관련된 기능을 간편하게 구현 가능
- 글로벌 설정
- fetch API : 각각의 fetch 요청마다 글로벌 설정을 다시 적용해야 함
- axios : 글로벌 설정을 제공 / 요청 URL의 기본 접두사, 헤더 등을 설정 가능
- 인터셉터 - 요청 & 응답 데이터 가공 등을 한 곳에서 관리하는 기능
- fecth API : 인터셉터 설정이 없어, 직접 구현해야 함
- axios : 인터셉터를 통해 요청이나 응답에 대한 처리를 간편하게 수행 가능
- 캐시 관리:
- fecth API : axios보다 제한된 캐시 관리 기능 제공 / HTTP 요청을 보낼 때 HTTP 캐시 헤더를 설정하여 캐시 관리를 구현 가능
- axios : fetch API보다 더 확장된 캐시 관리 기능을 제공, 요청을 캐시하거나, 캐시에서 응답을 가져올 수 있음
// 예제
axios.get("API 주소") // 요청 성공 시 resolve를 실행
// 요청 거부 시 rejected를 실행
axios.get("주소")
.then(data => axios.get("B", data))
.then(d => axios.get("C", data))
// 외부 라이브러리이기 때문에, 가져와 주어야 함
// HTTP Method
// GET POST PATCH PUT DELETE (CRUD)
// GET -> 데이터 가져오기
// POST -> 데이터 작성
// PATCH, PUT -> 데이터 수정
// DELETE -> 데이터 삭제
// 1. Promise then 이용
axios.get('<<a href=https://jsonplaceholder.typicode.com/todos>https://jsonplaceholder.typicode.com/todos</a>>').then(res => {
console.log(res);
console.log(res.data);
}).catch(error => {console.log(error)});
// 2. async await
async function getData(){
const result = await axios.get('<<a href=https://jsonplaceholder.typicode.com/todos>https://jsonplaceholder.typicode.com/todos</a>>');
console.log(result.data);
}
getData();