fetch 함수가 반환하는 Promise를 어떻게 사용(Consume)하는지 방법을 알아보자.
아래는 오픈 API를 사용해 국가(country) 데이터를 가져오는 코드이다. 처음 fetch로 요청했을 때 Promise는 'pending' 상태이다. 비동기 작업이 백그라운드에서 실행 중이기 때문이다. 이후, 어느 시점이 되면 Promise는 'settled(정해짐)' 상태로 이동하여, fulfilled 또는 rejected가 된다.
const getCountryData = function (country) {
fetch(`https://restcountries.com/v2/name/${country}`).then(function (
response
) {
console.log(response);
});
};
getCountryData('portugal');
'fulfilled'된 Promise를 처리하는 방법
상기 코드에서 Promise가 항상 성공(fuifilled)한다고 가정해보자. 성공된 상태를 다루기 위해 Promise에서 then 메소드를 호출할 수 있다. 이때, then 메소드에 콜백 함수를 넣어줘야 한다. 여기서 콜백 함수는 Promise가 성공한 후 결과가 나오면 바로 실행되길 원하는 함수이다. 따라서 콜백 함수는 Promise의 결과값인 인수(arguments)를 1개 받는다. 인수로 받은 response 값은 아래와 같이 나타난다.
response 값의 body를 보면 'ReadableStream'이라고 나타난다. 이는 아직 실제 데이터를 확인할 수 없는 상태이다. 데이터를 읽기 위해서는 응답값(response)에 json 메소드를 호출해야 한다. json 메소드는 모든 fetch 응답값에 사용 가능한 메소드이다. 여기서 중요한 사실은 json 메소드 자체도 비동기 함수라는 것이다. 이말은 json 메소드가 새로운 Promise를 반환한다는 것이며, 이를 다루기 위해서 또다른 then 메소드가 필요하다. 이를 종합하여 아래와 같이 코드를 작성할 수 있다.
const getCountryData = function (country) {
fetch(`https://restcountries.com/v2/name/${country}`)
.then(function (response) {
console.log(response); // ReadableStream으로 데이터 확인 불가능.
return response.json(); // 'json'메소드는 'Promise'로 동작한다.
})
.then(function (data) {
console.log(data); // 데이터 확인 가능.
renderCountry(data[0]));
});
};
getCountryData('portugal');
상기 이미지를 보면 두번째 then 메소드의 값에서 데이터를 확인할 수 있다는 사실을 알 수 있다. 이는 첫번째 then에서 받은 response 값이 데이터 그 자체이기 때문이다.
프로미스(Promise) Chaining
프로미스 Chaining을 해보자. 정상적으로 호출된 데이터 값을 보면 'borders'라는 이웃국가 정보를 나타내는 프로퍼티가 존재한다. then 메소드는 항상 Primise를 반환하기 때문에 해당 프로퍼티에 Promise Chaining을 사용해 이웃 국가의 데이터를 불러오는 코드를 아래와 같이 만들 수 있다. 코드를 분석해보면 두번째 국가를 호출하는 fetch ajax는 첫번째 fetch 국가 정보에 의존하는 것을 알 수 있다.
*참고 : then 메소드는 return에 값을 하드코딩으로 넣어도 Promise를 반환한다.
const getCountryData = function (country) {
// 첫번째 국가 호출
fetch(`https://restcountries.com/v2/name/${country}`)
.then((response) => response.json())
.then((data) => {
renderCountry(data[0]);
const neighbour = data[0].borders?.[0];
if (!neighbour) return;
// 두번째 국가 호출 (첫번째 국가 정보에 의존)
return fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`);
})
.then((response) => response.json())
.then((data) => renderCountry(data, 'neighbour'));
};
getCountryData('portugal');
초보자들이 많이하는 실수
처음 then 메소드를 사용하면 아래와 같이 then 메소드 안에 Chaining을 만드는 경우가 있다. 이 경우도 여전히 동작하지만, Call back 헬과 마찬가지로 가독성이 떨어지게 된다. 따라서 Primise를 처리하면 Chain은 항상 바깥에서 처리해야한다.
const getCountryData = function (country) {
// 첫번째 국가 호출
fetch(`https://restcountries.com/v2/name/${country}`)
.then((response) => response.json())
.then((data) => {
renderCountry(data[0]);
const neighbour = data[0].borders?.[0];
if (!neighbour) return;
// 두번째 국가 호출 (첫번째 국가 정보에 의존)
fetch(`https://restcountries.eu/rest/v2/alpha/${neighbour}`)
.then((response) => response.json())
.then((data) => renderCountry(data, 'neighbour'));
});
};
getCountryData('portugal');