Lsiron

Callback, promise, async / await 본문

언어/Java Script

Callback, promise, async / await

Lsiron 2024. 5. 17. 01:35

JS는 어느 한 순간에 동시에 단 하나의 작업만 실행 할 수 있다.

 

예를들어, 세탁기 돌리는데 20분, 청소하는데 10분, 세수하는데 5분이라면 세탁기를 돌려놓고 세수하고 청소하는게 아닌, 세탁기가 다 돌아갈때 까지 기다렸다가 끝나면 청소하고 청소 끝나면 세수하는것과 같은 방식으로 실행한다.

이러한 과정을 동기 프로그래밍이라 한다.(Sync Programming) 이러한 비효율적인 동작을 위해 비동기 프로그래밍이 나왔다.(Async Programming)

 

Javascript는 Single Threaded 이며, 하나의 Thread엔 하나의 Memory Heap 과 하나의 Call Stack이 있다.

동기 함수의 경우, Call Stack에 하나하나 쌓이게 되지만, 비동기 함수의 경우 Call Stack과 따로 분리된 Task Queue(Event Queue)로 옮겨진 후에 쌓인다.

즉, Call Stack에서 동기함수를 처리하는 동안 Task Queue에선 따로 비동기함수를 처리하는 중인 것.

 

 

Event Loop?!

Frontend의 경우 면접질문 100퍼센트 

최근 추세는 비동기, 동기 코드가 출력이 될때 이벤트 루프를 거쳐 출력되는 과정 및, 그 순서에 대한 해답을 묻는 질문이 많이 나온다.

Event Loop 는 JS RunTime이 생성되는 순간에 Event Loop가 생성이 된다.

Event Loop는 Task Queue를 계속 바라보면서 이 안에서 실행이 종료가 된 함수가 있는지 계속 확인을 하는 동시에 Call Stack이 비워져 있는지 계속 확인을 한다.

Task Queue 안에 있는 함수가 종료가 됐을때(즉, 비동기 작업이 끝났을때) 동기함수를 처리하는 Call Stack이 비워져있으면 이 Event Loop가  종료된 함수를 다시 Call Stack으로 옮긴다. 이때 이 함수가 동기로 실행해야할 나머지 작업을 실행하고 실행이 완료되면 종료가 된다.

/**
 * Async theory
 */
function longWork(){ //2초가 걸리는 작업
  const now = new Date();

  /**
   * milliseconds since epoch
   * 1970년도 1월 1일부터 지금 코드가 실행되는 순간까지의 시간을
   * 밀리초로 반환한다.
   */
  const milliseconds = now.getTime();
  const afterTwoSeconds = milliseconds + 2 * 1000;

  while(new Date().getTime() < afterTwoSeconds) {

  }

  console.log('완료');
}

console.log('hello');
longWork();
console.log('World');
 
/**hello
 * 완료
 * World
 * 가 출력된다.
 */

 

=> longWork가 실행완료된 2초뒤에 'World'가 출력되는것을 알 수 있다.

 

위 작업을 비동기로 한번 바꾸어주겠다.

function longWork(){
  setTimeout(()=>{
    console.log('완료');
  }, 2000);
}

console.log('Hello');
longWork();
console.log('World');
/**hello
 * World
 * 완료
 * 가 출력된다.
 */

 

=> 2초뒤에 longWork 함수가 실행되는 것은 동기적 프로그래밍과 똑같으나, longWork가 실행되는 동안 'World'를 미리 출력해버리고 이 후, longWork 함수의 실행이 완료된다. // Event Loop로 설명하자면, Hello와 World는 콜스택 안에서 미리 처리되고, longWork 함수는 Task Queue로 옮겨진 뒤, 실행이 된다. 

 

1. Callback 함수

다른 함수의 인자로 전달되어, 해당 함수의 실행이 완료되었을 때 호출되는 함수를 말한다. 이를 통해 비동기적인 작업을 처리하거나 함수의 실행 순서를 제어할 수 있다. 즉, 특정시간이 지난 후에 다시 불리는 함수이다.

콜백함수를 제대로 이해해야 이 다음에 오는 promise, async/await에 대해 이해 및 활용을 할 수 있다. 대충 넘어가면 어떻게 작용하는지 조차 모른다.

 

콜백 함수를 사용한 예시를 살펴보자.

