코로 넘어져도 헤딩만 하면 그만

fetch API에 promise 활용하기 본문

CODE STATES 44

fetch API에 promise 활용하기

꼬드리 2023. 3. 21. 15:38

자바 스크립트에서 fetch API는 무엇일까요?

요놈은 네트워크를 통해 이루어지는 요청이 가능하게 해주는 API입니다. 네트워크 요청은 대개 비동기 요청으로 이루어집니다. 따라서 URL로 데이터 요청하도록 도와주는 fetch API를 사용하면서, promise에 대해 좀더 알아보았습니다.

 

fetch API특정 URL로부터 정보를 받아오는 역할을 해줍니다. 

let url =
  "https://koreanjson.com/posts/1";
fetch(url)
  .then((response) => response.json())
  .then((json) => console.log(json))
  .catch((error) => console.log(error));

주로 이런 식으로 사용하는데,  url을 fetch("https://koreanjson.com/posts/1")처럼 직접 입력하는 것도 당연히 가능합니다. 

지금, 저 url 변수에 담긴 주소에는 JSON 형태의 데이터가 들어있습니다. 이 데이터를 fetch를 통해 가져와 원하는대로 만져줄 생각입니다. JSON 데이터란?

 

fetch(url)을 통해 우선 데이터를 불러오길 시도합니다.

.then 을 써서 error가 발생하지 않고 무사히 받아오는 상황을 가정합시다. 만약 에러가 발생했다면 아래의 .catch에서 받아오지 못했다고 결과가 나올 겁니다. 지금은 우선 잘 받아온 상황을 가정해주자고요.

무사히 받아온 결과에 response.json()를 붙여 날것의 JSON 파일을 JS 객체로 변환시켜줍니다. 이제 우리가 가진 데이터는 자바 스크립트 객체입니다. JSON파일과 자바스크립트 객체는 비슷하게 생겼지만 분명 다릅니다! 

 

-> 자꾸 헷갈리던 fetch에서 response.json() 그래서 왜 쓰는데?에 관한 참고 블로그

https://velog.io/@bining/javascript-JSON-%EC%A0%95%EC%9D%98%EC%99%80-%EB%A9%94%EC%84%9C%EB%93%9C 

 

그럼 이제 그 객체로 만든 결과를 한번 봅시다. .then에 json을 매개변수로 사용해서 바로 이전의 데이터를 불러온 뒤 콘솔을 찍어봅니다. 당연히 자바스크립트 객체로 예쁘게 만들어둔 결과물이 콘솔에 나오겠지요. 아래 .catch는 이 과정에 에러가 발생했을 때 콘솔에 error라고 명시해달라는 부분입니다.

자, fetch API가 어떻게 동작되는지 감이 오시나요? 

 

이처럼 fetch API의 동작 방식에 먼저 익숙해지는 것이 문제를 해결하는 데에 도움이 된 듯 합니다. 바로 문제로 넘어가봅시다.

 

 

01. basicChaining

먼저 문제에 관해 설명해보겠습니다. 아래의 두 주소에는 각각 JSON 데이터가 담겨 있습니다. 이 두 주소에서 데이터를 긁어온 뒤, 하나의 객체 안에 쏙 넣어주는 것이 도전 과제입니다. 말은 참 쉬운데...

우선 1번에서는 Promise.all이나 async await를 쓰지 않고 promise chain만 사용해서 풀어보겠습니다.

const newsURL = "http://localhost:4999/data/latestNews";
const weatherURL = "http://localhost:4999/data/weather";

function getNewsAndWeather() {
  // TODO: fetch을 이용해 작성합니다
  // TODO: 여러개의 Promise를 then으로 연결하여 작성합니다
}

if (typeof window === "undefined") {
  module.exports = {
    getNewsAndWeather,
  };
}

(완성한 코드는 조금 아래에 있습니다. 완성본을 띄워두고 설명을 읽으면 더 이해가 쉽게 될 겁니다.)

우선 각각의 주소를 변수 내부에 담아줍시다.

그리고 결과를 하나의 객체에 담아야 하니, let obj = {} 로 하나의 빈 객체를 만들어 주었습니다.

이제 무사히 불러왔을 때를 가정합니다. .then과 response.json()을 통해서 JSON파일을 js객체로 변환시켜 받아옵니다(이건 위에서 설명한 fetch로 불러올 때 자주 쓰이는 JSON 활용 방식과 연관이 있죠?).

이후 .then으로 프로미스를 받아 obj의 키 'news'를 만든 뒤 data.data를 넣어줍니다. 응? 왜 data.data냐구요?

 

사실, 주소 newsURL에 담긴 JSON 데이타는 data라는 키에 우리가 진짜 불러오고 싶은 값을 갖고 있습니다. 원래 newsURL 내부에 어떤 데이터가 들어있었는지 한번 까볼까요?

{
"data": [
{
"row_id": 2,
"title": "2021년 경제 성장률 전망 밝아",
"source": "A신문",
"timestamp": "2020/12/30"
},
{
"row_id": 3,
"title": "코로나19 증가추세 대폭 하락해",
"source": "BBC",
"timestamp": "2020/12/29"
},
{
"row_id": 4,
"title": "코드스테이츠 취업연계 파트너사 xxx건 돌파",
"source": "스타트업 뉴스",
"timestamp": "2020/12/31"
}
]
}

