본문 바로가기
  • soldonii's devlog
Javascript 공부/Zero To Mastery(-)

(16) HTTP/JSON/AJAX + 비동기적 자바스크립트 1

by soldonii 2019. 8. 27.

*Udemy의 "The Complete Web Developer in 2019 : Zero To Mastery" 강의에서 학습한 내용을 정리한 포스팅입니다.

*https://soldonii.github.io에서 2019년 7월 23일(화)에 작성한 글을 티스토리로 옮겨온 포스팅입니다.

*자바스크립트를 배우는 단계라 오류가 있을 수 있습니다. 틀린 내용은 댓글로 말씀해주시면 수정하겠습니다. 감사합니다. :)


1. HTTP/HTTPS

Tim Berners-Lee가 HTML을 만들었을 당시, 다른 컴퓨터의 문서를 불러와 읽기 위해 HTTP(Hyper Text Transfer Protocol) 또한 고안했다는 것을 배웠었다. HTTP를 통해 HTML, CSS, Javascript 파일을 불러와 웹 상에서 볼 수 있게 되었다. 쉽게 말하면, HTTP는 ‘클라이언트(client, 웹브라우저가 대표적인 클라이언트이다.)’와 ‘서버(server)’가 서로 의사소통 할 수 있는 공통언어인 셈이다.

 

클라이언트와 서버가 의사소통을 하는 과정은 다음과 같다.(용어를 정확하게 알아두자.)

⇒ 클라이언트는 HTTP를 통해 서버에 요청(request)을 보낸다.

⇒ 클라이언트의 요청(request)를 받은 서버는 해당 요청을 수행한 후, 클라이언트에게 응답(response)해 준다.

 

출처 : https://www.udemy.com/the-complete-web-developer-zero-to-mastery/

 

HTTP는 클라이언트가 보내는 요청(request)과 서버가 보내는 응답(response) 두 가지로 이루어져 있다. 이 중 요청(request) 파트에서 배울 것은 딱 4개이다! GET, POST, PUT, DELETE 명령어이다.

 

1) REQUEST 파트

# HTTP에서 사용되는 request 관련 네가지 명령어

  1. GET : 클라이언트가 서버에게 "HTML, CSS, Javascript 파일을 받고싶어!"라고 요청할 때 사용한다.
  2. POST : 클라이언트가 서버에게 "데이터를 보내줄테니 서버의 database에 저장해줘!"라고 요청할 때 사용한다.
  3. PUT : 클라이언트가 서버에게 "데이터를 보내줄테니 서버에 존재하는 정보를 update해줘!"라고 요청할 때 사용한다.
  4. DELETE : 클라이언트가 서버에게 "백엔드 database에 있는 정보를 지워줘!"라고 요청할 때 사용한다.

조금 원론적이니 Twitter 예시를 통해 이해해보자. GET은 트위터 서버에서 트윗 피드를 요청해서 사용자의 화면에 불러올 때, POST는 신규 유저가 가입했을 때, 가입 정보를 트위터 서버에 저장할 때, PUT은 유저가 포스팅한 트윗의 내용을 수정하고자 할 때, DELETE는 트윗을 삭제하거나 또는 사용자가 계정을 삭제할 때 사용되는 HTTP 명령어가 된다.

초기 HTTP는 단순히 텍스트를 전달하는 것에 불과했으나, 현재는 이미지, 비디오 등의 데이터도 HTML form 태그를 이용해서 백엔드에 전달할 수 있다. 또한 HTTP는 AJAX(Asynchronous Javascript And XML) 기술을 활용해서 웹페이지를 실시간으로 업데이트 할 때에도 사용될 수 있다.

 

# 클라이언트가 데이터를 서버에 전달하는 두가지 방식

특정 사이트에 접속할 때, 클라이언트는 링크의 주소를 서버에 전달하고 서버로부터 해당 링크에 대한 파일을 전달받아서 웹 페이지에 로드한다는 것을 배운 적이 있다. 만약 링크 주소 말고, 유저가 입력한 특정 데이터를 서버에 전달하고 싶을 때(ex. 유저가 로그인 시 입력한 정보를 서버에 전달)는 어떤 방식으로 전달할 수 있을까?

 

