📍 콜백 함수(callback function)
- 콜백 함수는 다른 함수의 인자로 전달되어 특정 이벤트가 발생하거나 작업이 완료된 후 호출되는 함수로 주로 비동기 작업에 사용된다.
- 여러 번 순차적으로 처리하고 싶다면 콜백 함수를 중첩해 구현할 수 있는데, 가독성이 좋지 않아 지양한다.
// increase 함수 => 숫자에 10을 더하고, 1초 후 콜백 함수를 호출
function increase(number, callback) {
setTimeout(() => {
const result = number + 10; // 10을 더한 결과 계산
if (callback) {
callback(result); // 결과를 콜백 함수로 전달
}
}, 1000); // 1초 후에 실행
}
// increase 함수 호출 후 1초 후 결과 출력
increase(0, result => {
console.log(result); // 출력: 10
});
📍 Promise
- JavaScript에서 비동기 작업을 처리하는 객체로, 비동기적인 코드 실행의 결과를 다루기 쉽게 해주기 위해 ES6에 도입된 기능이다.
- Promise는 비동기 작업이 완료 또는 실패했을 때의 결과를 나중에 사용할 수 있게 해준다. 이를 통해 콜백 지옥(callback hell) 같은 복잡한 코드를 피할 수 있다.
📍 Promise 객체의 상태와 메서드
- ① Promise 상태 (Pending, Resolved, Rejected)
비동기 작업의 상태를 나타내는 세 가지 상태
✔ Pending
: 비동기 작업이 아직 완료되지 않은 상태로 resolve나 reject가 호출될 때까지 기다림.
✔ Resolved
: 비동기 작업이 성공적으로 완료된 상태로 Promise가 처리된 결과를 반환함.
✔ Rejected
: 비동기 작업이 실패한 상태로 Promise가 실패 이유나 에러를 반환함.- ② Promise 메서드 (resolve, reject, then, catch, Promise 체이닝)
Promise를 사용하여 비동기 작업을 처리할 때 사용되는 메서드
✔ resolve
비동기 작업이 성공적으로 완료되었을 때 호출됨.
이때 resolve가 전달하는 값은 .then()에서 사용할 수 있음.
✔ reject
비동기 작업이 실패했을 때 호출되어 에러를 반환함.
이때 reject는 에러 메시지나 실패 이유를 전달하고, 실패 이유는 .catch()에서 처리할 수 있음.
✔ then
Promise가 성공적으로 완료된 후 실행할 콜백 함수를 정의함.
resolve가 호출되면, 그 결과를 .then()에서 다룰 수 있음.
✔ catch
Promise가 실패한 경우 실행할 콜백 함수를 정의함.
reject가 호출되면, 그 에러를 .catch()에서 처리함.
✔ Promise 체이닝
여러 비동기 작업을 순차적으로 처리할 때 사용됨.
then 메서드를 이어서 호출해 비동기 작업들을 순차적으로 처리할 수 있음.
// increase 함수는 숫자에 10을 더하고 Promise를 반환
function increase(number) {
return new Promise((resolve) => {
setTimeout(() => {
const result = number + 10; // 10을 더한 결과 계산
resolve(result); // Promise를 해결하고 결과 반환
}, 1000); // 1초 후에 실행
});
}
// 비동기 함수 main을 정의하여 await로 increase 함수의 완료를 기다림
async function main() {
const result = await increase(0); // increase 함수 호출 후 1초 기다림
console.log(result); // 출력: 10
}
// main 함수 호출
main();
예시2
여러 작업을 연달아 처리한다고 해서 함수를 여러 번 감싸는 것이 아니라 .then을 사용해 그다음 작업을 설정하기 때문에 콜백 지옥이 형성되지 않는다.
콜백 지옥
// 콜백 지옥 예제 (콜백이 중첩됨)
doTask1(function(result1) {
doTask2(result1, function(result2) {
doTask3(result2, function(result3) {
doTask4(result3, function(result4) {
console.log('최종 결과: ', result4);
});
});
});
});
promise 사용한 개선
// Promise를 사용하여 콜백 지옥을 피하는 예제
function doTask1() {
return new Promise((resolve) => {
setTimeout(() => resolve('결과1'), 1000);
});
}
function doTask2(input) {
return new Promise((resolve) => {
setTimeout(() => resolve(input + ' -> 결과2'), 1000);
});
}
function doTask3(input) {
return new Promise((resolve) => {
setTimeout(() => resolve(input + ' -> 결과3'), 1000);
});
}
function doTask4(input) {
return new Promise((resolve) => {
setTimeout(() => resolve(input + ' -> 결과4'), 1000);
});
}
// Promise 체이닝을 이용한 비동기 작업 처리
doTask1()
.then(result1 => {
return doTask2(result1); // 첫 번째 작업이 끝난 후 두 번째 작업
})
.then(result2 => {
return doTask3(result2); // 두 번째 작업이 끝난 후 세 번째 작업
})
.then(result3 => {
return doTask4(result3); // 세 번째 작업이 끝난 후 네 번째 작업
})
.then(finalResult => {
console.log('최종 결과:', finalResult); // 모든 작업 완료 후 최종 결과 출력
})
.catch(error => {
console.error('에러 발생:', error);
});
📍 async와 await
- JavaScript에서 비동기 작업을 더 쉽게 다루기 위해 ES2017(ES8)에서 도입된 기능
- React에서도 비동기 작업(예: API 호출, 데이터 처리 등)을 처리할 때 async와 await를 사용해 코드의 가독성을 높이고, Promise를 처리하는 방식이 더 간결하고 직관적이게 된다.
- async 함수 ⇒ 함수 앞에 붙여서 비동기 함수임을 명시하는 키워드
- async로 선언된 함수는 항상 Promise를 반환
- await 키워드 ⇒ Promise가 처리될 때까지 기다리게 하는 키워드
- async 함수 내에서만 사용할 수 있음.
- Promise가 해결되거나 거부될 때까지 함수의 실행을 일시 정지시키고, Promise가 해결되면 그 값을 반환함.
📍 기본 사용법
// 예시 // 비동기 함수 선언 async function fetchData() { const response = await fetch('https://api.example.com/data'); const data = await response.json(); return data; }
import React, { useState, useEffect } from 'react';
const FetchDataComponent = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// 비동기 함수로 데이터 호출
const fetchData = async () => {
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
setData(data);
} catch (error) {
setError(error.message);
} finally {
setLoading(false);
}
};
// 컴포넌트가 마운트되면 데이터 요청
useEffect(() => {
fetchData();
}, []); // 빈 배열로 한 번만 실행되게 설정
if (loading) {
return <div>Loading...</div>;
}
if (error) {
return <div>Error: {error}</div>;
}
return (
<div>
<h1>Data:</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default FetchDataComponent;