[JavaScript] Promise chaining과 에러핸들링

지구·2022년 2월 16일
0
post-thumbnail

그전에 알아둘 것
웹 페이지에서 서버가 보내는 요청 : request
서버가 보내주는 응답 : response라고 한다.

fetch함수

fetch함수는 서버에 request를 보내고 resposne를 받는 함수이다.

fetch('https://jsonplaceholder.typicode.com/users')
	.then((response) => response.text())

→ 이걸 받아서 처리하는 코드가 필요하겠죠?

then부분이 그러한 코드. response를 받으면 .then메소드 안에있는 콜백함수가 실행된다. 이 때 이 response는 하나의 객체로 넘어오는데, 파라미터 이름은 아무거나 써도되지만 걍 response라고 쓰는 것.(res라 쓰기도)

then이라는 method는 콜백을 등록해주는 메소드임.

resposne가 서버로부터 오면 then으로 등록한 콜백이 실행되고, 실행되고 나면, 그 후에 등록한 then메소드의 콜백이 계속해서 실행됨.

이전 콜백의 리턴값은 다음 콜백으로 넘어감.

fetch함수는 비동기적으로 실행되고, promise객체를 리턴한다. 위에서의 then메소드는 fetch함수가 리턴하는 promise객체의 메소드인 것.

그렇다면 promise객체란 무엇일까?

프로미스란 뭘까.

: 어떤 작업에 대한 상태정보를 갖고있는 객체.

fetch함수는 서버에 request를보내고, response를 받는다.

⇒ 성공할수도, 실패할 수도 있음

⇒ 이러한 성공/실패 결과는 fetch함수가 리턴하는 프로미스 객체에 저장된다.

그래서 프로미스 객체를 보면 fetch함수의 결과로 성공했는지 실패했는지 알 수 있다.

: 즉, 어떤 작업에 대한 성공실패상태 , 작업의 결과를 저장하는 객체라고 이해해보장.

promise는 세 가지 상태(state)를 갖는 객체다.

  1. 실행중 : pending
  2. 성공: fulfilled
  3. 실패: rejected

1→2 또는 1→3.

fetch함수에서의 작업이 성공적으로 이루어져 promise가 fulfilled상태(성공)가 된다면, promise객체는 그 작업의 성공결과도 함께 가지게된다.

= 작업성공결과.

promise객체가 rejected상태가 되면, 작업실패 이유에 대한 결과를 갖는다

= 작업실패정보

fetch('url')
.then(res  (), )

fetch함수와 함께 사용하는 콜백을 등록하는 .then메소드는 사실 Promise객체의 메소드이다!

pending → fulfilled상태가 될 때 실행할 콜백을 등록하는 메소드.(바로 실행하는게 아님)

언제 실행 되냐? fetch함수가 request보내고, response를 정상적으로 받았을때. 그때 이 콜백이 실행된다.

Promise 객체의 작업성공결과(response)는, 첫번째 콜백의 파라미터로 넘어오게 된다!!

fetch함수의 경우, 작업성공결과는 서버가 보낸 response.

Promise Chaining이란?

프로미스 객체들을 계속해서 연결해 나간다는 뜻. 이어나가기, 연결하기.

.then 또는 .catch.finally의 핸들러가 프라미스를 반환하면, 나머지 체인은 프라미스가 처리될 때까지 대기함. 처리가 완료되면 프라미스의 result(값 또는 에러)가 다음 체인으로 전달됨.

console.log('Start!);

fetch('https://jsonplaceholder.typicode.com/users')
	.then((response) => response.text())
	.then((result) => {console.log(result);});

console.log('End');

then메소드 뒤에는 계~속해서 then메소드들을 붙여나갈 수 있다.

then메소드가 이어질 경우 , 이전 콜백의 리턴값을 다음 콜백의 파라미터로 넘겨받아 사용하게됨.


fetch('https://jsonplaceholder.typicode.com/users')
	.then((response) => response.text())
	.then((result) => {
		const users = JSON.parse(result);
		return user[0];
	})
	.then(**(user)** => {
		console.log(user);
		const { address } = user;//비구조화할당으로 user에서 address추출.
		return address;
	})
	.then((**address**) => {
		console.log(address);
		const{ geo } = address;
		return geo;
	})
	.then (**geo**) => {
		console.log(geo);
		const { lat } = geo;
		return lat;
	})
	.then((lat) => {
		console.log(lat);
	});

여기서 프로미스 객체는 fetch함수의 작업결과인 response 하나라고 생각할 수 있으나,

사실 then메소드는 새로운 Promise객체를 리턴한다.(저 친구들 각각 별개의 Promise객체를 리턴중인거임)

Promise객체의 then메소드는 새로운 Promise객체를 리턴해주기 때문에, 속해서 then을 이어붙여나갈 수 있는 것!