첫번째 방식은 query string을 활용하는 방식이다. HTML form tag에서 method="GET" 으로 입력하면 query string을 통해 데이터를 전달하게 된다. 이 경우 데이터를 전달하는 순간, query string으로 변환된 데이터가 사이트 url 뒤쪽에 붙게 되며 동시에 서버에게 해당 정보가 전달된다. 아래 사진에서 보이듯이, 크롬 개발자 도구의 network 탭을 보면 Query String Parameter라는 이름으로 값이 전달된 것을 확인할 수 있다.

 

GET 메소드 - query string

두번째 방식은 HTML의 body에 붙여서 전달하는 방식이다. HTML form tag에서 method="POST" 로 입력하면 이 방식으로 전달한다. 이 경우 url 끝에 query string 형태로 데이터가 붙지 않으며, 마찬가지로 크롬 개발자 도구의 network 탭을 눌러보면 Query String Parameter 대신 Form Data라는 이름으로 데이터가 전달된 것을 확인할 수 있다. 서버 입장에서는 query string이 아니라 HTML의 body에 있는 Form Data를 통해 이 데이터를 전달받는 것이다.

 

POST method

 

# 문제점(HTTP vs. HTTPS)

그러나 GET을 이용하든, POST를 이용하든 문제가 있는데, 바로 password 같은 민감한 정보가 그대로 노출되고 있다는 점이다. HTTP 대신 HTTPS를 사용하게 된 배경이 여기에 있다. HTTPS의 끝에 붙는 S는 secure의 약자이다. HTTPS를 사용하면 브라우저와 웹사이트 사이의 커뮤니케이션이 암호화된다. 그렇기 때문에 데이터는 클라이언트와 서버만이 읽을 수 있게 된다. 참고로 HTTPS는 TLS(Transport Layer Security) 또는 그 전신인 SSL(Secure Socket Layer) 기술을 활용하고 있다고 한다.

 

2) RESPONSE 파트

# response of HTTP

응답(response) 파트의 경우, 서버가 클라이언트에게 전달하는 주요한 응답은 2가지이다. 첫번째는 "HTTP Status Messages"(HTTP Status Messages)이고, 두번째는 HTML과 같은 형태의 데이터이다.(이에 대해서는 이 수업의 백엔드 파트에서 더 다룰 예정이다.)

 

2. JSON

클라이언트과 서버가 데이터를 주고받을 때, 데이터는 텍스트로 이루어져 있다.

텍스트가 아닌, 예를 들어 우리에게 익숙한 Javascript 객체를 그대로 서버에 보낸다든지 할 수가 없다는 의미이다. 왜냐면 백엔드의 서버는 Javascript 뿐 아니라 Python이나 PHP 같은 다른 언어로도 구성이 될 수 있는데, 바꿔말하면 Javascript 언어로만 데이터를 보내면 서버는 그 말을 이해하지 못할 수 있다는 것이다.

따라서 데이터를 텍스트로 변환해서 주고받고, 이를 다시 Javascript 언어로 변환하는 방식이 필요하고, 이 때 사용되는 것이 바로 JSON(JavaScript Object Notation)이다.

 

참고로 데이터를 서버에 전달하는 방식으로 JSON 말고 XML 방식도 있지만 표준은 JSON으로 옮겨가고 있다. Javascript 객체와 유사하게 생겼기 때문에 더욱 편리하다. 상대적으로 XML은 HTML과 유사하게 생겼다.

JSON은 Javascript 외에 어떤 언어로도 읽힐 수 있다. 위에서 말했듯 단순 텍스트에 불과하기 때문이다. 따라서 Javascript 언어로 입력받은 데이터를 JSON을 이용해서 텍스트로 변환한 후, HTTP를 통해 서버에 데이터를 전달(request)하고, 서버는 데이터를 받아 필요한 요청을 수행한 후 클라이언트에게 수행 내역을 응답(response) 해준다. 클라이언트는 텍스트로 전달받은 response를 JSON을 이용해서 다시 Javascript 언어로 변환해서 동작을 수행한다.

 

# JSON 메소드

