문제 발견

외부 API 에서 데이터를 받아와서 호출하는 페이지를 만들려고 했다.

버튼을 누르기 전에는 아무것도 렌더링하지 않고, fetch 함수를 호출할 때 데이터를 렌더링하게 만들고 싶었다.

그림으로 나타내면 아래와 같다.

하지만 버튼을 한번 누르면 데이터가 렌더링 되지 않고 두번 이상 눌러야 렌더링 되는 문제가 발생했다.

 


문제 해결

fetch 함수가 promise 객체를 반환하는 함수이기 때문에 발생한 문제였다.

promise 객체는 비동기 처리를 위해 나온 객체이며 코드 실행 순서는 순서대로 진행하다가

비동기 함수(promise 객체를 반환하는 함수)가 실행되면 즉시 다음 코드로 실행 흐름이 넘어간 후 나중에 콜백이 실행된다.

console.log('Start');

fetch(url)
  .then((response) => response.json())
  .then((result) => { console.log("fetch"); });

console.log('End');

 

위의 코드는 동기 실행의 경우 아래처럼 출력 됐을 것이다.

 

  1. console.log("start") 출력
  2. fetch 함수를 실행하여 해당 url로 요청
  3. fetch의 응답값을 받고 then 메서드로 콜백 수행하여 consol.log("fetch") 출력
  4. console.log("End") 출력

하지만 비동기 실행의 경우 아래처럼 동작한다.

 

  1. console.log("start") 출력
  2. fetch 함수를 실행하여 해당 url로 요청
  3. console.log("End") 출력
  4. fetch의 응답값을 받고 then 메서드로 콜백 수행하여 console.log("fetch") 출력

변경 전

const url = "https://api-blue-archive.vercel.app/api/characters?page=2";
const targetArea = document.querySelector('.targetArea');
const imageArea = document.querySelector('.imageArea');
const btn = document.querySelector('.magicBtn');

let bindedData = {};

getData = function () {
    fetch(url, {
        mode: 'cors'})
        .then((response) => response.json())
        .then((data) => {
            console.log("fetch finished");
            return bindedData = data.data;
        });
    imageArea.innerHTML = '';
    console.log("rendering start");
    console.log(bindedData);
    for (let i = 0; i < 20; i++) {
        const student = document.createElement('div');
        const name = document.createElement('div');
        name.textContent = bindedData[i].name;
        student.append(name);
        const image = document.createElement('img');
        image.setAttribute('src',bindedData[i].photoUrl);
        student.append(image);
        imageArea.append(student);
    }
}

btn.addEventListener('click', getData);

콘솔창은 아래와 같다. (버튼 두번 눌러야 원하는 응답이 온다. 빨간 줄 기준으로 1번째 2번째 클릭 구분)

 

변경 후

const url = "https://api-blue-archive.vercel.app/api/characters?page=2";
const targetArea = document.querySelector('.targetArea');
const imageArea = document.querySelector('.imageArea');
const btn = document.querySelector('.magicBtn');

let bindedData = {};

getData = function () {
    fetch(url, {
        mode: 'cors'})
        .then((response) => response.json())
        .then((data) => {
            console.log("fetch finished");
            imageArea.innerHTML = '';
            bindedData = data.data;
            
            console.log("rendering start");
            console.log(bindedData);
            for (let i = 0; i < 20; i++) {
                const student = document.createElement('div');
                const name = document.createElement('div');
                name.textContent = bindedData[i].name;
                student.append(name);
                const image = document.createElement('img');
                image.setAttribute('src',bindedData[i].photoUrl);
                student.append(image);
                imageArea.append(student);
            }
        });
}

btn.addEventListener('click', getData);

콘솔창은 아래와 같다. (버튼 한번만 눌러도 원하는 응답을 받을 수 있다.)

 

+ Recent posts