아래의 코드는 getUserData 함수를 호출할 때 콜백 함수를 전달하여 데이터를 가져온 후에 해당 데이터를 출력하는 예시이다.

// 비동기적으로 사용자 데이터를 가져오는 함수
function getUserData(callback) {
  setTimeout(() => {
      const userData = {
          id: 1,
          name: 'Lsiron',
          email: 'lsiron@example.com'
      };
      callback(userData); // 콜백 함수 호출  
  }, 2000); // 2초 후에 실행
}

// getUserData 함수 호출 시 콜백 함수 전달
getUserData(function(data) {
  console.log('사용자 데이터:', data);    // 사용자 데이터: {id: 1, name: 'Lsiron', email: 'lsiron@example.com'} 출력
}); 
  1. getUserData 함수가 호출되면, setTimeout 함수가 실행된다. setTimeout 함수는 2초 후에 콜백 함수 내부의 코드를 실행하도록 예약된다.
  2. 2초가 지난 후, setTimeout 함수 내부의 코드가 실행된다. userData 객체가 생성되고, 그 안에 사용자 정보가 담겨 있다.(id: 1, name: ‘Lsiron’, email: ‘lsiron@example.com’).
  3. callback(userData) 코드가 실행되어 콜백 함수가 호출된다. 이때, userData 객체가 콜백 함수의 매개변수 data로 전달된다.
  4. 콜백 함수 내부의 코드가 실행된다. 전달받은 data를 출력하는 console.log('사용자 데이터:', data); 코드가 실행되어 “사용자 데이터: { id: 1, name: ‘Lsiron’, email: ‘lsiron@example.com’ }”가 콘솔에 출력된다.

내 기준 이해가 안됐던 getUserDate의 인자 function(data) .. 왜 인자로 저걸 넣어야 하는지 AI 에 물어봤더니 답변이 이렇다.

 

Q : 왜 getUserData의 인자로 function(data)를 넣어줬는지 이해가 안돼

A : getUserData 함수의 인자로 function(data)를 넣어준 이유는 비동기 처리를 위한 콜백 함수를 전달하기 위해서입니다.

setTimeout 함수를 이용하여 일정 시간이 지난 후에 사용자 데이터를 가져오는 비동기 작업을 수행하고 있습니다. 이때, 데이터를 가져온 후에 해야 할 작업을 콜백 함수로 전달하여 처리하도록 설계되어 있습니다.

따라서 getUserData 함수를 호출할 때, 콜백 함수를 인자로 전달하여 사용자 데이터를 가져온 후에 실행할 코드를 정의하고 있습니다. 이렇게 하면 데이터를 가져온 후에 콜백 함수가 자동으로 실행되어 데이터 처리 작업을 할 수 있게 됩니다.

 

Q : 그냥 data를 인자로 넣는건 안돼?

 

A : 네, 단순히 data를 인자로 전달하는 것만으로는 원하는 결과를 얻을 수 없습니다. 비동기 처리를 위해서는 콜백 함수를 사용해야 합니다. 따라서 function(data)와 같이 콜백 함수를 인자로 전달하여 사용자 데이터를 가져온 후에 실행할 코드를 정의해야 합니다.

 

즉, 애초에 파라미터 자체가 함수 형식으로 되어있기 때문에 함수를 인자로 넣어줘야 한다는 것.

 

그래도 이해가 안 된다. 이걸 더 쉽게 풀어서 설명한다면?

콜백 함수는 다른 함수 안에서 실행이 완료된 후에 실행되는 함수를 말한다.

일종의 “나중에 실행할 함수”라고 생각하면 된다. 예를 들어, A 함수 안에서 B 함수를 호출할 때, B 함수가 실행을 완료한 후에 C 함수를 실행하고 싶다면 C 함수를 B 함수의 인자로 전달하면 된다. 이렇게 하면 B 함수가 실행을 마치고 나서 C 함수가 실행된다. 이게 콜백 함수의 개념이다.

function A(callback) {
  console.log('A 함수 실행');
  setTimeout(() => {
      B(callback);
  }, 2000); // 2초 후에 B 함수 실행
}

function B(callback) {
  console.log('B 함수 실행');
  setTimeout(() => {
      callback();
  }, 1000); // 1초 후에 콜백 함수 실행
}