JSON은 텍스트 ⟺ Javascript 객체 변환을 용이하게 하기 위한 것이므로 각 변환에 해당되는 메소드 2개만 알면 된다.

 

  • JSON.stringify() : 자바스크립트 객체를 텍스트로 변환할 때 사용하는 JSON 메소드이다.
  • JSON.parse() : 텍스트를 다시 자바스크립트 객체로 변환할 때 사용하는 JSON 메소드이다.
let obj = JSON.parse('{"name":"John", "age":30, "city":"New York"}');
let myJSON = JSON.stringify(obj);

 

# JSON vs. Fom Data

아래는 서버에 JSON과 Form Data로 데이터를 전달할 때의 차이가 무엇인지를 간략하게 설명해놓은 글이다. Andrei가 기술해놓은 영어 원문을 그대로 옮겨놓는다.(혹시 해석하다가 잘못된 정보가 전달될까봐..) 핵심만 보면, Form Data는 form tag를 통해 정보를 서버에 전달하게 되는데, JSON과 AJAX를 이용하면 form 전체를 서버에 전달하지 않고 특정 데이터만, 내가 원할 때 서버에 전달할 수 있다는 내용이다.

 

Originally, the only way to submit some form data to a server was through the <form> tag in HTML. As we have learned, it can do a POST or a GET request. With JSON you can now grab the contents of the <input> in a form and submit those with JSON instead of as a form data. You can now submit to the server whenever you want without it necessarily being a <form>, through AJAX.. What is AJAX you might say?

 

3. AJAX

여태까지 HTTP를 사용해서 어떻게 클라이언트와 서버가 통신하는지 살펴봤다. 다만 한 가지 문제가 있다면, 위에서 배운 내용만을 활용해서 서버와 통신할 경우, 클라이언트는 서버로부터 response를 받을 때마다 페이지를 reloading해야 한다는 점이다. 당연히 이는 웹 페이지의 속도를 저하시키고, 아주 나쁜 UX를 전달하게 된다. 그리고 사이트가 커지면 커질수록 이 문제는 더욱 심화될 것이다. 따라서 웹페이지가 한 번 로딩된 후에는, 전체가 아닌 변화가 필요한 부분만 update 시킬 수 있는 기술이 필요한데, 이를 가능케 하는 기술이 바로 AJAX(Asynchronous Javascript And XML)이다.

 

AJAX를 활용하면 페이지 reloading 없이 정보를 업데이트가 가능하며, 유저가 웹페이지를 서핑하는 동안 백그라운드에서 필요한 데이터를 처리한다. 2006년 구글에 의해 개발되었고, 브라우저에 이미 구현되어 있는 XMLHTTP request라는 방식을 활용해서 수행된다.

 

다만 XHR(XMLHTTP Request) 방식은 아래 사진만 봐도 알다시피 꽤나 복잡하다. 이는 jQuery가 등장하면서 훨씬 간편해졌다. 하지만 현재는 Fetch 명령어를 사용함으로써 훨씬 더 간편하게 사용할 수 있다.

출처 : https://www.udemy.com/the-complete-web-developer-zero-to-mastery/

 

React-app 실습에서 했던 roboFriends App에서 JSON을 어떻게 활용했는지 코드 한 줄 씩 뜯어보자.

