1. Node.js 비동기 처리 방식
- 비동기 발생
실행되는 코드가 기다려야하는 시간이 생긴다는 의미
(이전 작업이 오래 걸리면 기다려주지 않고 다음 코드를 무작정 실행)
ex) setTimeOut(), setInterval(), query() ...
- 비동기 처리
이전 작업의 시간을 다 기다려서 순서를 맞춰서 코드를 실행
1. 콜백 함수: 할 일 다하고, 콜백함수 실행 (= 순서 맞춰서 뒤에 실행)
2. promise (resolve, reject)
3. then & catch
4. ES2017 promise => async & await
1.1. Promise
let promise = new Promise(function(resolve, reject) {
// ...
});
Promise 는 resolve, reject 를 매개변수로 가지고 있는데, 이 매개변수들은 Promise 의 할 일이 끝난 뒤 불러 줄 콜백 함수이다.
Promise 에 마우스를 올려놓으면 위와 같이 뜨는데, executor 는 Promise 가 할 일(지켜내야 할 약속)이고, Promise 가 할 일을 성공적으로 끝내면 resolve 를, 할 일을 실패하면 reject 를 호출한다.
resolve/reject는 일이 끝나면 무조건 resolve/reject 를 호출해서 함수 안에 값을 넣어 전달한다.
- 성공 -> resolve(결과)
- 실패 -> reject(에러)
1.2. then()
then 메서드는 Promise 가 완료되었을 때 실행되는 콜백을 설정하는 메서드로 2개의 콜백 함수를 받는다.
첫 번째 콜백 함수는 작업이 성공했을 때, 즉 resolve 가 사용될 때 호출되고,
두 번째 콜백 함수는 작업이 실패했을 때, 즉 reject 가 사용될 때 호출된다.
이 예시 코드를 살펴보자.
3초 후 resolve("완료") 가 호출되어 promise 가 완료된다. 성공적으로 일을 끝냈기 때문에 then 의 첫 번째 콜백 함수의 result 로 결과인 "완료!" 가 들어가게 되고 콜백 함수가 실행되어 "완료!" 가 콘솔에 출력된다.
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("완료!"), 3000); // 할 일
});
// Promise의 기본 메서드 then: promise가 일 다 하고(= 약속 다 지키고) 호출해야하는 함수
promise.then(
function(result) {
console.log(result);
},
function(error) {
console.log(error);
}
);
1.3. promise chaining
여러 개의 비동기 작업을 순차적으로 처리할 때 하나의 Promise 가 완료된 후 다음 Promise 를 이어서 처리하는 방식이다.
각 then 메서드는 새로운 Promise 를 반환하기 때문에, 이를 이용해 연속적으로 비동기 작업을 처리할 수 있다.
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("완료"), 3000);
}).then(
function(result) {
console.log(result);
return result + "!!!!!"; // 다음 then 메서드의 result로 들어감
},
function(error) {}
).then(
function(result) {
console.log(result);
return result + "!!!!!"; // 다음 then 메서드의 result로 들어감
},
function(error) {}
).then(
function(result) {
console.log(result);
return result + "!!!!!"; // 다음 then 메서드의 result로 들어감
},
function(error) {}
);
1.4. async & await
async 는 자바스크립트에서 비동기 함수를 정의할 때 사용하는 키워드로, 함수 앞에 async 키워드가 붙으면 무조건 Promise 객체를 반환한다.
- 반환 값이 Promise 가 아닐 때 -> Promise.resolve 로 감싼 객체 반환
- 반환 값이 Promise 일 때 -> Promise 객체 반환
아래의 코드를 보면 return 7 로 7을 반환하는 것 같지만, 내부에서 자동으로 Promise 객체를 만들어서 7을 감싸서 Promise.resolve(7) 로 반환한다.
async function f() {
return 7; // Promise 객체 반환 중
}
f().then(
function(result) {
console.log("resolve: ", result);
},
function(err) {
console.log("reject: ", err);
}
)
async 는 Promise 객체를 만들어주는 역할도 하지만, await 과 함께 사용되었을 때는 Promise 객체가 일이 끝날 때까지 기다릴 수 있는 공간을 제공하는 역할도 한다.
await 은 async 안에서만 사용할 수 있으며, promise 객체가 일을 다 할 때까지 기다리도록 해준다.
아래의 코드는 위에 코드와 동일한 기능을 하는데, async + then 이렇게 사용했을 때보다 직관적이고 가독성이 좋다.
async function f() {
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("완료!"), 3000);
});
let result = await promise; // promise 객체가 일 다 할때까지 기다려줌
console.log(result);
}
f();
3개의 promise 순서대로 실행시키기
async function f() {
let promise1 = new Promise(function(resolve, reject) {
setTimeout(() => resolve("첫 번째 쿼리"), 3000);
});
let result1 = await promise1;
console.log(result1);
let promise2 = new Promise(function(resolve, reject) {
setTimeout(() => resolve("두 번째 쿼리 with " + result1), 3000);
});
let result2 = await promise2;
console.log(result2);
let promise3 = new Promise(function(resolve, reject) {
setTimeout(() => resolve("세 번째 쿼리 with " + result2), 3000);
});
let result3 = await promise3;
console.log(result3);
}
f();
2. query 문 순서대로 실행시키기
주문 API 의 "주문하기" 기능을 구현하다가 문제가 생겼다.
리퀘스트 바디에 담겨온 정보 중 배송 정보인 delivery 와 구매하는 책 정보인 items 배열은 각각 delivery 테이블과 orderedBook 테이블에 저장해서 관리하는데, 참조 관계 때문에 INSERT 순서가 존재한다.
먼저 delivery 정보를 delivery 테이블에 INSERT 하고 delivery_id 값을 들고 나오고, 들고 나온 delivery_id 값은 주문 정보를 orders 테이블에 INSERT 할 때 사용된다. orders 테이블에 INSERT 하면서 order_id 를 들고 나오고 들고 나온 order_id 값은 주문 도서 정보를 orderedBook 테이블에 INSERT 할 때 사용된다.
테스트를 해보니 첫번째 쿼리문이 실행되고 나서 delivery_id 값이 undefined 로 찍히는 이상한 문제가 생겼는데,
query 문이 비동기로 처리되기 때문에 쿼리문 실행이 끝나서 delivery_id 값에 올바른 값이 들어오기 전에 다음 쿼리문이 실행되어서 발생하는 문제였다. (쿼리문이 순서대로 실행되지 않아서 발생하는 문제) 그렇다면 문제를 고쳐보자. (다음 편에..)
자세한 내용 참고 ▼
[TIL] 10/11 주문 기능 구현
1. 주문 기능 구현 1.1. 결제(주문) API - 결제하기 = 주문하기 = 주문등록 = 데이터베이스 주문 INSERT items -> 결제할 도서가 담겨있는 배열delivery -> 입력받은 주소/이름/연락처 firstBookTitle -> 주문 목
everydayc0ding.tistory.com
const order = (req, res) => {
const {items, delivery, totalQuantity, totalPrice, userId, firstBookTitle} = req.body;
let delivery_id = '';
let order_id = '';
let sql = `INSERT INTO delivery (address, receiver, contact) VALUES (?, ?, ?)`;
let values = [delivery.address, delivery.receiver, delivery.contact];
conn.query(sql, values,
(err, results) => {
if (err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
delivery_id = results.insertId;
}
)
sql = `INSERT INTO orders (book_title, total_quantity, total_price, user_id, delivery_id)
VALUES (?, ?, ?, ?, ?)`
values = [firstBookTitle, totalQuantity, totalPrice, userId, delivery_id]
conn.query(sql, values,
(err, results) => {
if (err) {
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
order_id = results.insertId;
}
)
sql = `INSERT INTO orderedBook (order_id, book_id, quantity) VALUES ?` // 벌크로 insert 를 한다
values = [];
items.forEach((item) => {
values.push([order_id, item.book_id, item.quantity]);
})
conn.query(sql, [values],
(err, results) => {
if (err) {
console.log(results);
console.log(err);
return res.status(StatusCodes.BAD_REQUEST).end();
}
return res.status(StatusCodes.OK).json(results);
}
)
};
'TIL with Programmers' 카테고리의 다른 글
[TIL] 10/16 JWT - TokenExpiredError, JsonWebTokenError, authorization, ERR_HTTP_HEADERS_SENT (0) | 2024.10.16 |
---|---|
[TIL] 10/15 MySQL 데이터 삭제-DELETE/DROP/TRUNCATE, 주문하기 API (0) | 2024.10.15 |
[회고록] 풀 사이클 개발 데브코스 8주차 회고 (5) | 2024.10.12 |
[TIL] 10/11 주문 기능 구현 (2) | 2024.10.11 |
[TIL] 10/10 장바구니 기능 구현, SQL 에러 (1) | 2024.10.10 |