[JavaScript] 콜백 패턴과 비동기 처리

기본적인 콜백 패턴

function fetchData(url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  
  xhr.onload = () => {
    if (xhr.status === 200) {
      callback(null, xhr.responseText);
    } else {
      callback(new Error(`HTTP 오류 발생: ${xhr.status}`));
    }
  };
  
  xhr.onerror = () => {
    callback(new Error('네트워크 오류가 발생했습니다.'));
  };
  
  xhr.send();
}

// 사용 예시:
fetchData('https://api.example.com/data', (err, data) => {
  if (err) {
    console.error('데이터를 가져오는 중 오류:', err.message);
  } else {
    console.log('데이터를 성공적으로 가져왔습니다:', data);
    // 가져온 데이터를 활용하는 로직
  }
});
  • fetchData 함수는 XMLHttpRequest 객체를 사용하여 지정된 URL로 GET 요청을 보냅니다.
  • 요청이 성공하면 콜백 함수의 첫 번째 인수로 null, 두 번째 인수로 응답 데이터를 전달합니다.
  • 요청에 실패하거나 오류가 발생하면 에러 객체를 콜백의 첫 번째 인수로 전달합니다.
  • 이러한 방식은 에러 우선 콜백(error-first callback) 패턴입니다.

콜백 지옥 (Callback Hell)

비동기 작업이 중첩되면 콜백 함수가 계속해서 중첩되는 현상이 발생할 수 있습니다.

fetchData('https://api.example.com/data1', (err, data1) => {
  if (err) {
    console.error('데이터 1을 가져오는 중 오류:', err.message);
  } else {
    console.log('데이터 1:', data1);
    fetchData('https://api.example.com/data2', (err, data2) => {
      if (err) {
        console.error('데이터 2를 가져오는 중 오류:', err.message);
      } else {
        console.log('데이터 2:', data2);
        fetchData('https://api.example.com/data3', (err, data3) => {
          if (err) {
            console.error('데이터 3을 가져오는 중 오류:', err.message);
          } else {
            console.log('데이터 3:', data3);
            // 모든 데이터를 가져온 후의 로직
            console.log('모든 데이터를 성공적으로 가져왔습니다.');
          }
        });
      }
    });
  }
});
  • 콜백 함수가 여러 단계 중첩되어 코드의 가독성이 떨어지고 유지보수가 어렵습니다.
  • 에러 처리가 복잡해지고, 로직의 흐름을 파악하기 어려워집니다.

콜백 지옥 탈출 – 함수 분리 및 모듈화

function handleData1(err, data1) {
  if (err) {
    return handleError(err);
  }
  console.log('데이터 1:', data1);
  fetchData('https://api.example.com/data2', handleData2);
}

function handleData2(err, data2) {
  if (err) {
    return handleError(err);
  }
  console.log('데이터 2:', data2);
  fetchData('https://api.example.com/data3', handleData3);
}

function handleData3(err, data3) {
  if (err) {
    return handleError(err);
  }
  console.log('데이터 3:', data3);
  // 모든 데이터를 가져온 후의 로직
  console.log('모든 데이터를 성공적으로 가져왔습니다.');
}

function handleError(err) {
  console.error('오류 발생:', err.message);
}

// 시작점
fetchData('https://api.example.com/data1', handleData1);