componentDidMount() {
  fetch('https://jsonplaceholder.typicode.com/users')
    .then(response => response.json())
    .then(users => this.setState({ robots: users }));
}

 

 

  • fetch('https://jsonplaceholder.typicode.com/users') : fetch()는 window 객체의 일부이다. 현재 이 url에는 JSON 형태로 이루어진 객체가 담겨져 있는데, 클라이언트 단에서 fetch()를 통해서 웹 API 서버에서 해당 객체를 불러오겠다고 요청(request)하는 것이다. 

    그러면 서버는 API에 접속해서 필요한 요청을 가져오는데에 성공했는지 실패했는지를 Promise 객체의 값에 담아서 클라이언트에게 전달해준다. 성공했을 경우 PromiseStatus는 "resolved"이고 PromiseValue는 response가 된다. 실패했을 경우 PromiseStatus는 "rejected"가 되고 에러를 return한다.(=fetch()는 성공하든 실패하든 Promise 객체를 return한다.)
  • 이 경우 가져오는데 성공했으므로 PromiseValue에 response가 담긴 Promise 객체를 반환했다. 이 결과값에 .then()을 하면 다시 한 번 Promise 객체를 반환하게 되는데, .then()은 Promise가 성공했을 때 수행할 콜백함수와 실패했을 때 수행할 콜백함수 두개를 인자로 받는 Promise의 메소드 중 하나이다.
  • 위에서 fetch()를 통해 API에서 요청을 가져오는데에 성공했다는 응답(response)을 서버로부터 받았으므로, 첫번째 .then() 안에는 성공했을 때 수행할 콜백 함수를 넣어준다. fetch()를 통해 받은 Promise 객체의 response를 클라이언트가 읽을 수 있는 Javascript 객체로 변환하기 위해 response.json()을 리턴하도록 했다.

    (위에서 설명했듯이, 클라이언트는 서버로부터 정보를 받으면 JSON.parse()를 통해 다시 Javascript 객체로 변환하는 과정이 필요하다. response.json()은 이를 수행하는 과정이다.)
    .json()은 JSON을 이용해서 텍스트를 Javascript 객체로 parse한 결과를 담은 Promise 객체를 다시 반환한다.
  • Javascript 객체로 parse된 결과가 PromiseValue에 배열의 형태로 담긴 Promise 객체가 리턴됐다. 이 배열이 바로 우리가 서버에 요청(request)했을 때 응답(response)받기를 원했던 결과물이다. 이제 결과물을 얻었으므로, 마지막으로 .then() 안에서 응답받은 데이터를 활용할 방식을 콜백 함수로 입력해주면 된다.

이해가 어려울 경우, 위 코드를 크롬 콘솔에 한 줄 한 줄 쳐보면서 어떤 결과가 리턴되는지 살펴보면 된다. 위 방식을 통해, 우리는 웹페이지 전체를 reloading 시키지 않고도 원하는 user 정보를 얻게 되었다.

 

이제 프론트엔드 쪽의 큰 그림을 그릴 수 있다. 기본적으로는 HTML, CSS, Javascript를 기반으로 클라이언트 쪽을 다룰 수 있는데, 여기에 React 같은 라이브러리를 활용해서 순수하게 HTML, CSS, Javascript 만을 활용할 때보다 더 간편하고 효율적으로 클라이언트를 다룰 수 있다.

여기에 HTTP를 통해 클라이언트와 서버가 소통하면서 웹페이지를 동적으로 만들 수 있는데, AJAX 기술을 더해 클라이언트와 서버의 통신을 비동기적으로 수행함으로써 웹페이지를 여전히 동적이면서 더 효율적으로 작동되도록 하는 방법을 알았다.

 

 

4. Promises

Promise is an object that may produce a single value some time in the future. Either a resolved value or a reason that it's not resolve.(rejected)

Promise에 대한 정의를 딱 한 문장으로 한글로 정의하기 위해서 이리저리 찾아보았는데, 결국 Andrei가 정의해 준 위의 영문 정의가 가장 정확한 정의 같다. Promise는 어떤 결과값을 return하지 않고, 대신 promise를 반환해서 미래의 어떤 시점에 결과를 제공한다. 그리고 Promise는 대기(pending), 이행(fulfilled), 거부(rejected) 세가지 중 하나의 상태를 가진다.

 

전부 다 정리하기는 내용도 많고 다 이해를 못 한 부분도 있어서,, MDN 문서를 참고하자.

MDN Promise 

 

Promise가 왜 유용한지 알려면, 그 이전에 콜백을 알아야 한다. 콜백을 사용해서 "A가 처리되면 B를 처리하고, B가 처리되면 C를 처리해줘"와 같은 비동기적인 명령을 입력하면 pyramid of doom, 콜백 지옥에 빠지게 된다. 이 때 ES6에 새롭게 등장한 Promise를 사용하면 콜백과 마찬가지로 비동기적으로 명령하지만 훨씬 시각적으로 간단하게 구현되도록 해준다.(아래 참고)

// callback pyramid of doom
movePlayer(100, 'Left', function() {
  movePlayer(400, 'Left', function() {
    movePlayer(10, 'Right', function() {
      movePlayer(330, 'Left', function() {
      });
		});
	});
});