function C() {
  console.log('C 함수 실행');
}

// A 함수 호출
A(C); // A 함수 실행 출력 후 2초뒤, B 함수 실행 출력 후, 1초뒤 C 함수 실행 출력

 

=> 위 코드에서 A 함수가 호출되면 “A 함수 실행”이 출력되고, 2초 후에 B 함수가 실행된다.

B 함수가 실행되면 “B 함수 실행”이 출력되고, 1초 후에 전달받은 콜백 함수인 C 함수가 실행된다. 따라서 “C 함수 실행”이 출력된다. 이렇게 A 함수가 B 함수를 호출하고, B 함수가 실행을 완료한 후에 C 함수를 실행하는 관계가 함수로 표현되어있다.

그럼 여기에서 왜 C 함수는 함수를 인자로 넣지 않았는가?

function C는 인자를 받지 않는 단순한 함수이기 때문이다. 여기서는 C 함수가 콜백 함수로 활용되는 것이 아니라, 그냥 단순히 실행되는 함수이다. 따라서 C 함수에는 인자를 넣지 않아도 된다. 만약 C 함수를 콜백으로 활용하려면 해당 함수를 호출할 때 인자를 넣어주어야 한다.

 

Q : getUserData(function(data) {console.log(‘사용자 데이터:’, data); });
그러니까 C 함수도 위에 getUserData 처럼 인자 data를 받아서 해당 함수 내부에 작용하는게 목적이라면 함수를 인자로 넣어야한다 이거지?

 

A : 네, 맞습니다. 함수가 다른 함수의 콜백으로 사용될 때는 해당 함수가 인자를 받도록 정의되어야 합니다. 따라서 C 함수가 다른 함수의 콜백으로 사용될 때는 인자를 받아서 그 인자를 활용하도록 정의되어야 합니다. 위의 예시에서 getUserData 함수가 data라는 인자를 콜백 함수로 전달하는 것처럼, C 함수도 필요한 인자를 받아서 해당 함수 내부에서 작용할 수 있도록 구현해야 합니다.

 

이 처럼 콜백함수의 대표적인 함수인, setTimeout 함수가 있다. (번외 : setInterval(x,y) - y밀리초 주기마다 x 실행) 

function waitAndRun(){
  setTimeout(() => {
    console.log('끝');
  }, 2000);
}

waitAndRun(); // 2초 후에 '끝' 이 출력됨.

-콜백지옥-

function waitAndRun2(){
  setTimeout(() => {
    console.log('1번 콜백 끝');
    setTimeout(() => {
      console.log('2번 콜백 끝');
      setTimeout(() => {
        console.log('3번 콜백 끝');
      }, 2000);
    }, 2000);
  }, 2000);
}

waitAndRun2() // 2초후 1번 콜백 끝 다음 2초후 2번 콜백 끝 다음 2초후 3번 콜백 끝 출력

 

=> 콜백지옥은 콜백 함수를 중첩해서 사용하다 보면 코드가 길고 복잡해지는 현상을 말한다.

이러한 현상은 콜백 함수가 비동기적으로 실행되는 JavaScript에서 자주 발생할 수 있다. 예를 들어, 한 비동기 함수가 또 다른 비동기 함수를 호출하고, 그 함수가 또 다른 함수를 호출하는 식으로 계속 중첩되면 코드가 가독성이 떨어지고 유지보수가 어려워진다. 이러한 상황을 콜백지옥이라고 부르며, 이를 해결하기 위해 Promise나 async/await와 같은 비동기 처리 방식을 사용하는 것이 좋다.

2. Promise 함수

Promise 함수는 new 키워드를 사용하여 생성하고 이는 두개의 인자를 받는데 resolve, reject 말고 다른 이름을 써도 된다. 하지만 대표적인것은 resolve, reject 이다. 순서가 제일 중요하다! 이름은 상관없으나, 기능은 동일하다.

Resolve

const timeoutPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('완료');
  }, 2000);
});

timeoutPromise.then((res) => {
    console.log(res); // 2초후, '완료' 출력
});

 

=> resolve는 then 함수와 연결이 된다.

호출을 할 때 timeoutPromise.then 이라고 한 다음, 함수를 넣어주면 이 안에서 하나의 파라미터를 받을 수 있다(res).

