All Articles

wecode 1주차_7일 TIL_fetch로 json 데이터 불러오기

Image from iOS.jpg

너무 열심히 했는지 산 지 일주일도 안 된 나의 슬리퍼가 끊어져버렸다!ㅋㅋㅋㅋ (지금은 순간접착제로 붙였지롱~)

세상 어려웠던 부분…☆ 고통의 연속이였지만.. 주위 분들의 따뜻한 도움과 구글링을 통해 해결해나갔다..! Json 데이터를 자바스크립트로 불러오는 부분은 특히나 어려웠다. 노마드코더 니꼬의 무비앱 만들기 react강의에서 fetch를 통해 API를 가져오는 부분이 그당시엔 잘 이해가지 않았는데… 자바스크립트에서 함 해보니까 약간 알것 같기도 하고..!!

Promise

fetch는 Promise 기반의 매커니즘이라 fetch를 알아보기 전에 우리는 Promise를 먼저 알아야 해요!

pinky-swear-329329_1280.jpg

Promise란 말 그대로 약속! 대통령 후보들이 선거에 나와서 공약 걸잖아요. 그런것처럼 미래에 어떠한 일을 할 건데 그것에 대해 미리 말하는거라고 생각하면 돼요!

<Promise 실행과정>

  • pending : 실행되거나 (fulfiled) 실패되거나(rejected) 둘 중에 한 가지 일이 일어나기 바로 전의 상태.
  • fulfiled : 함수가 실행되면 fulfiled. 공약 지켰음.
  • rejected : 함수가 실행되지 않으면 rejected. 공약 불이행.
  • settled : 실행된건지 실패한건지 모르겠으나 여튼 결론이 난 상태.

예시를 통해 살펴볼게요~

let firstFunction = function() {
  return new Promise( // new를 안 써주면 Type Error가 뜨더라구요.
  function (resolve, reject){ //실행 되었을때, 안 되었을때가 인자로.. 
    resolve('나는 ') // 실행이 된다면 resolve 괄호 안의 함수가 뜸
  }
  )
}
console.log(firstFunction())

// 첫번째 함수와 이어지는 함수 만들려고 함
let secondFunction = function(dataFromFirstFunction) { 
  return new Promise( 
    function(resolve, reject){
      resolve(dataFromFirstFunction + '아직 배가 고프다')
    }
  )
}

console.log(secondFunction())

이 함수를 콘솔창에 찍어보면…

image.png firstFunction까지는 실행이 잘 되지만, secondFunction의 결과값으론 ‘undefined아직 배가 고프다’가 나온거 보면 연결이 안된것 같죠? resolve 괄호 안에 들어가는 함수의 이름이 dataFromFirstFunction긴 하지만 실질적으로 첫번째 함수에서 데이터를 받아오진 못해요!

이럴 때 then을 이용하는거에요! 함수1.then()을 실행하면 함수1이 실행되고 곧바로 then 괄호안의 함수가 실행이 됩니다.

firstFunction().then(function(data){
console.log(data)})

콘솔창에 이 함수식을 덧붙여보면 ‘나는’이라는 firstFunction의 값이 나오게 돼요! then 함수 안의 인자인 data를 console에 찍었더니 묶여있는 함수1의 값을 불러오게 되는거죠~ 그럼 이러한 특성을 이용해 첫번째 함수와 두번째 함수를 엮어봅시다!

firstFunction().then(function(data){
  console.log(secondFunction(data))
})

secondFunction()을 실행시킬때 그 안에 data라는 인자를 넣어야 값이 나오니까 함수 안에 윗줄에서 썼던 data라는 동일한 인자를 넣어줬어요.

image.png 이제 Promise 함수 안의 값이 제대로 뜹니다! 하지만 우리의 목표는 string 형태의 리턴값이니깐.. console.log 부분을 return으로 살짝 바꿔봅시다!!

firstFunction().then(function(data){
  return secondFunction(data)
})

콘솔창에 이 식을 찍어보면 여전히 Promise 함수를 벗어나지 못하는걸 확인할 수 있어요. 이럴땐 then을 한번 더 써줘야해요.

