웹 애플리케이션에서 서버 쪽 데이터가 필요할 때는 Ajax 기법을 사용하여 서버의 API를 호춤함으로써 데이터를 수신한다. 이렇게 서버의 API를 사용해야 할 때는 네트워크 송수신 과정에서 시간이 걸리기 때문에 작업이 즉시 처리되는 것이 아니라, 응답을 받을 때까지 기다렷다가 전달받은 응답 데이터를 처리해야한다. 이 과정에서 해당 작업을 비동기적으로 처리하게 된다.
동기 : 요청이 끝날 때까지 기다리는 동안 중지 상태가 되기 때문에 다른 작업을 할 수 없다. 요청이 끝나야 비로소 그다음 예정된 작업을 할 수 있다.
비동기 : 웹 애플리케이션이 멈추지 않기 때문에 동시에 여러가지 요청을 처리할 수 잇고, 기다리는 과정에서 다른 함수도 호출할 수 있다.
이렇게 서버 API를 호출할 때 외에도 작업을 비동기적으로 처리할 때가 있는데, 바로 setTimeout함수를 사용하여 특정 작업을 예약할 때이다.
자바스크립트에서 비동기 작업을 할 때 가장 흔히 사용하는 방법은 콜백 함수를 사용하는 것이다.
function increase(number, callback) {
setTimeout(() => {
const result = number + 10;
if (callback) {
callback(result);
}
}, 1000);
}
increase(0, (result) => {
console.log(result);
});
콜백함수를 중첩으로 구현할 수도 있다. ( 1초에 걸쳐 10씩 증가되는 값을 반환하려면)
function increase(number, callback) {
setTimeout(() => {
const result = number + 10;
if (callback) {
callback(result);
}
}, 1000);
}
console.log("작업시작");
increase(0, (result) => {
console.log(result);
increase(result, (result) => {
console.log(result);
increase(result, (result) => {
console.log(result);
increase(result, (result) => {
console.log(result);
console.log("작업 완료");
});
});
});
});
이렇게 콜백안에 또 콜백을 넣어서 구현할 수 있지만, 여러번 중첩이 되면 코드의 가독성이 나빠진다. 이러한 형태의 코드를 '콜백 지옥'이라 부르고 이러한 코드는 지양해야 할 형태이다.
promise는 콜백 지옥같은 코드가 형성되지 않게 하는 방법으로 ES6에 도입된 기능이다.
.then을 사용하여 다음 작업을 설정한다. .catch로 에러를 잡아낸다.
function increase(number) {
const promise = new Promise((resolve, reject) => {
//resolve는 성공, reject는 실패
setTimeout(() => {
const result = number + 20;
if (result > 50) {
//50보다 높으면 에러 발생시키기
const e = new Error("NumberTooBig");
return reject(e);
}
resolve(result); // number 값에 +10 후 성공 처리
}, 1000);
});
return promise;
}
increase(0)
.then((number) => {
//Promise에서 resolve된 값은 .then을 통해 받아 올 수 있음.
console.log("number1 :", number);
return increase(number); //Promise를 리턴하면
})
.then((number) => {
//또 .then으로 처리가능
console.log("number2 :", number);
return increase(number);
})
.then((number) => {
console.log("number3 :", number);
return increase(number);
})
.then((number) => {
console.log("number4 :", number);
return increase(number);
})
.catch((e) => {
//도중에 에러가 발생한다면 .catch를 통해 알 수 있다.
console.log(e);
});
async/await는 promise를 더욱 쉽게 사용할 수 있도록 해주는 ES8(ES2017)문법이다. 이 문법을 사용하려면 function앞 부분에 async 키워드를 추가하고, 해당 함수 내부에서 promise의 앞부분에 await 키워드를 사용한다. 이렇게 하면 promise가 끝날 때까지 기다리고, 그 결과 값을 특정 변수에 담을 수 있다.
function increase(number) {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const result = number + 10;
if (result > 50) {
const e = new Error("NumberTooBig");
return reject(e);
}
resolve(result);
}, 1000);
});
return promise;
}
async function runTasks() {
try {
//try / catch 구문을 사용하여 에러를 처리한다.
let result = await increase(0);
console.log("result1:", result);
result = await increase(result);
console.log("result2:", result);
result = await increase(result);
console.log("result3:", result);
result = await increase(result);
console.log("result4:", result);
result = await increase(result);
console.log("result5:", result);
result = await increase(result);
console.log("result6:", result);
} catch (e) {
console.log(e);
}
}
runTasks();
axios는 현재 가장 많이 사용되고 있는 자바스크립트 HTTP클라이언트이다. 이 라이브러리의 특징은 HTTP 요청을 Promise 기반으로 처리한다는 점이다.
onClink 함수에서는 axios.get함수를 사용했다. 이 함수는 파라미터로 전달된 주소에 GET요청을 해주고 이에 대한 결과는 .then을 통해 비동기적으로 확인 할 수 있다.
// import logo from './logo.svg';
// import './App.css';
import { useState } from 'react';
import axios from 'axios';
function App() {
const [data, setDate] = useState(null);
const onClick = () => {
axios
.get('https://jsonplaceholder.typicode.com/todos/1')
.then((response) => {
console.log(response);
setDate(response.data);
});
};
return (
<div className="App">
<div>
<button onClick={onClick}>불러오기</button>
</div>
{data && (
<textarea
rows={7}
value={JSON.stringify(data, null, 2)}
readOnly={true}
/>
)}
</div>
);
}
export default App;