이때 이 파라미터가 위의 resolve 함수에서 넣어준 '완료' 값이다.

 

setTimeout을 하고서 2초를 기다린 다음에 '완료'라는 값을 resolve를 했으니까, 이 timeoutPromise.then이라는 함수는 2초 뒤에 첫번째 파라미터(res)에 '완료'라는 값을 넣은 뒤에 then의 콜백함수가 실행된다.

 

즉, 위의 resolve라는 함수를 실행하는 순간에 반환받은 Promise 객체에서 then을 붙이면, resolve라는 함수가 실행되는 시점에 반환값을 받을 수가 있다.

 

이러한 Promise로 무엇을 할 수 있냐면,  Promise를 반환하는 함수를 만들 수 있다.

이를 통해 여러개의 Promise를 연결할 수 있다.

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
    resolve('완료');
  }, seconds * 1000);
});

getPromise(1)
.then((res) => {
  console.log(res) // 1초후에 '완료' 출력
})

 

=> 다양한 딜레이값을 가지는 Promise를 연결시켜주기 위해 딜레이 값을 함수로 받고 파라미터로 second를 넣어준다.

const getPromise = (seconds) => new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('완료');
  }, seconds * 1000);
});

getPromise(1)
.then((res) => {
  console.log(res) // 1초후에 '완료' 출력

  return getPromise(3);
}).then((res) => {
  console.log(res); //3초후에 '완료' 출력

  return getPromise(4);
}).then((res) => {
  console.log(res); //4초후에 '완료' 출력
});

 

=> return 값을 통해 Promise를 연결하여(return getPromise(1)) 위와 같이 계속해서 Chaining을 할 수 있다. 콜백지옥보단 확실하게 직관적임을 알 수있다.

Reject

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
    reject('에러');
  }, seconds * 1000);
});

getPromise(1)
  .then((res) => {
    console.log(res)
  })
  .catch((res) => {
    console.log(res); // 1초후, '에러' 출력
  })

 

=> reject는 Error가 발생 했을때 이다. 이는 catch 함수와 연결이 되는데,

이 말은 즉, resolve를 실행하면 then을 실행 시킬 수 있고, reject를 실행하면 catch를 실행 시킬 수 있다.

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
    if(xxx){
      resolve('성공')
    } else{
      reject('에러');
    }
  }, seconds * 1000);
});

getPromise(1)
  .then((res) => {
    console.log(res)
  })
  .catch((res) => {
    console.log(res); // 1초후, '에러' 출력
  })

 

=> 위와 같이 조건문을 사용하여, resolve인 경우와 reject인 경우를 나누어, 많이 쓰이곤 한다.

Finally

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
      reject('에러');
  }, seconds * 1000);
});

getPromise(1)
  .then((res) => {
    console.log(res)
  })
  .catch((res) => {
    console.log(res); // 1초후, '에러' 출력
  })
  .finally(() => {
    console.log('---finally---'); // 에러 출력과 동시에 ---finally--- 출력
  });

 

=> finally는 콜백함수에 argument를 받지 않는다. finally 같은 경우는 then이 걸리든 catch가 걸리든 무조건 실행이 되는 함수이다.

Tip

프로미스가 서로 의존하고 있지 않다면 동시에 실행을 시켜, 훨씬 더 속도를 빠르게 처리 할 수있다.

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
      reject('에러');
  }, seconds * 1000);
});

Promise.all([
  getPromise(1),
  getPromise(4),
  getPromise(1),
]).then((res) => {
  console.log(res); // 4초후에 ['에러', '에러', '에러'] 출력
})

 

=> 위와 같이 리스트로 한꺼번에 값을 받을 수 있는데 이때, 가장 느린 함수 기준으로 then 또는 catch가 풀린다. 일단 전부 실행은 동시에 된다.

3. Async Await 

현대에 가장 많이 사용되는 비동기 프로그래밍 async 와 await.

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
      resolve('완료');
  }, seconds * 1000);
});

async function runner(){
  const result1 = await getPromise(1); //1은 위 선언된 getPromise 함수의 second 파라미터에 넣는 값임.
  console.log(result1);
}

runner(); // 1초 후, '완료' 출력

 

=> async, await의 예제는 위와 같다.

runner();
console.log('실행 끝');

 