firstFunction().then(function(data) {
  return secondFunction(data);
}).then(function(data){
        console.log(data)
   })

세번째줄 then 앞까지가 ‘나는 아직 배가 고프다’라는 string을 가진 Promise를 불러오는 거였죠. 거기다 then을 이용해 함수의 인자로 data라는 값을 넣었을 때, console창에 data 값이 나오게 해줄거에요.

image.png

값이 string 형태로 아주 잘 나오고 있죠??!

근데 여기서 한가지 의문점이 생기는 것…

image.png

마지막에 console.log가 아닌 return이 되면 string이 아닌 Promise 형태로 값이 나온다는것.. 이건 왜 그런지 도저히 모르겠네요 ㅠㅠ

fetch

fetch는 생활코딩으로 기초를 잡고 제로초님 포스팅과 유튜브로 다시 공부해봤어여~

  1. fetch() : API에서 data를 불러올때 쓰는 Promise 기반의 매커니즘. fetch(‘html’)를 통해서 괄호안의 파일을 불러온다. fetch가 리턴하는건 결국은 Promise라서 우리는 then을 통해 Promise가 아닌 다른 형태로 값을 불러와야한다… 맞나?
  2. .then() : then은 fetch의 비서 정도라고 생각하면 될듯..?! fetch 실행으로 서버에서 응답할 때까지 .then()이라는 함수를 이용해서 then의 괄호 안에 들어가는 함수를 실행한다! 쉬지도 않고 부지런쓰~ 위에서 나온 Promise 때와 동일한 기능!
fetch('./data/getAllTimeline.json')
.then((res) => {
  if (res.status === 200 || res.status === 201) { 
    res.text().then(text => console.log(text))
  }
  else {
    console.log(res.statusText)
  }
}).catch(err => console.log(err)) 

간단히 보자면 then을 통해서 reponse 객체를 받고, catch를 통해 error를 받아요! 이 과정을 순서대로 자세히 살펴봅시다~

  1. fetch로 data폴더의 getAllTimeline.json이라는 데이터를 불러온다
  2. then을 이용해 response 객체를 불러오는데…
  3. response의 상태가 200이나 201일때(정상작동시)
  4. 콘솔창에 text를 띄운다
  5. 그렇지 않으면 response의 상태를 띄운다. 404 Page Not Found와 같은 빡치는 에러를 보게 되는거죠 ㅎ

미니터 json파일을 자바스크립트로 불러오기

저는 일단 data라는 폴더 안에 아래와 같은 getAllTimeline.json이라는 파일을 가지고 있어요.

{
  "result": [
    {
      "contents": "도리를 찾아서~",
      "date": "19920601",
      "user": "Dory"
    },
    {
      "contents": "와플번트 진짜 맛있었는데 왜 사라진거지 너무 속상하다",
      "date": "20190327",
      "user": "Waffle"
    },
    {
      "contents": "예쓰! 우리는 모두 위코더 룰루~",
      "date": "20190729",
      "user": "Wecode"
    },
    {
      "contents": "아스트로에 차은우가 있다면, 위코드에는 내가 있쥐",
      "date": "20190323",
      "user": "Eunwoo Song"
    },
    {
      "contents": "누구보다도 예리한 감각을 자랑하는 프론트앤드 엔지니어는 누구~?",
      "date": "20190323",
      "user": "Yeri Kim"
    },
    {
      "contents": "아이스크림 드실분?",
      "date": "20190803",
      "user": "Jihun Park"
    },
    {
      "contents": "주말에는 냉방 시스템이 가동되지 않으니 이 점 참고하시기 바랍니다.",
      "date": "20190321",
      "user": "WeWork"
    }
  ]
}

자바스크립트에서 fetch를 써봅시다. 페이지를 열 때 기본적으로 떠 있는 트윗목록을 불러오기 위해 opening이라는 함수 안에 fetch함수를 써봤어요.