// use promise
movePlayer(100, 'Left')
	.then(() => movePlayer(400, 'Left'))
	.then(() => movePlayer(10, 'Right'))
	.then(() => movePlayer(330, 'Left'))

 

Promise를 이해하기 위해 직접 실습해보자.

const test1 = new Promise((resolve, reject) => {
  if (array.length > 10) {
    resolve('Stuff Worked');
  } else {
    reject('Error, it broke');
  }
})

test1.then(result => result + '!'); 
// Promise {<resolved>: "Stuff Worked!"}가 return 된다.

 

Promise는 new Promise() 키워드로 생성하는데, Promise의 상태가 resolve(fulfilled) 일 때 어떤 작업을 수행할지, 반대로 Promise의 상태가 reject일 때 어떤 작업을 수행할지를 지정해주어야 한다. 위의 경우, array의 길이가 10을 초과할 경우에 PromiseStatus에는 "resolved"가 값으로, PromiseValue에는 "Stuff Worked"가 값으로 할당된 Promise 객체가 변수 test1에 할당될 것이다. 만약 array의 길이가 10 이하일 경우에는 PromiseStatus의 값은 "rejected", PromiseValue의 값은 "Error, it broke"가 할당된 Promise 객체가 변수 test1에 할당되는 것이다.

 

만약 array의 길이가 11이라서 변수 test1에 resolved된 Promise가 담겨있다고 가정해보자. test1.then(result => result + '!'); 의 경우, 대상이 되는 test1의 PromiseStatus가 "resolved" 일 때 PromiseValue에 할당된 값에 '!'를 더하라고 명령을 내리고 있다. 따라서 test1.then()을 수행하면 Promise {<resolved>: "Stuff Worked!"} 객체가 리턴되는 것이다.(.then() 또한 Promise를 리턴한다고 위에서 기술해 놓았다.)

 

# .catch()

.catch()를 이해하기 위해 아래 코드를 뜯어보자.

test1
	.then(result => result + '!')
	.then(result2 => {
		throw Error;
		console.log(result2);
	})
	.catch(() => console.log('Oops! Error!'));
// Oops! Error!

 

 

두번째 .then()에서 throw Error를 하였는데, Promise를 수행하는 과정에서 error가 나도록 한 것이다. Promise가 수행되다가 throw Error를 만나면 그 아래의 코드는 수행되지 않고 그 즉시 빠져나와 .catch() 안으로 들어간다. 즉, 에러가 발생될 경우, Promise를 resolve하는 동작을 중지하고, reject한 후(PromiseValue는 "rejected"가 된다) .catch() 내에서 수행하라고 한 동작을 수행한다.

promise
	.then(result => result + '!')
	.then(result2 => result2 + '?')
	.catch(() => console.log('Oops! Error!'))
	.then(result3 => {
  	throw Error;
	  console.log(result3 + '!')
	});
// Uncaught (in promise) ƒ Error() { [native code] }

 

위 코드의 경우, 첫번째 두번째 .then()에는 에러가 없기 때문에 .catch()가 어떤 오류도 잡아내지 못했다. 즉 .catch()는 그 이전에 발생한 .then() 체인에서 오류가 발생했을 경우에 대해서만 오류를 잡아내는 역할을 한다. 그 이후에 발생한 에러에 대해서는 대처하지 못한다.

 

# Promise는 왜 좋을까? 언제 좋을까?

Promise는 비동기적인 자바스크립트 프로그래밍, 즉 AJAX 기술을 활용할 때(ex. API call을 만들거나, 데이터베이스에서 데이터를 불러오는 등의 작업을 할 때) 백그라운드에서 해당 기능이 구동되도록 하는 등의 방식을 활용할 때 매우 유용하다.

이해를 더하기 위해 다른 예제도 살펴보자.

const promise = new Promise((resolve, reject) => {
  if (true) {
    resolve('Stuff Worked');
  } else {
    reject.apply('Error, it broke');
  }
})

const promise2 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'HIIII');
})

const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 1000, 'POOOKIE');
})

const promise4 = new Promise((resolve, reject) => {
  setTimeout(resolve, 5000, 'Is it me you are looking for?');
})