맨 위에 data라는 키의 내부에 자료들이 들어있는 게 보이죠. 바로 이것 때문에 매개변수 data로 들어온 객체에서 data키를 찾아서 이것만 새 객체 obj에 넣어주어야 합니다.

 

이제 weatherURL을 fetch한 결과를 리턴해준 뒤 다시 then으로 chaining합니다. weatherURL 주소에 들어있는 원본 JSON 데이터에는 newsURL과 달리 키가 없습니다(뭐가 들어오는지 헷갈리면 콘솔 찍어보고 들어온 데이터의 구조를 파악하면 도움이 됩니다.)

따라서 매개변수 data로 들어온 값 그대로 넣어주면 됩니다. data.data처럼 쓸 필요가 없다는 뜻이죠.

마지막으로 처음에는 비어 있었지만, 지금은 두 개의 키가 담긴 객체 obj를 리턴해주면 답이 나오겠습니다. 

 

코드는 아래와 같이 되겠네요.

const newsURL = "http://localhost:4999/data/latestNews";
const weatherURL = "http://localhost:4999/data/weather";

function getNewsAndWeather() {
  // TODO: fetch을 이용해 작성합니다
  // TODO: 여러개의 Promise를 then으로 연결하여 작성합니다
  let obj = {};
  return (
    fetch(newsURL)
      .then((response) => response.json())
      //response.json 메서드를 사용하면 json파일을 바로 js객체로 받아온다.
      //fetch API의 응답(response) 객체는json()를 제공하고 있어JSON.parse() 대신 사용할 수 있다.
      //response.json()객체를 호출하면 바로 JSON데이터가 js객체로 변한다는 뜻.
      .then((data) => {
        obj["news"] = data.data;
        return fetch(weatherURL);
      })
      .then((response) => response.json())
      .then((data) => {
        obj["weather"] = data;
        console.log(obj);
        return obj;
      })
  );
}

if (typeof window === "undefined") {
  module.exports = {
    getNewsAndWeather,
  };
}

 

 

 

02. promiseAll

동일한 내용을 promiseAll을 사용해서 작성해 보았습니다. 

Promise.all을 통해 두 주소에서 데이터를 한번에 긁어오면,

.then을 사용해서 들어온 data를 객체 내부에 넣은 뒤 리턴 해주는 식입니다.

 

아래와 같이 fetch로 데이터를 받아와서 json파일을 객체로 만들어주기 / 이 데이터를 가공해 새 객체에 넣기 로 과정이 나뉘는 것을 볼 수 있습니다. .then(data) 쯤에 data의 콘솔을 찍어보면 어떤 형태로 가공된 데이터가 주어지는지 확인해볼 수 있습니다.

헷갈리지 마세요. .then(data)로 쓰는 data는 매개변수입니다. 헷갈린다면 다른 단어로도 사용할 수 있다는 뜻입니다. 그러나 data[0].data에서 뒤에 붙는 data는 newsURL의 데이터가 갖고 있는 기본 키 이름입니다. 바뀌어선 안 됩니다.

function getNewsAndWeatherAll() {
  return Promise.all([
    fetch(newsURL).then((response) => response.json()), 
    fetch(weatherURL).then((response) => response.json()
    )])
    .then((data) => {
    //console.log(data)를 찍어보면 [0]과 [1]에 값이 실려오는 걸 볼 수 있습니다.
    //따라서 아래처럼 나눠서 각각 넣어줘야 합니다.
    return { news: data[0].data, weather: data[1] };
  });
}

if (typeof window === "undefined") {
  module.exports = {
    getNewsAndWeatherAll,
  };
}

 

 

03. asyncAwait

async function getNewsAndWeatherAsync() {
  let news = await fetch(newsURL).then((response) => response.json());
  let weather = await fetch(weatherURL).then((response) => response.json());
  return { news: news.data, weather: weather };
}
if (typeof window === "undefined") {
  module.exports = {
    getNewsAndWeatherAsync,
  };
}

마지막으로 이제 async await를 사용해서 같은 결과를 구현해 볼게요. 1번에서 변수에 넣은 주소 newsURL과 weatherURL을 그대로 사용합니다. 이건 async함수이기 때문에 맨 앞에 async를 붙여 두었습니다.

 

이제 비동기적으로 불러오는 데이터들에 await를 써줍니다. fetch로 불러오고-response.json()로 다듬어둔 자료를 변수 news와 weather에 각각 넣어줍니다. 아래서 또 쓸 건데, 길어지면 보기 싫잖아요. 

마지막으로 결과값 객체는 return 해주면 됩니다. news만 news.data인 이유는 뭐다? 위에서 계속 설명했듯이 news 데이터 속에는 data라는 키가 있고 그 내부에 우리가 원하는 값이 들어있기 때문입니다. 그 내부까지 들어가야 해요!  

 

이제 1번 2번 3번이 전부 동일하게, 두 주소에 담긴 값을 깔끔한 하나의 객체로 키 news와 키 weather을 활용하여 담아준 것을 볼 수 있습니다. 유레카! 

Comments