1) Promise
1-1. 이상한 객체
우리가 XMLHttpRequest 를 통해서 외부 API로부터 여러 과정을 거쳐서 데이터를 얻을 수 있었는데,
사실 fetch라는 함수를 통해서 쉽게 데이터를 얻을 수 있습니다.
다음은 Mozilla 사이트에서 설명된 내용입니다.
Fetch API를 이용하면 Request나 Response와 같은 HTTP의 파이프라인을 구성하는 요소를 조작하는것이 가능합니다. 또한
fetch()
메서드를 이용하는 것으로 비동기 네트워크 통신을 알기쉽게 기술할 수 있습니다.이전에 이러한 기능을
XMLHttpRequest
에서 제공하고 있었습니다. Fetch는 이러한 API의 대체제로Service Workers
같은 기술로 간단히 이용하는것이 가능합니다. 또한 CORS나 HTTP확장같은 HTTP에 관련한 개념을 모아 정의하고 있습니다.
const URL = "https://jsonplaceholder.typicode.com/posts/"
const res = fetch(URL)
console.log(res)
그런데 실행을 해보면 이상한게 나옵니다.
이제 이 이상한 Promise에 대해서 알아보도록 하겠습니다.
1-2. Promise 란?
Promise는 어떤 객체입니다. 설명서를 보면,
Promise(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void): Promise
A callback used to initialize the promise. This callback is passed two arguments: a resolve callback used to resolve the promise with a value or the result of another promise, and a reject callback used to reject the promise with a provided reason or error.
Creates a new Promise.
- Promise ( executor ) : executor라는 callback을 가짐.
- executor ( resolve, reject ) : resolve와 reject라는 callback을 가짐.
- resolve : promise 객체의 결과를 가져오기 위한 callback 함수.
- reject callback : promise 객체의 에러를 가져오기 위한 callback 함수
- executor ( resolve, reject ) : resolve와 reject라는 callback을 가짐.
어찌됬건 두개의 callback function을 지정해줘야 하니까 이런 식으로 짤 수 있습니다.
const orderCoffee = (order) => new Promise( (resolve, reject) => {
if (order === undefined) {
reject('주문에 실패함') // 주문실패할때 reject 실행
} else {
setTimeout( () => {
let coffee = order
resolve(coffee) // 주문성공할때 resolve 실행
}, 1000)
}
})
사실 Promise가 리턴되는건 당연합니다. orderCoffee는 order를 인자로 받고 Promise를 반환하는 함수니까요.
그리고, Promise를 잘 보면 “아아” 라는 값을 갖고있음을 알 수 있습니다.
resolve 함수의 인자로 들어가는 coffee 변수의 값을 Promise는 알고 있는 것이죠.
반환된 Promise 객체에서 이 값을 꺼내기 위해서는 then이라는 메서드가 필요합니다.
1-3. then
then의 설명서를 보면,
then(onfulfilled?: (value: any) => any, onrejected?: (reason: any) => PromiseLike
): Promise The callback to execute when the Promise is resolved.
Attaches callbacks for the resolution and/or rejection of the Promise.
@returns — A Promise for the completion of which ever callback is executed.
Promise가 resolve 되었을 때 실행하기 위한 callback fucntion입니다.
그러니까 resolve 함수를 실행하는 것 대신에 then이라는 메서드로 편하게 할 수 있는 것이죠.
예를 들어서,
const resolve = (coffee) => {
console.log(coffee)
}
이렇게 받은 coffee를 resolve함수를 통해서 출력해주는 로직을 짜고 싶다면,
orderCoffee("아아").then( (coffee) => {
console.log(coffee)
})
이렇게 로직을 그대로 then에 넣어주기만 하면 됩니다.
그러면 then 함수가 작동을 해서 “아아” 가 출력된 것을 알 수 있습니다.
물론 전체적인 함수의 리턴값도 Promise 객체인데, 이 Promise의 [[PromiseValue]]
값을 return값으로 관리할 수 있습니다.
그래서 이걸 다시 then을 통해서 출력할 수 있습니다.
1-4. catch
catch도 then과 비슷하게 reject를 실행하는 함수입니다. 결과값은 매우 비슷합니다.
1-5. fetch에 적용해보기.
const URL = 'https://koreanjson.com/posts/1'
const res = fetch(URL)
const res2 = res.then((response) => {
return response.json()
})
const res3 = res2.then((jsonObj) => {
console.log(jsonObj)
})
res => Promise : [[PromiseValue]] : response
res2 => Promise : [[PromiseValue]] : Object
res3 => res2에 저장된 Object의 값을 then을 통해서 출력하기.
유의해야할 점은 response.json() 메서드 그 자체도 비동기적으로 작동하기 때문에
const res2 = res.then((response) => {
console.log(response.json())
return response.json()
})
이라고 하면, 출력을 위해서 response.json()를 실컷 하고 있는데, 다시 return구문에서 비동기적인 행동을 해서 출력을 하라니까 오류를 출력한다.
2) Async - await
2-1. 직관적으로 코드를 적고 싶다.
const coffee = orderCoffee('따아')
console.log(coffee)
이게 가장 직관적인 방법이기 때문에 이런 방식을 추구하고 싶습니다.
2-2. 해결방법
-
우리의 로직을 함수 안에 넣어놓습니다.
const getCoffee = (order) => { const coffee = orderCoffee(order) console.log(coffee) }
-
async 라는 표현을 통해서
이 getCoffee라는 함수는 비동기적으로 처리한다는 로직이 내부에 있을 것입니다
라는 선언을 해주는 것입니다.const getCoffee = async (order) => { const coffee = orderCoffee(order) console.log(coffee) }
-
그리고나서 비동기적으로 실행되는 함수 앞에 await를 붙인다.
await는
이 함수가 실행이 완료될때 까지 기다려
라는 뜻입니다.const getCoffee = async (order) => { const coffee = await orderCoffee(order) console.log(coffee) }
그렇게 실행을 해보면, 성공적으로 우리가 원하는 결과가 나왔습니다.
async 함수의 결과물은 Promise객체이기 때문에 Promise객체도 같이 출력됩니다.
2-3. 요약
const getCoffee = async (order) => {
const coffee = await orderCoffee(order)
}
const coffee = orderCoffee(order).then( coffee => coffee )
다음 두 coffee 객체는 사실상 똑같습니다.
그러니까 orderCoffee는 Promise 객체를 반환하는데 await 키워드를 쓴다면 then을 체인하는 것보다 더 가독성이 좋기 때문에 이것을 쓴다고 생각하면 될 것 같습니다.
2-4. reject는 어떻게 처리?
에러가 났을 때는 파이썬의 try - except 처럼 try - catch 구문을 통해서 반환할 수 있습니다.
const getCoffee = async (order) => {
try {
const coffee = await orderCoffee(order)
console.log(coffee)
} catch (error) {
console.log(error)
}
}
이 error는 역시 Promise의 reject에 전달된 인자를 반환합니다.
2-5. fetch에 적용해보기
const URL = 'https://koreanjson.com/posts/1'
const getPost = async (URL) => {
const res = await fetch(URL)
const res2 = await res.json()
// res.json도 비동기적인 함수임을 유의하자!
console.log(res2)
}
getPost(URL)