중요한 사실,

then메소드가 return한 Promise객체는 가장처음엔 pending상태임.

나중에 then메소드로 등록한 콜백이 실행되고, 콜백에서 어떤 값을 리턴하면, then메소드가 리턴했던 Promsie객체가 영향을 받는다.

이 때, then메소드가 어떤값을 리턴하냐에 따라 받는 영향이 달라진다.

  1. callback안에서 Promise객체를 리턴하는 경우
  2. Promise객체가 아닌 값을 리턴하는 경우

then : Promise 객체 리턴

then안에있는 콜백이 어떤 값 / Promsie객체를 리턴해

1. callback안에서 Promise객체를 리턴하는 경우

then메소드가 리턴한 Promise객체와 동일한 상태와 결과값을 가지게 된다.

  • 만약 callback이 리턴한 Promise객체가 fulfilled상태가 되면, then메소드가 리턴한 Promise객체도 fulfilled상태를 갖고, 동일한 작업성공결과를 갖게된다.
  • 만약 callback이 리턴한 Promise객체가 rejected상태가 되면, then메소드가 리턴한Promise객체도 rejected상태를 갖고, 동일한 작업실패정보를 갖게된다.

2. callback에서 Promise객체가아닌 숫자.문자열, 일반객체를 리턴하는 경우

  • then메소드가 리턴한 Promise객체는 fulfilled상태가 되고, then메소드가 리턴하는 값을 작업성공결과로 갖게된다.

→ 이 두 가지 차이점은 중요한 거라서 코드를보면서 다시 설명

*참고

응답이 완전히 종료되고, 응답 전체를 읽으려면 메서드 response.text()를 호출해야 함. response.text()는 원격 서버에서 전송한 텍스트 전체가 다운로드되면, 이 텍스트를 result 값으로 갖는 이행된 프라미스를 반환합니다.

fetch('https://jsonplaceholder.typicode.com/users')
	.then((response) => response.text())
	.then((result) => {
		const users = JSON.parse(result);
		return users[0];
	})
	.then(**(user)** => {
		console.log(user);
		const { address } = user;//비구조화할당으로 user에서 address추출.
		return address;
	})
	.then((**address**) => {
		console.log(address);
		const{ geo } = address;
		return geo;
	})
	.then((**geo**) => {
		console.log(geo);
		const { lat } = geo;
		return lat;
	})
	.then((lat) => {
		console.log(lat);
	});
  1. 각각의 then메소드들은 각각 promise객체를 리턴하고, 모두 pending상태
  2. fetch함수가 리턴하는 promise객체가 fulfilled가 되면, 다음 then메소드의 callback이 실행된다.
  3. response객체의 text라는 메소드가 호출됨.(여기서 response객체의 text메소드는 프로미스 객체를 리턴하는 메소드이다. response를 잘 받았다면, text메소드가 리턴하는 Promise객체는 fulfilled상태가 되겠죠? 그리고 작업성공 결과로는 response의 내용을 갖고 있게됨)
  4. 위에서 말했듯, 콜백에서 프로미스 객체를 리턴할 경우, then메소드가 리턴했던(pending상태인) Promise객체도 콜백의 프로미스 객체와 동일한 상태(fulfilled)와 결과값(response의내용)을 갖게됨
  5. then메소드가 리턴한 결과값을 이어서 다음 콜백이 실행되는데, 여기서는 user[0]이라는 일반 객체(값)를 리턴해주고있음.:위에서 말했듯, 콜백 안에서 Promise가 아닌 일반 값들을 리턴할 경우, then메소드가 리턴했던 Promise객체는 fulfilled상태가 되고, 그 작업성공결과로는 callback에서 리턴한 값을 가지게 됨 (여기선 user[0])
  6. 이런식으로 계속이어짐...

이런식으로 순서대로 콜백이 실행되는 것이 then메소드의 특징.

text, json 메소드도 Promise객체 리턴한다


fetch로 받은 JSON데이터를 사용하기위해

fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => response.text())
  .then((result) => {
    const users = JSON.parse(result);
    // ...
  });

response 객체의 text 메소드로 리스폰스의 내용을 추출(response.text();)하고 이것을 Deserialize(JSON.parse(result);) 하거나

(serialize: object ⇒ JSON string, deserialize: JSON string ⇒ object)

  • JSON.parse()?

    JSON.parse() 메소드

    JSON.parse() 메소드는 JSON.stringify() 메소드와는 반대로 인수로 전달받은 문자열을 자바스크립트 객체로 변환하여 반환합니다.

    문법

    JSON.parse(text) text에는 변환할 문자열을 전달합니다. 이때 해당 문자열은 반드시 유효한 JSON 형식의 문자열이어야 합니다. 만약 JSON 형식에 맞지 않는 문자열을 전달하면, 자바스크립트는 오류를 발생시킬 것입니다. 코딩교육 티씨피스쿨
fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => **response.json()**)
  .then((users) => {
    // ...
  });

console.log('End');

response 객체의 json 메소드로 리스폰스의 내용 추출과 Deserialize를 한 번에 수행(response.json())할 수 있다

→ 이러한 text 메소드와 json 메소드가 Promise 객체를 리턴하는 메소드다.

1. text 메소드

fetch 함수로 리스폰스를 잘 받으면, response 객체의 text 메소드는, Promise객체를 리턴하게 됨.

: 이 Promise객체는 fulfilled 상태이면서 리스폰스의 바디에 있는 내용을 string 타입으로 변환한 값을 '작업 성공 결과'로 가짐

(문장이 조금 기니까 반복해서 읽어야함...)

이때 그 작업 성공 결과는 string 타입임. 이때 그 값이 만약 JSON 데이터라면 이전에 배운 것처럼 JSON 객체의 parse 메소드로 Deserialize를 해줘야한다!(JSON.parse(result);)

  • JSON 객체가 뭐냐? JSON은 JavaScript Object Notation의 약자입니다. JSON은 좀 더 쉽게 데이터를 교환하고 저장하기 위하여 만들어진 텍스트 기반의 데이터 교환 표준입니다. 예시
    {
        "language": [
            {
                "name": "HTML",
                "category": "web",
                "developer": "W3C"
            },
            {
                "name": "CSS",
                "category": "web",
                "developer": "W3C"
            },
            {
                "name": "Java",
                "category": "application",
                "developer": "Oracle"
            },
            {
                "name": "Python",
                "category": "application",
                "developer": "Python"
            }
        ]
    }
    
    JSON은 JavaScript Object Notation의 약자.
    • JSON은 사람이 읽을 수 있는 텍스트 기반의 데이터 교환 표준입니다.

    • 이러한 JSON은 XML의 대안으로서 좀 더 쉽게 데이터를 교환하고 저장하기 위하여 고안되었습니다.

    • 또한, JSON은 텍스트 기반이므로 어떠한 프로그래밍 언어에서도 JSON 데이터를 읽고 사용할 수 있습니다.

      JSON 데이터

      JSON 데이터는 이름과 값의 쌍으로 구성됩니다.

      이러한 JSON 데이터는 데이터 이름, 콜론(:), 값의 순서로 구성됩니다.

      문법

      "데이터이름": 값

      다음 예제는 데이터의 이름이 "name"이고, 값은 "식빵"이라는 문자열을 갖는 JSON 데이터의 예제입니다.

      예제

      "name": "식빵"

      데이터의 이름도 문자열이므로, 항상 큰따옴표("")와 함께 입력해야 합니다.

      데이터의 값으로는 다음과 같은 타입이 올 수 있습니다.

    1. 숫자(number)

    2. 문자열(string)

    3. 불리언(boolean)

    4. 객체(object)

    5. 배열(array)

    6. NULL

      http://tcpschool.com/json/intro

2. json 메소드

fetch 함수로 리스폰스를 잘 받으면, response 객체의 json 메소드는, Promise객체를 리턴하게되는데.

: 이 Promise객체는 fulfilled상태면서 response의 body에 잇는 JSON 데이털르 자바스크립트 객체로 deserialize한 객체를 '작업성공결과'로 가짐

만약 response의 body에 있는 내용이 JSON 타입이 아니라면 에러가 발생하고 Promise 객체는 rejected 상태가 되면서 그 '작업 실패 정보'를 갖게 된다. (json메소드가 json타입을 받기때문)

→ 핵심은 response 객체의 text 메소드와 json 메소드가 Promise 객체를 리턴하는 메소드라는 사실.

위에서 배운 내용인 'then 메소드가 리턴했던 Promise 객체(A)는 그 콜백에서 리턴한 Promise 객체(B)와 동일한 상태와 결과를 갖게 된다'는 규칙과 연관지어서 생각해보자.

이 말은 곧, 콜백에서 리턴한 Promise 객체로부터 새로운 Chain이 시작된다는 말과도 같음

response 객체의 text 메소드 또는 json 메소드 이후에 등장하는 then 메소드부터는 string 타입의 값이나 자바스크립트 객체를 갖고 바로 원하는 작업을 할 수 있다.

실행된 콜백 내부에서 에러가 발생했을 때

콜백이 실행되다가 에러가 발생하는 경우가 있습니다. 예를 들어

fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => {
        ...
        add(1, 2);// ReferenceError 발생
        ...
  });

이렇게 정의하지도 않은 함수를 콜백에서 사용해서 에러가 발생하거나

fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => {
        ...
        throw new Error('failed');
        ...
  });