function opening() {
  fetch('./data/getAllTimeline.json') // json파일이 있는 파일의 경로
    .then((res) => res.json()) // 데이터를 json형태로 바꿔주기
    .then(function (data) { // data라는 임의의 값을 인자로 받는 함수.
      let people = data.result; //json파일을 감싸는 객체가 result로 시작해서 data.result
      console.log(people) // people 콘솔을 찍어보면 {contents: "도리를 찾아서~", date: "19920601", user: "Dory"} 이러한 객체가 7개 나옴
      return people.map(function (person) { //people에서 contents,date,user를 뽑아내기 위해 map 사용
      // 이 아래는 html 틀 구현을 위해 쓴 식들
        let contents_wrap = createNode('div') 
        let name = createNode('button')
        let time = createNode('span')
        let doorit = createNode('div')
        contents_wrap.className = 'contents_wrap'
        name.className = 'doorits_id'
        time.className = 'time'
        doorit.className = 'doorits_contents'

        name.innerHTML += person.user
        time.innerHTML += `${String(person.date).split('').slice(0, 4).join('')}-${ String(person.date).split('').slice(4, 6).join('')}-${ String(person.date).split('').slice(6, 8).join('')}`
        doorit.innerHTML += person.contents

        append(contents_wrap, name)
        append(contents_wrap, time)
        append(contents_wrap, doorit)
        append(doorits_box, contents_wrap)

        doorits_box.prepend(contents_wrap)

        //원래 있는 트윗의 개수 불러오기!
        const sum_doorits = document.getElementsByClassName('sum_doorits')[0]
        const total_doorits = doorits_box.children.length
        sum_doorits.innerHTML = total_doorits;
      })
    })
    .catch(err => console.log(err))
}

개발자도구-네트워크 탭에 한번 들어가보세요. json 데이터를 성공적으로 불러왔다면 네트워크 탭에서 확인할 수 있어요~~

image.png

image.png

값도 아주 잘 불러와지고요!

근데 식을 작성하면서 fetch TypeError가 생겼었죠.. 어떤 오류냐..!

VM1621:1 Uncaught (in promise) TypeError: Failed to execute ‘fetch’ on ‘Window’: 1 argument required, but only 0 present. at :1:1

이 오류가 왜 생겼냐 하고 봤더니… 제가 Live Server를 통해 파일을 열지 않아서였더라고요 Ctrl+Shift+x 로 Live Server 검색 고고~ 비주얼 스튜디오 확장팩 다운 받으세요 그 다음 html파일 우클릭 해서 열면 저 오류는 없어질거에요~~

별책부록… 미니터에 업데이트 되는 트윗 수 반영하기

제가 처음에 접근했던 방식은..

image.png 2번의 div의 개수를 세서 1번에 추가가 될수 있도록.. 아주 단순하게 생각했어요

const sum_doorits = document.getElementsByClassName('sum_doorits')[0](1번의 className)
const total_doorits = doorits_box.children.length //화면에 뜬 전체 트윗중 하얀부분의 개수(2번의 개수)
sum_doorits.innerHTML += total_doorits; (1번에 2번의 개수 넣기)

이렇게요!

웹페이지의 콘솔에는 개수가 7개로 잘 나오지만 visual studio에선 값이 0개가 나오더라고요 저는 저 합계를 세어주는 함수의 위치가 별로 중요하지 않다고 생각해서 아무데나 넣었는데 그렇게 하면 안되고! 트윗이 생성되고 삭제되는 곳 모두에 넣어야 하더라구요!

그래서 트윗을 불러올때, 트윗 생성될때, 트윗 삭제될 때 모두 세 곳에 저 식을 넣어줬어요. 그랬더니 숫자가 넘나 잘 뜨는것!!

또 신기했던 점…

트윗 삭제가 될땐 +=이나 —가 아닌 그냥 대입연산자로 쓴다는 점.. 그것도 모르고 +1를 했는데 값이 늘어나서 거기다 -1을 붙이니까 값이 제대로 반영이 되고.. 어??? 이상한데?? 나 너무 바보같은데?? 하다가 식을 보니 제가 +=을 =로 썼더라고요 ㅋㅋㅋㅋ

여튼… 미니터는 이제 끝났습니다~(css 부분 정리할까말까 고민중…ㅠㅠ) 이젠 저도 게임만들기로 궈궈…☆

Reference