모던 자바스크립트 DeepDive 공부 내용 16차
이번 정리 목차는 44 ~ 47장이다.
44장 REST API
- REST는 HTTP를 기반으로 클라이언트가 서버의 리소스에 접근하는 방식을 규정한 아키텍처
- REST API는 REST를 기반으로 서비스 API 구현한 것 의미
REST API 구성
- 자원, 행위, 표현 3가지 요소로 구성, 자체 표현 구조로 구성되어 REST API만으로 HTTP 요청의 내용 이해 가능
| 구성 요소 | 내용 | 표현 방법 |
| 자원 | 자원 | URI(엔드포인트) |
| 행위 | 자원에 대한 행위 | HTTP 요청 메서드 |
| 표현 | 자원에 대한 행위의 구체적 내용 | 페이로드 |
REST API 설계 원칙
- URI는 리소스를 표현하는데 집중해야 한다.
- 리소스에 대한 행위는 HTTP 요청 메서드로 표현한다.
45장 프로미스
- 비동기 처리를 위해 콜백함수를 사용하는데 이 콜백 패턴은 콜백 헬로 인해 가독성 나쁘고 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한 번에 처리하는데 한계가 있다.
- 비동기 처리를 위한 또 다른 패턴으로 프로미스 도입, 콜백 패턴이 가진 단점을 보완하며 비동기 처리 시점을 명확하게 표현할 수 있는 장점이 있다.
비동기 처리를 위한 콜백 패턴의 단점
[콜백 헬]
- 비동기 함수를 호출하면 함수 내부의 비동기로 동작하는 코드가 완료되지 않아도 즉시 종료된다. 비동기로 동작하는 코드는 비동기 함수가 종료된 이후에 완료된다. 따라서 비동기 처리 결과를 외부에 반환할 수 없고, 상위 스코프의 변수에 할당할 수도 없다.
- 비동기 함수의 처리 결과에 대한 후속처리는 비동기 함수 내부에서 수행해야 하고 이 방법으로 콜백 함수를 전달하는 것이 일반 적인데 콜백 함수 중첩으로 인하여 복잡도가 높아지는 현상이 콜백 헬이다.
[에러 처리의 한계]
- 콜백 패턴의 가장 심각한 문제는 에러 처리가 곤란이다.
- 콜백 함수를 호출한 것은 비동기 함수가 아니기 때문에 에러를 캐치할 수 없다.
- 에러는 호출자 방향으로 전파된다.
- 이를 극복하고자 ES6 프로미스가 도입
프로미스의 생성
- new 연산자와 함께 Promise생성자 함수를 호출하면 Promise객체 생성
- 생성자 함수는 비동기 처리를 수행할 콜백함수를 인수로 받는다. (resolve, reject)
- 비동기 처리 성공하면 resolve 함수 호출, 실패하면 reject함수 호출
- 프로미스는 현재 비동기 처리가 어떻게 진행되고 있는지 상태 정보를 갖는다.
| 프로미스 상태 정보 | 의미 | 상태 변경 조건 |
| pending | 비동기 처리 아직 수행 X | 프로미스 생성된 직후 상태 |
| fulfilled | 비동기 처리 수행 성공 | resolve 함수 호출 |
| rejected | 비동기 처리 수행 실패 | reject 함수 호출 |
- 프로미스의 상태는 resolve 또는 reject 함수 호출하는 것으로 결정
- fulfilled 또는 rejected 상태를 settled 상태라고 한다.
- 프로미스는 비동기 처리 상태와 더불어 비동기 처리 결과도 상태로 갖는다.
- 비동기 처리 성공 시 처리 결과값, 실패 시 처리 결과값은 에러 객체를 갖는다.
- 즉, 프로미스는 비동기 처리 상태와 처리 결과를 관리하는 객체이다.
프로미스의 후속 처리 메서드
- 비동기 처리 상태 변화에 따라 후속 처리를 해야 하는데 프로미스는 후속처리 메서드를 제공한다(then, catch, finally 메서드)
- 비동기 처리 상태가 변화하면 후속 처리 메서드에 인수로 전달한 콜백함수가 선택적으로 호출된다. 이때 후속 처리 메서드의 콜백 함수에 프로미스의 처리 결과가 인수로 전달
- 모든 후속 처리 메서드는 프로미스를 반환하고 비동기로 동작
[Promise.prototype.then]
- 두 개의 콜백 함수를 인수로 전달받는다.
- 첫번째 콜백 함수는 fulfilled 상태일때 호출, 두번째 콜백 함수는 rejected 상태일때 호출
- 프로미스가 아닌 값을 반환하면 그 값을 암묵적으로 resolve, reject하여 프로미스 생성해 반환
[Promise.prototype.catch]
- 한 개의 콜백 함수를 인수로 전달받는다.
- reject 상태일 때만 콜백 함수 호출
[Promise.prototype.finally]
- 한 개의 콜백 함수를 인수로 전달받는다.
- fulfilled 또는 rejected 상관없이 무조건 한 번 호출
- 공통적 수행할 처리 내용 있을 시 유용
프로미스의 에러 처리
- 비동기 처리에서 발생한 에러는 후속처리 메서드 then과 catch로 처리 가능
- then 메서드에 두번째 콜백 함수 전달 보다 catch메서드 사용하는 것이 가독성 좋고 명확하므로 catch메서드 권장
프로미스 체이닝
- 후속처리 메서드는 언제나 프로미스를 반환하므로 연속적 호출이 가능, 이것이 프로미스 체이닝
- 프로미스 체이닝을 통해 비동기 처리 결과를 전달 받아 후속 처리를 하므로 콜백 헬이 발생하지는 않지만 프로미스도 콜백 패턴을 사용하므로 콜백 함수를 사용하지 않는 것은 아니다.
- ES8에 도입된 async/await통해 후속 처리 메서드 없이 마치 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현 가능(콜백 패턴 해결)
프로미스의 정적 메서드
[Promise.resolve / Promise.reject]
- 인수로 전달 받은 값을 resolve / reject 하는 프로미스 생성
[Promise.all]
- 여러 개의 비동기 처리를 모두 병렬 처리할 때 사용
- 프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달받는다.
- 전달받은 모든 프로미스가 모두 fulfilled상태가 되면 모든 처리 결과를 배열에 저장해 새로운 프로미스 반환하고 처리 순서 보장
- 전달받은 프로미스 중 하나라도 rejected상태가 되면 즉시 종료 반환
- 인수로 전달받은 이터러블 요소가 프로미스가 아니면 resolve메서드로 프로미스 래핑
[Promise.race]
- 프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달받는다.
- 가장 먼저 fulfilled상태가 된 프로미스의 처리 결과를 resolve하는 새로운 프로미스 반환
- 하나라도 rejected상태가 되면 즉시 종료 반환
[Promise.allSettled]
- 프로미스를 요소로 갖는 배열 등의 이터러블을 인수로 전달받는다.
- 전달 받은 프로미스가 모두 settled상태가 되면 처리 결과를 배열로 반환
- 프로미스가 fulfilled상태이면 status, value 프로퍼티 갖음
- 프로미스가 rejected상태이면 status, reason 프로퍼티 갖음
마이크로태스크 큐
- 프로미스의 후속 처리 메서드의 콜백 함수가 임시 저장되는 곳
- 태스크 큐보다 우선순위가 높다.
fetch
- XMLHttpRequest객체와 마찬가지로 HTTP 요청 전송 기능을 제공하는 Web API이다.
- XMLHttpRequest객체보다 사용법 간단하고 프로미스를 지원하기 때문에 비동기 처리를 위한 콜백 패턴의 단점에서 자유롭다.
- fetch함수에는 HTTP요청을 전송할 URL과 HTTP요청 메서드, HTTP요청 헤더, 페이로드 등을 설정한 객체를 전달한다.
- fetch함수는 HTTP응답을 나타내는 Response객체를 래핑한 Promise객체를 반환한다.
- fetch함수가 반환하는 프로미스는 오프라인 등의 네트워크 장애나 CORS 에러에 의해 요청이 완료되지 못한 경우에만 프로미스를 reject 하기에 에러처리에 주의가 필요하다.
- axios는 모든 HTTP 에러를 reject하는 프로미스를 반환하므로 catch에서 처리 가능하고 fetch보다 다양한 기능 지원
46장 제너레이터와 async/await
제너레이터란?
- 코드 블록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수한 함수
- 제너레이터 함수는 함수 호출자에게 함수 실행의 제어권을 양도할 수 있다.
- 제너레이터 함수는 함수 호출자와 함수의 상태를 주고받을 수 있다.
- 제너레이터 함수를 호출하면 함수 코드를 실행하는 것이 아니라 이터러블이면서 동시에 이터레이터인 제너레이터 객체를 반환한다.
제너레이터 함수 정의
- function* 키워드로 선언하고 하나 이상의 yield 표현식을 포함한다.
- 화살표 함수로 정의 X, 생성자 함수로 호출 X
제너레이터 객체
- 제너레이터 함수를 호출하면 함수 코드를 실행하는 것이 아닌 이터러블이면서 동시에 이터레이터인 제너레이터 객체를 생성해 반환한다.
- next메서드 뿐만 아니라 return, throw 메서드를 갖는다.
- next메서드 호출하면 yield표현식까지 코드 블럭 실행하고 yield된 값을 value 프로퍼티 값으로 false를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체 반환
- return메서드 호출하면 인수로 전달받은 값을 value 프로퍼티 값으로 true를 done프로퍼티 값으로 갖는 이터레이터 리절트 객체 반환
- throw메서드 호출하면 인수로 전달받은 에러를 발생시키고 undefined를 value프로퍼티 값으로 true를 done프로퍼티 값으로 갖는 이터레이터 리절트 객체 반환
제너레이터의 일시 중지와 재게
- yield키워드와 next 메서드를 통해 실행을 일시 중지했다가 필요한 시점에 다시 재게 가능
- next메서드를 호출하면 yield 표현식 까지만 실행, yield키워드는 제너레이터 함수의 실행을 일시 중지시키거나 yield 키워드 뒤에 오는 표현식의 평가 결과를 제너레이터 함수 호출자에게 반환
- next메서드에 전달한 인수는 제너레이터 함수의 yield표현식을 할당 받는 변수에 할당된다.
제너레이터의 활용
- 이터러블의 구현, 비동기 처리
async/await
- 제너레이터보다 간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 async/await 도입
- 프로미스 기반으로 동작, 후속 처리 메서드 없이 마치 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현할 수 있다.
[async 함수]
- async 키워드를 사용해 정의하고 언제나 프로미스 반환
[await 키워드]
- 프로미스가 settled상태가 될 때까지 대기하다가 settled상태가 되면 프로미스가 resolve한 처리 결과를 반환한다. 반드시 await키워드는 프로미스 앞에서 사용해야 한다.
- await 키워드는 다음 실행을 일시 중지시켰다가 프로미스가 settled상태가 되면 다시 재개
[에러 처리]
- try … catch문으로 에러 처리 가능
- async 함수 내에서 catch문 사용해서 에러 처리하지 않으면 async 함수는 에러를 reject하는 프로미스를 반환하므로 프로미스 후속처리 메서드 사용해서 에러 캐치도 가능하다.
47장 에러 처리
에러처리의 필요성
- 에러나 예외적인 상황에 대응하지 않으면 프로그램이 강제 종료되므로 언제나 에러나 예외적인 상황이 발생할 수 있다는 것을 전제로 대응하는 코드를 작성하는 것이 중요하다.
try … catch … finally문
- 에러처리 구현 방법은 예외적 상황이 발생하면 반환하는 값(null 또는 -1)을 if문이나 단축평가 또는 옵셔널 체이닝 연산자로 확인해서 처리하는 방법과 에러 처리 코드를 미리 등록해 두고 에러가 발생하면 에러 처리 코드로 점프하도록 하는 방법이 있다.
- try … catch … finally문은 두번째 방법이다.
- try 코드 블록이 먼저 실행되고 이 과정에서 에러가 발생하면 catch문의 매개 변수에 전달되고 catch가 실행된다. finally 코드 블록은 에러 발생과 상관없이 반드시 단 한 번 실행된다.
- try … catch … finally문을 사용하면 강제 종료되지 않는다.
에러 객체
- Error 생성자 함수는 에러 객체를 생성
- 에러 객체는 message 프로퍼티와 stack 프로퍼티를 갖는다.
- message 프로퍼티 값은 Error 생성자 함수에 인수로 전달한 에러 메시지
- stack 프로퍼티 값은 에러를 발생시킨 콜 스택의 호출 정보를 나타내는 문자열
- JS는 7가지 에러 생성자 함수 제공(Error, SyntaxError, ReferenceError, TypeError, RangeError, URIError, EvalError)
throw 문
- 에러를 발생시키려면 try 코드 블록에서 throw문으로 에러 객체를 던져야 한다.
에러 전파
- 에러는 호출자 방향으로 전파된다. (콜 스택의 아래 방향)
- throw된 에러를 캐치하지 않으면 호출자 방향으로 전파된다. 어디에서도 캐치하지 않으면 프로그램은 강제 종료된다.
- 비동기 함수인 setTimeout이나 프로미스 후속처리 메서드의 콜백함수는 호출자가 없다. 콜 스택의 가장 하부에 존재하므로 에러를 전파할 호출자가 존재하지 않는다.