=> 비단, 위와 같이 console.log('실행 끝') 을 아래에 입력해도, '실행 끝'이 먼저 출력되고 딜레이시간이 끝나야 '완료'가 출력된다.

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
      resolve('완료');
  }, seconds * 1000);
});

async function runner(){
  const result1 = await getPromise(1);
  console.log(result1);
  const result2 = await getPromise(4);
  console.log(result2);
  const result3 = await getPromise(1);
  console.log(result3);
}
 
runner(); // 1초후 '완료' 출력, 다음 4초후 '완료' 출력, 다음 1초후 '완료' 출력

 

=> 중복 출력을 하고싶을 경우, 위와 같이 변수를 추가적으로 선언 해 주면 가능하다.

그렇다면 reject는 어떻게 될까? 

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
      reject('에러');
  }, seconds * 1000);
});

async function runner(){
  const result1 = await getPromise(1);
  console.log(result1);
  const result2 = await getPromise(4);
  console.log(result2);
  const result3 = await getPromise(1);
  console.log(result3);
}

runner(); // 에러발생!!

 

=> 위와 같은 async 구조에서 reject값을 넣고 실행을 시키면 에러가 발생한다.

이 때문에 우리는 완전한 문장으로, try catch문을 사용하여 async 함수 속 변수들을 감싸주어야 한다.

const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
      reject('에러');
  }, seconds * 1000);
});

async function runner(){
  try{
    const result1 = await getPromise(1);
    console.log(result1);
    const result2 = await getPromise(4);
    console.log(result2);
    const result3 = await getPromise(1);
    console.log(result3);
  }catch(e){
    console.log(e);
  }
}
runner(); // '에러 ' 출력

 

=> resolve 와 연결된 try문은 출력이 되지않고 reject와 연결된 catch문이 출력이 됨을 알 수있다.

만약 위 상태에서 resolve를 실행 시킨다면 catch문이 아닌, try문이 실행되어, 1초후에 '완료' 다음 4초후에 '완료' 다음 1초후에 '완료' 가 출력이 된다

finally 까지 추가하여, async await의 더 완전한 문장을 만들어보면 다음과 같다.


const getPromise = (seconds) => new Promise((resolve, reject) => { //몇초를 기다릴지 second로 넣는다.
  setTimeout(() => {
      reject('에러');
  }, seconds * 1000);
});

async function runner(){
  try{
    const result1 = await getPromise(1);
    console.log(result1);
    const result2 = await getPromise(4);
    console.log(result2);
    const result3 = await getPromise(1);
    console.log(result3);
  }catch(e){
    console.log(e);
  }finally{
    console.log('---finally---');
  }
}
runner(); // '에러 '
          // '---finally---'출력

 

4. Async Await 파헤치기 

여러가지 비동기 함수의 기초에 대해 알아 보았다. 허나, 이들을 다룰려면 기초로는 터무니없이 부족하다.

먼저 async/ await 사용법부터 알아보자.

async function myAsyncFunction() {
  const result = await myAsyncTask();
  console.log(result); // 반환값 확인
}

myAsyncFunction();

 

=> myAsyncTask() 함수가 Promise를 반환하고 있다고 가정하면, await 키워드를 사용하여 해당 Promise가 처리될 때까지 기다린 후 반환값을 result 변수에 할당하고, 이를 통해 반환값을 확인할 수 있다.

위 예시에서는 반환값을 콘솔에 출력하였지만, 실제로는 반환값을 다른 변수에 할당하거나 필요에 따라 다양한 처리를 할 수 있다.

async function myAsyncFunction() {
  const result = await myAsyncTask();
  return result; // 반환값을 반환
}

async function processResult() {
  try {
      const returnedValue = await myAsyncFunction();
      console.log(returnedValue); // 반환값 확인
      // 반환값을 다른 변수에 할당하거나 필요에 따라 다양한 처리 가능
  } catch (error) {
      console.error('Error:', error);
  }
}

processResult();

 

=> 위 코드에서는 myAsyncFunction 함수가 async 키워드로 선언되어 있고, await를 사용하여 myAsyncTask 함수가 반환하는 Promise를 기다린다.

이후 processResult 함수에서 myAsyncFunction의 반환값을 받아와서 처리한다.

받은 값을 다른 변수에 할당하거나 필요에 따라 다양한 처리를 할 수 있다. 만약 에러가 발생할 경우 catch 블록에서 에러를 처리할 수 있다.

 

