Node.js + Express 앱에서 웹페이지의 데이터를 긁어오는 getData 함수를 saveData 함수 내에서 실행하여, console.log()로 결과를 표시해 봤더니, 원하던 데이터는 나오지 않고 Promise { < pending > }
만 나옴.
아래는 해당 코드의 편집본. (깃허브에서 오리지널 코드 보기)
export const getData = async (req, res) => {
try {
await accessURL();
let finalResult = [];
finalResult = result;
result = [];
return finalResult;
} catch(e) {
console.log(e);
};
};
export const saveData = (req, res) => {
const finalResult = getData();
console.log(finalResult);
return res.end();
};
Node.js v14.19.1
npm 6.14.16
async
가 붙은 함수는, return 값이 Promise가 아니더라도 Promise
화 해준다.
getData 함수에는 async가 붙었으므로, return 값(finalResult)을 Promise화하여 반환한다.
따라서 getData 함수를 써먹을 땐 await
getData()가 되어야 하는 것이다.
getData 함수 실행 시 async/await
사용. 아래는 해당 코드.
(깃허브에서 오리지널 코드 보기)
export const saveData = (req, res) => {
let data = [];
(async () => {
data = await getData();
console.log(data);
})();
return res.end();
};
Promise
, async/await
개념을 되짚어 본 후에 1차 해결 코드를 다시 살펴보니, 개선할 부분이 눈에 띄었다.
saveData 함수 안에 불필요하게 들어가 있는 즉시실행함수
삭제하고, async
를 제일 바깥쪽(익명함수 앞)에 붙임. (깃허브에서 보기)
saveData 함수 안에 try/catch
에러 핸들링 추가
export const saveData = async (req, res) => {
let data = [];
try {
data = await getData();
(생략)
return res.render("home", { data: dataInDB });
} catch (e) {
console.log(e);
};
};
try/catch
에러 핸들링 추가. 이로써 프로젝트 내의 모든 await
은 try/catch 처리됨. (깃허브에서 보기)const accessURL = async () => {
try {
(생략)
const response = await client.get(`/page/${i}`);
(생략)
} catch (e) {
console.log(e);
};
};
위 이슈의 원인과 해결책을 찾다가 알게 된 지식에 대하여 정리해 보았다.
JavaScript 관련으로 비동기 처리, 함수 기본 개념, 호이스팅, 스코프, 클로저에 대해 조사하였고,
프로그램 실행 과정에 있어 일반적인 순서와 JavaScript에서의 순서를 알아보았다.
콜백 함수
콜백 지옥
Promise
사용 (더 좋음)async/await
사용 (더더 좋음)비동기 처리
에 쓰이는 객체이다. 주로 서버에서 데이터를 가져올 때 사용한다.데이터 가져오는 함수를 Promise 객체
로 감싸서,
성공 시 (Fullfilled
)
resolve()
실행되어, then(첫 번째 인자)
로 결과 값을 받을 수 있고, 실패 시 (Rejected
)
reject()
실행되어, then(두 번째 인자)
로 에러를 받을 수 있다.catch()
를 사용하는 것이 더 바람직하다.아직 처리 중일 시 (Pending
)
Promise 적용 전: Data를 받으면 callbackFunc()에 넘김
function getData(callbackFunc) {
$.get('url 주소/products/1', function(response) {
callbackFunc(response);
});
}
getData(function(tableData) {
console.log(tableData);
});
Promise 적용 후: Data를 받으면 resolve()
호출하여, then(첫 번째 인자)
로 결과값을 받는다."
function getData(callback) {
return new Promise(function(resolve, reject) {
$.get('url 주소/products/1', function(response) {
resolve(response);
});
});
}
getData().then(function(tableData) {
console.log(tableData);
});
기본(간략)
비동기 처리
패턴 중 제일 최신 방법이다. Promise
를 기반으로 한다.async/await
를 쓰면 이를 그대로 유지할 수 있다.구조(일반)
바깥 함수에 async
붙이고, 안쪽의 비동기 처리 함수에 await
붙이고,
Promise
를 반환하고, Promise가 아닌 것은 Resolved Promise
로 감싸 반환"성공(이행) 시
result
값을 반환.실패(거부) 시
예외
가 생성됨(에러가 발생한 장소에서 throw error를 호출한 것과 동일함)try/catch
로 처리.catch
를 붙여 처리할 수도 있다.async function f() {
let response = await fetch('http://유효하지-않은-url');
}
f().catch(alert); // TypeError: failed to fetch
구조(예외)
1) 최상위 레벨 코드에도 await
사용하려면 ?
익명 async 함수
로 코드 감싸기
커밋 9ab4ae4에선 함수 안이었는데도 '익명 async 함수'를 썼었다. (잘못됨)
익명 async 즉시 실행 함수
라는 표현이 더 정확하지 않을까? (확인 필요)예시
(async () => {
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
(생략)
})();
2) 최상위 레벨 코드의 에러 핸들링을 하려면 ?
async 함수 바깥의 최상위 레벨 코드에선 await
을 사용할 수 없다.
때문에 .then/catch
를 추가해 최종 결과나 처리되지 못한 에러를 다루는 것이 관행처럼 되어 있다.
예시
정의
구성
크게 두 부분으로 구성된다.
첫 번째는 괄호((), Grouping Operator)로 둘러싸인 익명함수(Anonymous Function)
이다. 이는 전역 스코프에 불필요한 변수를 추가해서 오염시키는 것을 방지할 수 있을 뿐 아니라 IIFE 내부안으로 다른 변수들이 접근하는 것을 막을 수 있는 방법이다.
함수를 괄호로 감싸면 자바스크립트가 함수를 함수 선언문이 아닌 함수 표현식
으로 인식하도록 속일 수 있다.
두 번째 부분은 맨 끝의 괄호()
이다. 즉시 실행 함수를 생성한다. 이를 통해 자바스크립트 엔진은 함수를 즉시 해석해서 실행한다.
예시
(function () {
var aName = "Barry";
})();
주의
괄호로 둘러싸인 익명함수
인 거지, 익명함수
그 자체는 아니다. (확인 필요)용도
var
도 블록 스코프
갖게 함.정의
함수 표현식
만 익명일 수 있다. (확인 필요)예시
var square = function(number) { return number * number };
var x = square(4)
함수 ?
함수 만드는 법 ?
함수 선언
독립된 구문
형태로 존재.함수 선언
으로 생성된 함수는 Function 객체
로, Function 객체의 모든 속성(property), 메서드 및 행위 특성(behavior)을 갖는다.호이스팅
) function square(number) {
return number * number;
}
함수 표현식
함수가 표현식의 일부
로 존재.
함수 표현식은 실행 흐름이 표현식에 다다랐을 때 만들어진다.
함수 표현식
과 function 문(함수 선언)
사이의 주요 차이점은 함수 이름
으로, 함수 표현식으로 익명 함수
를 만들 경우 이름을 생략할 수 있다.
var square = function(number) { return number * number };
var x = square(4);
함수 표현식은 함수 선언문을 사용하는 게 부적절할 때에 사용하는 것이 좋다.
함수 표현식
에서 함수의 이름
을 지정하면, 함수 내에서 자신을 참조
하는 데 사용되거나, 디버거 내 스택 추적
에서 함수를 식별할 수 있다.
여러 함수 형식
일반 함수(이름 있는 함수)
익명 함수
즉시 실행 함수
화살표 함수
기타
메모리
공간을 선언 전에 미리 할당하는 것.선언
을 나중에 해도 정상 실행.변수
/함수
모두에 적용.let
, const
도 호이스팅이 되나, 참조 에러가 발생한다.var
함수 스코프
런타임
이전에 선언
/초기화
진행실행컨텍스트
에 등록 (추후 보충)let
블록 스코프
선언
, 런타임 이후에 초기화
진행 (확인 필요)const
블록 스코프
선언
, 런타임 이후에 초기화
진행 (확인 필요) '런타임 이전에 선언/초기화' vs. '런타임 이전에 선언, 이후에 초기화' 비교
정의
사용처
private
화. (공개할 스코프
변경) (재확인 필요)일반적인 프로그래밍 언어의 경우
주요 흐름
코드를
컴파일
하거나 인터프리팅
하여
기계어
로 바꿔주고, 인터프리팅은 Bytecode
로 바꿔주는 것.컴파일 에러
실행 (런타임)
런타임
?런타임 에러
요약
JavaScript V8 엔진
의 경우 (JIT Compilation
)
주요 흐름
Overview
코드를
Bytecode(중간 코드)
로 바꾸고
인터프리터(Ignition)
실행하고, (런타임
)
자주 사용되는 코드를 기계어
로 바꿈 (컴파일
, 최적화
)
컴파일러(TurboFan)
요약
interpreter 언어
다. 다만 JavaScript 엔진 내부에서 실행 중 컴파일이 필요한 경우에 내부에서 컴파일
한다. async
가 붙어 있었기 때문이었다. 때문에 해당 함수를 쓸 때 await
을 붙여줄 필요가 있다.JavaScript
async
붙은 함수는 늘 Promise
를 리턴한다.콜백 함수 중첩 선언
, Promise
, async/await
이 있고, 후자일수록 사용이 더 간편하다.함수 선언
혹은 함수 표현식
으로 만든다. 함수 선언으로 만드는 경우에만 호이스팅
이 된다.익명 함수
와 즉시 실행 함수
는 다른 것이다.호이스팅
이란 정확히는 변수와 함수의 메모리 공간을 미리 할당하는 것이다.함수 스코프
, let과 const는 블록 스코프
이다.클로저
로 함수 레벨 스코프
로 인한 문제를 해결할 수 있다.프로그램 실행 과정
컴파일
후 런타임
이나, JavaScript는 런타임
후 컴파일
이다.JavaScript 콜백 지옥
자바스크립트 비동기 처리와 콜백 함수
JavaScript 콜백 지옥의 해결책: Promise, async/await
자바스크립트 Promise 쉽게 이해하기
자바스크립트 async와 await
모던 자바스크립트 > async와 await
JavaScript 함수 기본
MDN > 개발자를 위한 웹 기술 > 함수
MDN > 용어 사전 > 함수
JavaScript 함수 선언, 함수 표현식
MDN > 함수 선언
MDN > 함수 표현식
모던 자바스크립트 > 함수 표현
JavaScript 즉시 실행 함수
MDN > IIFE
모던 자바스크립트 > 즉시 실행 함수 표현식
JavaScript 익명함수
익명 함수(Anonymous Function)에 대하여
JavaScript 호이스팅
MDN > 호이스팅
호이스팅(Hoisting) 이란?
JavaScript의 let과 const, 그리고 TDZ
JavaScript 변수 선언법별 스코프
모던 자바스크립트 - 10. var, let, const
자바스크립트를 배워보자 3일차 - 변수
let, const와 블록 레벨 스코프
일반적인 프로그램의 실행 과정
Web 12. 런타임(Runtime)과 컴파일타임(Compile time)
위키백과 > 컴파일러
위키백과 > 런타임
JavaScript V8엔진의 작동 원리 (JIT Compilation)
위키백과 > JIT 컴파일
JavaScript, 인터프리터 언어일까?
컴파일러와 인터프리터
JavaScript Engine에 대해서(3)- JIT 컴파일러는 무슨 일을 할까?
Javascript 기초 - JavaScript 개발한다면 JIT은 알아야JIT
MDN > 자바스크립트가 뭔가요? > 인터프리터와 컴파일러
2탄_브라우저의 자바스크립트 엔진은 js를 어떻게 해석하고 실행하는가 ?
자바스크립트 엔진과 런타임
자바스크립트 엔진, 런타임
V8 엔진은 어떻게 내 코드를 실행하는 걸까?
[Javascript] 자바스크립트 엔진 (Javascript Engine - V8)
V8 작동원리
JavaScript V8 엔진 Overview 이미지
JavaScript, 인터프리터 언어일까?
JavaScript 클로저
자바스크립트의 스코프와 클로저
MDN > 클로저
클로저
JavaScript 클로저 사용 이유
클로저의 의미와 사용하는 이유
Javascript 클로저 (Closure) 란? 사용 이유는?
JavaScript Closure는 왜 쓸까?