[JavaScript] 기다려! 먹어! async/await 이해하기
여의도한량이
1
14697
0
0
2021.04.26 17:21
우리는 뭉치에게 기다려! 먹어! 훈련을 교육시키고 싶습니다.
뭉치의 다리에 간식을 올려놓고 먹어!라고 했을 경우 뭉치가 간식을 먹었으면 하는데 뭉치는 주인의 명령을 기다리지 않고(비동기 방식 문제) 제멋대로 간식을 먹어버렸습니다...뭉치에게 훈련(일반 콜백함수)을 시켜보았지만 여전히 뭉치는 말을 듣지 않습니다. 뭉치는 말못하는 강아지라 혼낼 수도 없고 우리는 난처한 상황에 빠져버렸습니다ㅠㅠ 그래서 우리는 더 쉬운 훈련방법(async/await)으로 뭉치를 교육시키고자 합니다!!
# 비동기와 동기
자바스크립트를 사용할 때 가장 신경써야 하는 부분 중 하나가 동기와 비동기 처리입니다. 분명 로직은 맞게 짠거 같은데 결과값이 undefined 이거나 알수 없는 에러가 발생할 경우 십중팔구 비동기 문제를 의심해봐야 합니다.
아래 코드는 Node.js 중 데이터베이스에 접근해 쿼리문을 조회하는 코드입니다.
router.get('/', function (req, res, next) {
let query_result = "init"
connection.connect();
connection.query('SELECT * FROM bjgg.champions LIMIT 1', function(err, result, fields){
query_result = result;
});
connection.end();
res.json(query_result);
});
**간단한 코드 설명
connection.query는 콜백 함수로 쿼리문의 결과 값을 받는 함수입니다. 콜백함수 result에 결과값이 담깁니다.
res.json(query_result)는 웹 브라우저에 json 값을 보여줍니다.
connection.connect()와 .end()는 신경쓰시지 않아도 됩니다.
과연 query_result 는 어떤 값을 출력할까요? "init" 아니면 "SELECT 쿼리 결과값"?
~
~
~
정답은 바로 "init" 입니다.
자바스크립트는 비동기 방식으로 동작하기 때문에 connection.query의 결과값을 받아 오기도 전에 이미 다음 함수인 res.json(query_result)로 넘어가게 되어 쿼리문의 결과값을 받을 수 없습니다.
router.get('/', function (req, res, next) {
let query_result = "init"
connection.connect();
connection.query('SELECT * FROM bjgg.champions LIMIT 1', function(err, result, fields){
query_result = result;
connection.end();
res.json(query_result);
});
});
많은 분들이 위와 같이 코드를 작성하면 쉽게 해결되지 않을까? 라고 생각하실 수도 있습니다. 맞습니다. 위와 같이 해도 문제없이 잘돌아갑니다.
결과 값을 잘 가져온 것을 보실 수 있습니다. 하지만 "SELECT 쿼리 결과값"을 다른 콜백함수에서 사용해야 한다면 어떨까요?
router.get('/', function (req, res, next) {
let query_result = "init"
connection.connect();
connection.query('SELECT * FROM bjgg.champions LIMIT 1', function(err, result, fields){
query_result = result;
myFunc_01(query_result, function(result_01){
myFunc_02(result_01, function(result_02){
myFunc_03(result_02, function(result_03){
//콜백 지옥...console.log(result_03);
});
});
})
connection.end();
res.json(query_result);
});
});
query_result를 이용하여 3개의 함수(myFunc_01, 02, 03)를 거친 뒤 result_03을 구한다고 한다면,
위와 같이 콜백 함수를 구성하여야 하는데 보이는 것처럼 가독성이 상당히 떨어집니다. 예시 코드에는 함수안에 다른 로직들이 없지만, 만약 함수 안에 다른 로직들까지 있으면 파악하기가 상당히 힘듭니다. 이를 소위 말해 콜백 지옥에 빠졌다고 말합니다.
# async/await
콜백지옥을 해결하고자 나온 것이 바로 async/await 입니다. async/await가 나오기 앞서 promise라는 것이 먼저 나왔는데 promise에 대해선 다루지 않겠습니다.
(개발자의 욕심은 끝이 없다고 promise 또한 가독성이 좋지 않다 생각해 이를 더욱 개선한 것이 async/await입니다.)
async/await 문법의 자바스크립트 스펙은 ES2017입니다. 따라서 최신 브라우저가 아니라면 해당 스크립트는 동작하지 않을 수도 있습니다.
router.get('/',asyncfunction (req, res, next) {
let query_result = "init"
query_result = await connection('SELECT * FROM bjgg.champions LIMIT 1');
res.json(query_result);
});
위 코드는 async/await 문법을 사용한 코드입니다. 얼핏봐도 가독성이 좋아졌다는 걸 알 수 있습니다.
async/await 문법 사용법은 await를 쓰고싶은 함수 앞에 async를 써주고, 비동기를 동기로 처리하고 싶은 함수 앞에 await를 붙여주기만 하면 됩니다.
결과값을 잘 가져온 걸 볼 수 있습니다.
router.get('/',asyncfunction (req, res, next) {
let query_result = "init"
query_result = await connection('SELECT * FROM bjgg.champions LIMIT 1');
result_01 = await myFunc_01(query_result);
result_02 = await myFunc_02(result_01);
result_03 = await myFunc_03(result_02);
res.json(query_result);
});
콜백지옥에 빠졌던 코드들도 위와 같이 가독성 좋게 바꿀 수 있습니다. 훨씬 보기가 좋습니다.
그리고 try~catch 문을 사용할 수 있기 때문에 예외처리 또한 쉽게 구현할 수 있습니다.
router.get('/',asyncfunction (req, res, next) {
try{
let query_result = "init"
query_result = await connection('SELECT * FROM bjgg.champions LIMIT 1');
result_01 = await myFunc_01(query_result);
result_02 = await myFunc_02(result_01);
result_03 = await myFunc_03(result_02);
res.json(query_result);
}catch(error){
console.log(error);
}
});
# 기특한 뭉치
우리는 이제 새로운 훈련방법(async/await)을 알았기 때문에 기존에 했던 훈련방법(일반 콜백함수)보다 더욱 쉽게 뭉치를 교육시킬 수 있습니다. 뭉치는 더 이상 간식을 제멋대로 먹지 않습니다. 뭉치에게 기다려!(await)라고 말한 후 먹어!(await의 결과값을 받았을 때)라고 외칠때 만 뭉치는 간식을 먹습니다.
읽어주셔서 감사합니다.