async await을 자유롭게 쓰려면 어느 타이밍에 써야하는지 알아야겠다.

위 사례에서 보면 async는 아무 함수에나 붙일 수 있다. 허나, await을 보면 아무 함수엔 쓸 수 없는 것 처럼 보인다.

await은 async 내부에서 promise를 반환 해 주는 함수에만 쓸 수 있으며, 아무 함수에 await를 씌울 순 없다. 

const 나는promise = (seconds) => new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Lsiron');
  }, seconds * 1000);
});

async function checkAwaitPromise() {
  const resultPromise = await 나는promise(1); <==== 나는promise가 프로미스 반환함
  console.log(resultPromise); // 1초후 Lsiron 출력
}

 

=> 즉, 대표적으로 위 처럼 promise를 할당한 함수에 await을 씌우거나,

async function 나는fetch() {
  try {
      const response = await fetch('https://Lsiron.tistory.com'); <==== fetch가 프로미스 반환함
      if (!response.ok) { // 응답을 못 받아서 에러 날 경우 에러 던지기
          throw new Error('Failed to fetch data');
      }
      const data = await response.json(); <==== 번외지만, response.json()도 프로미스 반환함.(fetch에서 날라온 데이터 임)
      console.log(data); // 응답 데이터 확인
      return data; // 반환값으로 데이터 전달
  } catch (error) {
      console.error('Error:', error);
  }
}

 

=> 위 처럼 fetch 함수를 할당한 함수에 await을 씌울 수 있다. 왜냐하면? await을 사용하여 비동기 함수를 호출한 경우, 해당 함수의 반환값을 Promise 객체로 감싸져있기 때문이다.

 

async와 await의 사용법을 알았다고 해서 능수능란하게 사용하긴 무리이다. 예제들을 통해 실력을 길러 나가보자.

 

문제 1) 아래 fetch문을 async / await 형태로 전환하시오.

  method: 'GET',
})
  .then((res) => res.json())
  .then((res) => {
    console.log(res);
  });

 

해답 1)

async function fetchData() {
    try {
        const response = await fetch('https://Lsiron.tistory.com', {
            method: 'GET',
        });
        const data = await response.json();
        console.log(data);
    } catch (error) {
        console.error('Error:', error);
    }
}

fetchData();

 

=> 위 코드에서 fetchData 함수가 async 키워드로 선언되어 있고, fetch 함수를 await를 사용하여 데이터를 받아오고 json 형태로 변환하여 변수에 할당한다. 이후 데이터를 출력한다. 만약 에러가 발생하면 catch 블록에서 에러를 처리한다. fetchData 함수를 호출하여 데이터를 받아와서 처리한다.

 

문제 2)

lsiron2를 완성해보세요. 

문제 1) pathname = "/siron/2" 로 만들어서 GET 요청을 보내보시오. 

문제 2) 응답 결과 (json)를 콘솔 로그에 출력하시오. 

조건 1) fetch는 첫 번째 프로미스를 반드시 .then(res => res.json()) 으로 풀어주어야 한다.

lsiron();

const URL = "https://lsiron.tistory.com";

function lsiron2() {
  fetch(URL)
}

lsiron2()
 

 

해답2)

async function lsiron2() {
  const pathname = "/siron/2";
  const URL = "https://lsiron.tistory.com" + pathname;
  // 프로미스 체이닝만을 사용한 방법
  fetch(URL)
    .then((res) => res.json())
    .then((res) => {
      console.log(res);
    });

  // async-await 방법 1
  const res = await fetch(URL);
  const json = await res.json();
  console.log(json);

  // async-await 방법 2
  const json = await fetch(URL).then((res) => res.json());
  console.log(json);
}

lsiron2();

 

=> 위 코드에서 lsiron2 함수가 async 키워드로 선언되어 있고, pathname 변수를 “/siron/2”로 설정하고 URL 변수에 URL을 조합한다.

fetch 함수를 사용하여 해당 URL로 GET 요청을 보내고, 첫 번째 프로미스를 .then(res => res.json())으로 풀어주어야 한다. 받아온 응답 데이터를 콘솔에 출력하고 lsiron2 함수를 호출하여 실행한다.

 

 

 

 

 

참조: 유튜브 _ 코드팩토리