HTTP를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처
REST를 기반으로 서비스 API를 구현한 것
REST의 기본원칙을 성실히 지킨 서비스 디자인을 RESTful
이라고 표현한다.
자바스크립트 패키지 매니저, Node.js에서 사용할 수 있는 모듈들을 패키지화 하여 모아둔 저장소 역할과 패키지 설치 및 관리를 위한 CLI를 제공한다.
JS가 모듈 기능이 없었기 때문에 이런 상황에서 제안된 것이 CommonJS와 AMD이다.
CommonJS 방식은 AMD에 비해 문법이 간단하며 동기 방식으로 동작한다.
AMD 방식은 CommonJS 방식에 비해 문법이 다소 까다로우며 비동기 방식으로 동작한다.
Node.js는 사실상 모듈 시스템의 표준인 CommonJS 방식을 채택하였다.
브라우저에서 모듈 사용은 대부분의 브라우저가 ES6 모듈을 지원하지 않고 있으므로 Browserify 또는 webpack과 같은 모듈 번들러를 사용해야한다.
함수 내부에 비동기로 동작하는 코드를 포함한 함수
비동기 함수를 호출하면 함수 내부의 비동기로 동작하는 코드가 완료되지 않았다 해도 기다리지 않고 즉시 종료된다. 즉, 비동기 함수 내부의 비동기로 동작하는 코드는 비동기 함수가 종료된 이후에 완료된다.
따라서 비동기 함수 내부의 비동기로 동작하는 코드에서 처리 결과를 외부에 반환하거나 상위 스코프의 변수에 할당하면 기대한 대로 동작하지 않는다.
ex) setTimeout 함수는 콜백함수의 호출이 비동기로 동작하기 때문에 비동기 함수 이다. 비동기 함수인 setTimeout 함수의 콜백함수는 setTimeout 함수가 종료된 이후에 호출된다. 따라서 setTimeout 함수 내부의 콜백 함수에서 처리된 결과를 외부로 반환하거나 상위 스코프의 변수에 할당하면 기대한 대로 동작하지 않는다.
이벤트 핸들러는 비동기로 동작한다.
이벤트 핸들러를 호출하는 것은 개발자가 아니기 때문에 반환문은 의미가 없다.
(브라우저가 이벤트 핸들러를 호출함)
비동기 함수는 비동기 처리 결과를 외부에 반환할 수 없고, 상위 스코프의 변수에 할당할 수도 없다. 따라서 비동기 함수의 처리 결과(서버의 응답 등)에 대한 후속 처리는 비동기 함수 내부에서 수행해야 한다.
이때 비동기 함수에 비동기 처리 결과에 대한 후속처리를 수행하는 콜백함수를 전달하는 것이 일반적이다.
콜백 함수를 통해 비동기 처리 결과에 대한 후속 처리를 수행하는 비동기 함수가 비동기 처리 결과를 가지고 또 다시 비동기 함수를 호출한다면 콜백 함수가 중첩되어 복잡도가 높아지는 현상
이 발생하는 것.
콜백 헬은 가독성을 나쁘게 하며 실수를 유발하는 원인이 된다.
ex) 에러는 호출자 방향(콜 스택의 아래 방향)으로 전파된다. setTimeout의 경우, setTimeout 함수의 콜백함수를 호출한 것은 setTimeout 함수가 아니다. 따라서 setTimeout 함수의 콜백함수가 발생시킨 에러는 catch 블록에서 캐치되지 않는다.
비동기 처리를 위한 콜백 패턴은 콜백 헬이나 에러 처리가 곤란하다는 문제가 있다. 이를 극복하기 위해 ES6에서 프로미스가 도입되었다.
프로미스는 호스트 객체가 아닌 ECMAScript 사양에 정의된 표준 빌트인 객체이다.
프로미스는 현재 비동기 처리가 어떻게 진행되고 있는지를 나타내는 상태 정보를 갖는다.
⇒ settled 상태: fulfilled 또는 rejected 상태(비동기 처리가 수행된 상태)
⇒ 프로미스는 비동기 처리 상태
와 처리 결과
를 관리하는 객체이다.
프로미스는 후속 처리 메서드를 통해 콜백 헬을 해결한다.
⇒ 프로미스 체이닝을 통해 해결
⇒ 모든 후속 처리 메서드는 프로미스를 반환
⇒ 프로미스도 콜백 패턴을 사용하므로 콜백 함수를 사용하지 않는 것은 아니다.
⇒ 콜백 패턴은 가독성이 좋지 않다. 이 문제는 ES8에서 도입된 async/await을 사용하면 프로미스의 후속 처리 메서드 없이 마치 동기 처리처럼 프로미스 처리 결과를 반환하도록 구현할 수 있다.
여러 개의 비동기 처리를 모두 병렬 처리할 때 사용한다.
모두 fulfilled 상태가 되면 모든 처리 결과를 배열에 저장해 새로운 프로미스를 반환
프로미스의 후속 처리 메서드의 콜백함수
는 테스크 큐가 아니라 마이크로테스크 큐에 저장된다.
마이크로테스크 큐는 테스크 큐보다 우선순위가 높다.
즉, 이벤트 루프는 콜 스택이 비면 먼저 마이크로테스크 큐에서 대기하고 있는 함수를 가져와 실행한다.
XMLHttpRequest 객체와 마찬가지로 HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API이다.
프로미스를 지원하기 때문에 비동기 처리를 위한 콜백 패턴의 단점에서 자유롭다.
fetch 함수는 HTTP 응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환한다.
fetch 함수가 반환하는 프로미스는 기본적으로 404 Not Found나 500 Internal Server Error와 같은 HTTP 에러가 발생해도 에러를 reject 하지 않고, 불리언 타입의 ok 상태를 false로 설정한 Response 객체를 resolve한다.
오프라인 등의 네트워크 장애나 CORS 에러에 의해 요청이 완료되지 못한 경우에만 프로미스를 reject한다.
const wrongUrl = 'https://jsonplaceholder.typicode.com/XXX/1';
fetch(wrongUrl)
.then(response => {
if (!response.ok) throw new Error(response.statusText);
return response.json();
})
.then(todo => console.log(todo))
.catch(err => console.error(err));
⇒ axios는 HTTP 에러를 reject하는 프로미스를 반환한다. 또한 axios는 인터셉터, 요청 설정 등 fetch 보다 다양한 기능을 제공한다.
코드 블록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수한 함수이다.
⇒ 제너레이터 함수는 next 메서드와 yield 표현식을 통해 함수 호출자와 함수의 상태를 주고받을 수 있다.
이러한 특성을 활용하면 프로미스를 사용한 비동기 처리를 동기 처리처럼 구현
할 수 있다.
즉, 프로미스 후속 처리 메서드 then, catch, finally 없이 비동기 처리 결과를 반환하도록 구현할 수 있다.
ES8에서는 제너레이터 보다 간단하고 가독성 좋게 비동기 처리를 동기처리처럼 동작하도록 구현
할 수 있는 async/await이 도입되었다.
async/await은 프로미스를 기반으로 동작
한다.
프로미스의 후속처리 메서드 없이 마치 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다.
async 함수는 언제나 프로미스를 반환한다.
await은 프로미스가 settled 상태가 될 때까지 대기하다가 settled 상태가 되면 프로미스가 resolve한 처리결과를 반환한다.
async/await에서 에러 처리는 try...catch 문을 사용할 수 있다.
⇒ 프로미스를 반환하는 비동기 함수는 명시적으로 호출할 수 있기 때문에 호출자가 명확하다. (그래서 try...catch 문을 쓸 수 있다.)
에러에 대해 대처하지 않고 방치하면 프로그램은 강제 종료된다.
try...catch문을 사용해 대처하면 프로그램이 강제 종료되지 않고 계속해서 코드를 실행할 수 있다.
에러는 호출자 방향으로 전파된다.
즉, 콜스택의 아래방향으로 전파된다.
비동기 함수인 setTimeout이나 프로미스 후속 처리 메서드의 콜백 함수는 호출자가 없다. 콜스택이 비면 이벤트 루프에 의해 콜스택으로 푸시되어 실행되기 때문에 콜백함수의 실행 컨텍스트는 가장 하부에 존재하게 된다.
⇒ 따라서 에러를 전파할 호출자가 존재하지 않는다.