특정 경우에 인위적으로 throw 문을 써서 에러를 발생시키는 경우도 있을 것임.

이렇게 콜백이 실행되다가 에러가 발생한다면, then 메소드가 리턴했던 Promise 객체는 어떻게 될까? 이 경우에는 Promise 객체가 rejected 상태가 되고, 작업 실패 정보로 해당 에러 객체를 갖게 됨. 개발자 도구에서 실행해보면

const promise = fetch('https://jsonplaceholder.typicode.com/users')
  .then((response) => { throw new Error('test'); });

promise 를 입력하여 then 메소드가 리턴한 Promise 객체의 내부를 살펴보면 이렇게 생겼는데요.

https://bakey-api.codeit.kr/api/files/resource?root=static&seqId=4374&directory=Untitled.png&name=Untitled.png

지금 [[PromiseState]]는 Promise 객체의 상태를, [[PromiseResult]]는 Promise 객체의 결과(작업 성공 결과 또는 작업 실패 정보)를 나타내는 내부 슬롯.
자세히 보면 현재 Promise 객체가 rejected 상태이고, 발생한 Error 객체를 그 작업 실패 정보로 갖고 있다는 것을 알 수 있e다.

  • 이렇게 콜백 실행 중에 에러가 발생하면, then 메소드가 리턴한 Promise 객체는 rejected 상태가 되고, 그 작업 실패 정보로 해당 Error 객체를 갖게 된다!

Promise Error Handling


.catch 는 프라미스에서 발생한 모든 에러를 다룬다. reject()가 호출되거나 에러가 던져지면 .catch에서 이를 처리합니다.

위에선 .then메소드를 사용해서 fulfilled상태가 되었을 때 실행할 콜백을 등록하는 경우만 보았는데,

rejected 상태가 될때 실행하고 싶은 콜백은 어떻게 등록하는 걸까?

fetch('https://jsonplaceholder.typicode.com/users') // Promise-A
  .then(successCallback, errorCallback);

: 이렇게 then메소드의 두번째 파라미터로 넣으면된다.!

하.지.만. 다른 방법도있다.
catch메소드를 사용하는것!

catch 메소드?

Promise객체가 rejected상태가 되엇을때 실행할 콜백을 등록하는 Promise객체의 메소드.

fetch('[https://jsonplaceholder.typicode.com/users](https://jsonplaceholder.typicode.com/users)')
.then((response) => response.text())
.catch((error) => {console.log(error);})
.then((result) => {console.log(result);});

위코드를 시험해보기 위해 일부러 error를발생시켜보자 (존재하지 않는url주소를 적어보장)
코드 실행하면, catch메소드 안의 콜백이 실행되어서 에러가 콘솔에 찍힘.

  • 사실 catch메소드는 then메소드를 약간 변형한것과 똑같음
  • 즉, .then(undefined, (error) => {console.log(error);})
    와 동일.
  • 실무에선 then을 활용한 rejected상태에서의 콜백등록 보단 catch메소드를 사용한다.

어떻게 fetch 함수에서 발생한 에러가 catch 메소드 안의 콜백에까지 전달될 수 있는 걸까?(약간어려움)

(인터넷 연결 안된상황가정) ⇒ fetch결과로 rejected

fetch('https://jsonplaceholder.typicode.com/users')// Promise-A
  .then((response) => response.text())// Promise-B
  .then(undefined, (error) => { console.log(error); })// Promise-C
  .then((result) => { console.log(result); });// Promise-D

catch 메소드는 then 메소드의 첫 번째 인자로 undefined을 넣은 것과 같기 땜에 catch 메소드를 then 메소드로 변환해서 이해해보자.

이 코드에서 fetch 함수와 각각의 then 메소드가 리턴하는 Promise 객체를 순서대로 Promise-A, B, C, D라고 하고, 그리고 각각의 Promise의 상태가 어떻게 변하는지 살펴보자.

일단 fetch 함수의 작업이 실패해서 Promise-A 객체가 rejected 상태가 되면, 첫 번째 then 메소드의 두 번째 콜백이 실행되어야 함.

첫 번째 then 메소드에는 두 번째 콜백이 없기 때문에 아무 콜백도 실행되지 X

이런 경우에는 Promise-B 객체가 Promise-A와 똑같은 rejected 상태가 되고, 동일한 작업 실패 정보를 갖게 된다.

→ 그럼 이제 rejected 상태가 된 Promise-B에 붙은 then 메소드에는 두 번째 콜백이 있기 때문에 이 두 번째 콜백이 실행됨. (즉, catch 메소드의 콜백이 실행). (rejected 상태의 Promise객체가 넘어갔기 때문에)

profile
디자인과 기획이 재미있는 프론트엔드 개발자입니다. 블로그 이사 준비중. . .

0개의 댓글