// Promise 실행하기
Promise.all([promise, promise2, promise3, promise4])
  .then(values => {
    console.log(values);
  })
// ["Stuff Worked", "HIIII", "POOOKIE", "Is it me you are looking for?"]

 

위 코드를 실행하면, 5초 뒤에 ["Stuff Worked", "HIIII", "POOOKIE", "Is it me you are looking for?"] 가 콘솔에 출력된다. Promise.all()의 경우 주어진 모든 프로미스가 이행된 후 이행하는 프로미스를 반환하기 때문에, setTimeout이 5초보다 작은 변수 promise, promise2, promise3가 있음에도 최종적으로는 5초 뒤에 콘솔에 출력이 된다.

const urls = [
  'https://jsonplaceholder.typicode.com/users',
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/albums'
]

Promise.all(urls.map(url => {
  return fetch(url)
  	/* 
  	Promise {<pending>}
    __proto__: Promise
    [[PromiseStatus]]: "resolved"
    [[PromiseValue]]: Array(3)
    0: Response {type: "cors", url: "https://jsonplaceholder.typicode.com/users", redirected: false, status: 200, ok: true, …}
    1: Response {type: "cors", url: "https://jsonplaceholder.typicode.com/posts", redirected: false, status: 200, ok: true, …}
    2: Response {type: "cors", url: "https://jsonplaceholder.typicode.com/albums", redirected: false, status: 200, ok: true, …} 
    */
    .then(resp => resp.json())
		}))
		/*
		Promise {<pending>}
    __proto__: Promise
    [[PromiseStatus]]: "resolved"
    [[PromiseValue]]: Array(3)
    0: (10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    1: (100) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
    2: (100) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…},
		*/
	  .then(results => {
      console.log(results[0]);
      console.log(results[1]);
      console.log(results[2]);
		})
		/* 
		Promise {<pending>}
		__proto__: Promise
		[[PromiseStatus]]: "resolved"
		[[PromiseValue]]: undefined
		(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
		(100) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
		(100) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
		*/

 

  • .fetch() : 첫번째 .fetch()를 하면 웹 API에서 불러올 정보를 서버에 요청(request)하게 되고, 웹 서버는 그에 대한 결과를 응답(response)해준다. 응답(response)이 값으로 담긴 Promise가 리턴된다.
  • .then(resp => resp.json()) : 서버에서 전달 준 응답(response)을 클라이언트가 해석하기 위해서는 JSON을 이용하여 Javascript 객체로 parse하는 과정이 필요하기 때문에 .then(resp => resp.json())이 필요하다. 응답(response)을 Javascript 객체로 변환한 값이 담긴 Promise가 리턴된다.
  • 두번째 .then() : 이제 리턴된 값을 자유자재로 클라이언트 단에서 이용하기 위해서 .then()을 한 번 더 해준다. 위 예제에서는 그냥 console.log()를 했기 때문에 불러온 결과값을 콘솔에 출력하기만 했다.

만일 Promise로 실행하려는 대상에 오류가 있다면? 이 경우 promise는 resolve되지 못하고 reject된다. .catch()를 통해 발생한 error를 잡아내고 특정 동작을 실행할 수 있다.

const urls = [
  'https://jsonplaceholde.typicode.com/users', // 주소 중 일부에 오타가 있는 오류 발생!
  'https://jsonplaceholder.typicode.com/posts',
  'https://jsonplaceholder.typicode.com/albums'
]

Promise.all(urls.map(url => {
  return fetch(url)
    .then(resp => resp.json())}))
    .then(results => {
      console.log('users', results[0]);
      console.log('posts', results[1]);
      console.log('albums', results[2]);
    })
    .catch(() => console.log('error'));

 

'Javascript 공부 > Zero To Mastery(-)' 카테고리의 다른 글

(18) 백엔드 기본  (0) 2019.08.27
(17) HTTP/JSON/AJAX + 비동기적 자바스크립트 2  (0) 2019.08.27
(15) 리액트  (0) 2019.08.27
(14) NPM + NPM Scripts  (0) 2019.08.27
(13) 커맨드 라인, 깃, 깃허브  (0) 2019.08.27

댓글