오늘은 동기,비동기에 대해 알아보겠습니다.
한번에 하나씩 순서대로 수행하는 것입니다.
한마디로 번호표라고 생각 하시면 편합니다.
예를 들어 병원에서 번호표를 뽑고 진료를 기다리고 있을 때 자기 차례가 오기 전까지 앞 번호를 뽑은 사람들이 차례대로 한명씩 진료를 받는 방법이 동기라고 생각하시면 됩니다
주로 티케팅 예매 사이트에서 순차대로 먼저 클릭한사람 먼저 서버에 요청을 보낸사람 순으로 처리해줄때 사용합니다
그렇다면 동기 프로그래밍은 무엇일까요 간단한 예를 들어 보겠습니다
console.log("작업 1");
console.log("작업 2");
console.log("작업 3");
이런식으로 간단한 코드를 본다면
console.log를 작업1 ~ 작업3 까지 차례대로 하나씩 코드를 실행 합니다.
비동기란 동시에 여러 작업을 수행하는 것입니다.
예를 들자면 동시에 여러 손님을 받는 식당입니다.
손님이 동시에 여러개의 주문을 넣는 다면 동시에 여러 요리를 해야하기 떄문에 비동기 작업이라고 볼 수 있습니다.
주로 로그인기능을 구현할때 순서 상관없이 처리해줄 때 사용합니다
그렇다면 비동기 프로그래밍은 무엇일까요 간단한 예를 들어보겠습니댜.
console.log('1');
setTimeout(() => {
console.log('2');
}, 2000);
이 코드는 1이 실행된 이후에 2초뒤에 콘솔창에서 2가 출력되는 코드 입니다.
setTimeout(() => {
console.log('2');
}, 2000);
console.log('1');
여기서 위 코드 처럼 함수 선언을 먼저 한다면
실행 결과가 2가 먼저 출력되고 2초뒤에 1이 출력될 것 같지만
setTimeout이 비동기 함수이기 때문에 2초가 지나기 전에 먼저 1이 출력되고 2초후 2가 출력됩니다.
한마디로 비동기 작업이기 때문에 타이머가 끝나길 기다리지 않고 먼저 다른 값을 출력합니다.
콜백함수란 다른 함수의 인자로 전달되는 함수
function main(callback) {
callback();
}
main(() => {});
위 코드 처럼 함수를 main에서 호출할 때 인자로 전달 돼는 함수를 콜백함수라고 합니다.
콜백 함수를 전달 받는 함수는 전달 받은 콜백을 함수 내부에서 호출 하도록 구현 되어있습니다.
콜백함수를 쓴다면 비동기 작업도 순차적으로 실행 가능 합니다
// 1.로그인
function login(username, callback) {
setTimeout(() => {
callback(username);
}, 1000);
}
// 2. 장바구니에 넣기
function addToCart(product, callback) {
setTimeout(() => {
callback(product);
}, 1000);
}
// 3.결제하기
function makePayment(cardNumber, product, callback) {
setTimeout(() => {
callback(cardNumber, product);
}, 1000);
}
login("홍길동", (username) => {
console.log(`${username}님 안녕하세요`);
addToCart("시계", (product) => {
console.log(`${product}를 장바구니에 담았습니다`);
makePayment("0000000000000000", product, (cardNumber, item) => {
console.log(`${cardNumber.slice(0, 6)}로 ${product}를 구매했습니다`);
});
});
});
다만 이런식으로 코드를 짜게 된다면 가독성도 안좋을 뿐만 아니라 콜백함수를 계속 호출하는 콜백지옥에 빠지게 됩니다 또한 유지보수도 힘들어지게 됩니다.
그렇다면 이러한 문제를 어떻게 해결 할까요?
바로 Promise를 사용하면 됩니다.
Promise는 비동기 작업의 상태(진행중, 성공, 실패)를 나타내는 객체입니다.
비유하자면 비동기 작업이 끝나면 결과값이나 에러가 들어가는 상자와 같습니다.
세 가지 상태(pending, fulfilled, rejected)가 있으며 작업의 결과에 따라 상태와 값이 결정됩니다.
pending은 대기 상태 result는 undefined
fulfillde는 성공했을 때 완료상태 result는 결과값
rejected는 실패했을 때 오류상태를 나타냄 result는 error
new Promise(익시큐터 함수)로 생성할 수 있습니다.
익시큐터 함수는 두 개의 인자(resolve, reject)를 받으며 비동기 작업이 성공하면 resolve, 실패하면 reject를 호출하면 됩니다.
생성 즉시 익시큐터 함수가 실행됩니다.
익시큐터 함수는 new Promise()에 괄호 안에있는 함수를 뜻합니다
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({ name: '철수' });
}, 1000);
});
비동기 작업마다 Promise를 반환하는 함수로 만들고 원하는 시점에 호출해 비동기 작업을 실행할 수 있습니다.
함수 내부에서 new Promise로 작업을 변환하고 return하면 됩니다.
then: Promise가 성공(fulfilled)하면 실행되는 콜백을 등록합니다.
catch: 실패(rejected) 시 에러를 처리합니다.
finally: 작업 성공/실패와 관계없이 무조건 실행되는 마지막 후처리 콜백을 지정할 수 있습니다.
getData()
.then((data) => { console.log(data); })
.catch((err) => { console.log(err); })
.finally(() => { console.log('작업 완료'); });
then의 반환값이 또 다른 Promise라면 다음 then에서 결과가 이어받아집니다.
여러 비동기 작업을 순차적으로 이어붙여 관리할 수 있어 '콜백 지옥'을 예방합니다.
getData()
.then((data) => getMoreData(data))
.then((moreData) => processData(moreData))
.catch((err) => handleError(err));
fetch란 JavaScript에서 서버와 데이터를 주고받기 위해 사용된다.
fetch 등 많은 현대 웹 API는 Promise를 반환합니다.
then/catch/finally와 chaining으로 비동기 데이터 요청 및 처리 로직을 간결하게 구성할 수 있습니다.
예시코드
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log("에러발생");
})
.finally(() => {
console.log("마무리 작업");
});
사진 차럼 fetch로 링크에있는 test서버의 데이터들을 불러오고 콘솔에서 출력 혹시 오류가 발생한다면 .catch
로 에러 검출 finally로 꼭 실행되야하는 후처리 작업
// 1.로그인
function login(username) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (username) {
resolve(username);
} else {
reject(new Error("아이디를 입력해주세요"));
}
}, 1000);
});
}
// 2.장바구니에 넣기
function addToCart(product) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (product) {
resolve(product);
} else {
reject(new Error("장바구니에 넣을 상품이 없어요"));
}
}, 1000);
});
}
// 3.결제하기
function makePayment(cardNumber, product) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (cardNumber !== 16) {
reject(new Error("잘못된 카드 번호 입니다."));
return;
}
if (!product) {
reject(new Error("결제할 상품을 넣어 주세요"));
return;
}
resolve(product);
}, 1000);
});
}
login("홍길동")
.then((username) => {
console.log(`${username}님 환영합니다`);
return addToCart("시계");
})
.then((product) => {
console.log(`${product}를 장바구니에 넣었어요`);
return makePayment("0000000000000000", product);
})
.then((product) => {
console.log(`${product}를 구매했습니다`);
});
// 1.로그인
function login(username, callback) {
setTimeout(() => {
callback(username);
}, 1000);
}
// 2. 장바구니에 넣기
function addToCart(product, callback) {
setTimeout(() => {
callback(product);
}, 1000);
}
// 3.결제하기
function makePayment(cardNumber, product, callback) {
setTimeout(() => {
callback(cardNumber, product);
}, 1000);
}
login("홍길동", (username) => {
console.log(`${username}님 안녕하세요`);
addToCart("시계", (product) => {
console.log(`${product}를 장바구니에 담았습니다`);
makePayment("0000000000000000", product, (cardNumber, item) => {
console.log(`${cardNumber.slice(0, 6)}로 ${product}를 구매했습니다`);
});
});
});
쇼핑몰 로그인 → 장바구니 담기 → 결제 등 순차 작업에 Promise chaning을 사용하면 가독성 좋은 코드로 쉽게 관리할 수 있습니다.
각 단계에서 발생 가능한 에러마다 catch를 중간중간 넣어 유연하게 에러를 처리할 수도 있습니다.
async 키워드는 함수를 비동기 함수로 만들어 줍니다.
async가 붙은 함수는 항상 Promise가 반환됩니다.
함수 내부에서 일반 값을 반환하면 그 값이 자동으로 Promise로 변환되어 반환됩니다.
내부에서 직접 Promise를 반환하면 중첩되지 않고 그대로 반환됩니다.
예시
async function getUser() {
return '안녕';
}
→ 내부적으로 Promise.resolve('안녕') 형태로 반환됩니다.
await는 Promise가 완료될 때까지 기다리는 키워드입니다.
반드시 async 함수 내부에서만 사용할 수 있습니다.
해당 Promise가 resolve될 때까지 코드를 일시 중단하고 결과값을 받아 변수에 담습니다.
예시
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('데이터를 받아왔습니다');
resolve('서버1');
},2000);
})
}
async function getUser() {
const result = await networkRequest();
console.log(result);
}
const user = getUser();
user.then((name) => console.log(name));
await은 비동기 작업이 완료될 때까지 기다려서 코드가 마치 동기적으로 실행되는 것처럼 보입니다.
try...catch 문을 활용하면 비동기 함수 내부의 에러를 쉽게 처리할 수 있습니다.
await으로 호출되는 함수에서 throw가 발생하면 catch 블록으로 전달됩니다.
예시
async function getData() {
try {
const data = await fetchData();
console.log(data);
} catch (error) {
console.error('에러 발생:', error.message);
}
}
에러가 발생하면 코드가 멈추지 않고 안전하게 처리됩니다.
async function fetchData() {
const response = await fetch("https://jsonplaceholder.typicode.com/users")
const data = await response.json();
console.log(response);
}
fetchData();
fetch("https://jsonplaceholder.typicode.com/users")
.then((response) => {
return response.json();
})
.then((data) => {
console.log(data);
})
.catch((error) => {
console.log("에러발생");
})
.finally(() => {
console.log("마무리 작업");
});
이 코드는 URL에서 사용자 데이터 JSON을 비동기적으로 받아와서 데이터가 완전히 로드된 후 콘솔에 출력하는 기능을 합니다.
이처럼 async와 await를 쓰면 복잡한 Promise 체인 없이도 마치 동기 처리하듯 깔끔하고 직관적인 비동기 코드 작성이 가능합니다.
오늘은 비동기,동기 , 콜백함수, promise, async, await에 대해 알아보았습니다.
promise개념이 조금 어려웠지만 여러 영상이나 블로그 찾아보면서 공부 해보았습니다.
promise와 async,await을 잘 사용하면 비동기 프로그래밍을 효율적이게 구현할 수 있을것 같습니다.
글 읽어주셔서 감사합니다 피드백 많이 해주세요!