logo

JavaScript의 콜백 지옥

JavaScript는 비동기(비차단) 단일 스레드 프로그래밍 언어입니다. 즉, 한 번에 하나의 프로세스만 실행할 수 있습니다.

프로그래밍 언어에서 콜백 지옥은 일반적으로 비동기 호출로 코드를 작성하는 비효율적인 방법을 나타냅니다. 파멸의 피라미드라고도 알려져 있습니다.

자바스크립트에서 콜백 지옥은 중첩된 콜백 함수가 과도하게 실행되는 상황을 말한다. 코드 가독성과 유지 관리가 줄어듭니다. 콜백 지옥 상황은 일반적으로 여러 API 요청을 생성하거나 복잡한 종속성이 있는 이벤트를 처리하는 등 비동기 요청 작업을 처리할 때 발생합니다.

JavaScript의 콜백 지옥을 더 잘 이해하려면 먼저 JavaScript의 콜백과 이벤트 루프를 이해하세요.

JavaScript의 콜백

JavaScript는 문자열, 배열, 함수 등 모든 것을 객체로 간주합니다. 따라서 콜백 개념을 사용하면 함수를 다른 함수의 인수로 전달할 수 있습니다. 콜백 함수가 먼저 실행을 완료하고 상위 함수가 나중에 실행됩니다.

콜백 함수는 비동기적으로 실행되며 비동기 작업이 완료될 때까지 기다리지 않고 코드가 계속 실행될 수 있도록 합니다. 여러 비동기 작업이 결합되고 각 작업이 이전 작업에 종속되면 코드 구조가 복잡해집니다.

콜백의 용도와 중요성을 이해해 보겠습니다. 세 개의 매개변수(문자열 하나와 숫자 두 개)를 취하는 함수가 있는 예를 가정해 보겠습니다. 여러 조건이 포함된 문자열 텍스트를 기반으로 일부 출력을 원합니다.

아래 예를 고려하십시오.

 function expectedResult(action, x, y){ if(action === 'add'){ return x+y }else if(action === 'subtract'){ return x-y } } console.log(expectedResult('add',20,10)) console.log(expectedResult('subtract',30,10)) 

산출:

 30 20 

위의 코드는 잘 작동하지만 코드를 확장 가능하게 만들려면 더 많은 작업을 추가해야 합니다. 조건문의 수도 계속 증가하므로 최적화하고 읽을 수 있어야 하는 지저분한 코드 구조가 발생합니다.

따라서 다음과 같이 코드를 더 나은 방식으로 다시 작성할 수 있습니다.

 function add(x,y){ return x+y } function subtract(x,y){ return x-y } function expectedResult(callBack, x, y){ return callBack(x,y) } console.log(expectedResult(add, 20, 10)) console.log(expectedResult(subtract, 30, 10)) 

산출:

 30 20 

그래도 출력은 동일합니다. 그러나 위의 예에서는 별도의 함수 본문을 정의하고 해당 함수를 예상 결과 함수에 콜백 함수로 전달했습니다. 따라서 예상되는 결과의 기능을 확장하여 다른 작업으로 또 다른 기능 본체를 만들어 콜백 함수로 사용하려는 경우 코드를 더 쉽게 이해하고 가독성을 높일 수 있습니다.

지원되는 JavaScript 기능에서 사용할 수 있는 콜백의 다른 예가 있습니다. 몇 가지 일반적인 예로는 이벤트 리스너와 맵, 축소, 필터 등과 같은 배열 함수가 있습니다.

이를 더 잘 이해하려면 JavaScript의 값에 의한 전달과 참조에 의한 전달을 이해해야 합니다.

JavaScript는 기본 데이터 유형과 기본이 아닌 데이터 유형의 두 가지 유형을 지원합니다. 기본 데이터 유형은 정의되지 않음, null, 문자열 및 부울이며 변경할 수 없거나 비교적 불변이라고 말할 수 있습니다. 비원시 데이터 유형은 변경되거나 변경될 수 있는 배열, 함수 및 객체입니다.

참조로 전달은 함수가 인수로 사용될 수 있는 것처럼 엔터티의 참조 주소를 전달합니다. 따라서 해당 함수 내의 값이 변경되면 함수 외부에서 사용할 수 있는 원래 값도 변경됩니다.

이에 비해 값별 전달 개념은 함수 본문 외부에서 사용할 수 있는 원래 값을 변경하지 않습니다. 대신 메모리를 사용하여 값을 두 개의 다른 위치에 복사합니다. JavaScript는 참조로 모든 객체를 식별했습니다.

JavaScript에서 addEventListener는 click, mouseover 및 mouseout과 같은 이벤트를 수신하고 이벤트가 트리거되면 실행될 함수로 두 번째 인수를 사용합니다. 이 함수는 참조 개념에 의한 전달로 사용되며 괄호 없이 사용하여 전달됩니다.

아래 예를 고려하십시오. 이 예에서는 콜백 함수인 addEventListener에 Greeting 함수를 인수로 전달했습니다. 클릭 이벤트가 트리거되면 호출됩니다.

테스트.html:

 Javascript Callback Example <h3>Javascript Callback</h3> Click Here to Console const button = document.getElementById(&apos;btn&apos;); const greet=()=&gt;{ console.log(&apos;Hello, How are you?&apos;) } button.addEventListener(&apos;click&apos;, greet) 

산출:

JavaScript의 콜백 지옥

위의 예에서는 콜백 함수인 addEventListener에 Greeting 함수를 인수로 전달했습니다. 클릭 이벤트가 트리거되면 호출됩니다.

마찬가지로 필터도 콜백 함수의 한 예입니다. 필터를 사용하여 배열을 반복하는 경우 배열 데이터를 처리하기 위한 인수로 다른 콜백 함수를 사용합니다. 아래 예를 고려하십시오. 이 예에서는 더 큰 함수를 사용하여 배열에서 5보다 큰 숫자를 인쇄합니다. 필터 메소드에서 콜백 함수로 isGreater 함수를 사용하고 있습니다.

 const arr = [3,10,6,7] const isGreater = num =&gt; num &gt; 5 console.log(arr.filter(isGreater)) 

산출:

 [ 10, 6, 7 ] 

위의 예에서는 필터 메소드에서 더 큰 함수를 콜백 함수로 사용하는 것을 보여줍니다.

JavaScript의 콜백, 이벤트 루프를 더 잘 이해하기 위해 동기 및 비동기 JavaScript에 대해 논의해 보겠습니다.

동기식 자바스크립트

동기식 프로그래밍 언어의 기능이 무엇인지 이해합시다. 동기 프로그래밍에는 다음과 같은 기능이 있습니다.

실행 차단: 동기 프로그래밍 언어는 기존 명령문이 실행될 후속 명령문의 실행을 차단하는 차단 실행 기술을 지원합니다. 따라서 명령문의 예측 가능하고 결정론적인 실행이 달성됩니다.

순차적 흐름: 동기식 프로그래밍은 순차적 실행 흐름을 지원합니다. 즉, 각 문이 순차적인 방식으로 실행된다는 의미입니다. 언어 프로그램은 다음 명령문으로 이동하기 전에 명령문이 완료될 때까지 기다립니다.

간단: 동기식 프로그래밍은 실행 흐름의 순서를 예측할 수 있기 때문에 이해하기 쉬운 것으로 간주되는 경우가 많습니다. 일반적으로 선형적이고 예측하기 쉽습니다. 소규모 애플리케이션은 중요한 작업 순서를 처리할 수 있기 때문에 이러한 언어로 개발하는 것이 좋습니다.

직접적인 오류 처리: 동기식 프로그래밍 언어에서는 오류 처리가 매우 쉽습니다. 명령문이 실행될 때 오류가 발생하면 오류가 발생하고 프로그램이 이를 포착할 수 있습니다.

간단히 말해서 동기 프로그래밍에는 두 가지 핵심 기능이 있습니다. 즉, 한 번에 하나의 작업이 실행되고 다음 작업 세트는 현재 작업이 완료된 후에만 처리됩니다. 따라서 순차적 코드 실행을 따릅니다.

명령문이 실행될 때 프로그래밍의 이러한 동작, 언어는 각 작업이 이전 작업이 완료될 때까지 기다려야 하기 때문에 블록 코드 상황을 만듭니다.

그러나 사람들이 JavaScript에 관해 이야기할 때 동기식인지 비동기식인지에 대한 대답은 항상 헷갈립니다.

위에서 설명한 예에서는 필터 함수에서 함수를 콜백으로 사용했을 때 동기적으로 실행되었습니다. 따라서 이를 동기 실행이라고 합니다. 필터 함수는 더 큰 함수가 실행을 마칠 때까지 기다려야 합니다.

따라서 콜백 함수는 호출된 상위 함수의 실행을 차단하므로 콜백 차단이라고도 합니다.

기본적으로 JavaScript는 단일 스레드 동기 및 차단 특성으로 간주됩니다. 그러나 몇 가지 접근 방식을 사용하면 다양한 시나리오에 따라 비동기식으로 작동하도록 할 수 있습니다.

이제 비동기 JavaScript를 이해해 봅시다.

비동기 자바스크립트

비동기 프로그래밍 언어는 애플리케이션의 성능을 향상시키는 데 중점을 둡니다. 콜백은 이러한 시나리오에서 사용될 수 있습니다. 아래 예를 통해 JavaScript의 비동기 동작을 분석할 수 있습니다.

 function greet(){ console.log(&apos;greet after 1 second&apos;) } setTimeout(greet, 1000) 

위의 예에서 setTimeout 함수는 콜백과 밀리초 단위의 시간을 인수로 사용합니다. 콜백은 언급된 시간(여기서는 1초) 후에 호출됩니다. 간단히 말해서, 함수는 실행을 위해 1초를 기다립니다. 이제 아래 코드를 살펴보세요.

 function greet(){ console.log(&apos;greet after 1 second&apos;) } setTimeout(greet, 1000) console.log(&apos;first&apos;) console.log(&apos;Second&apos;) 

산출:

 first Second greet after 1 second 

위 코드에서 타이머가 경과하는 동안 setTimeout 이후의 로그 메시지가 먼저 실행됩니다. 따라서 1초의 시간 간격을 두고 인사말 메시지가 출력됩니다.

JavaScript에서 setTimeout은 비동기 함수입니다. setTimeout 함수를 호출할 때마다 지정된 지연 후에 실행될 콜백 함수(이 경우greet)를 등록합니다. 그러나 후속 코드의 실행을 차단하지는 않습니다.

위의 예에서 로그 메시지는 즉시 실행되는 동기 문입니다. 이는 setTimeout 함수에 종속되지 않습니다. 따라서 setTimeout에 지정된 지연을 기다리지 않고 해당 메시지를 실행하고 콘솔에 기록합니다.

한편 JavaScript의 이벤트 루프는 비동기 작업을 처리합니다. 이 경우에는 지정된 지연 시간(1초)이 지나길 기다리고, 그 시간이 지나면 콜백 함수(greet)를 꺼내서 실행합니다.

따라서 setTimeout 함수 이후의 다른 코드는 백그라운드에서 실행되는 동안 실행되었습니다. 이 동작을 통해 JavaScript는 비동기 작업이 완료되기를 기다리는 동안 다른 작업을 수행할 수 있습니다.

JavaScript에서 비동기 이벤트를 처리하려면 호출 스택과 콜백 대기열을 이해해야 합니다.

아래 이미지를 고려하십시오.

JavaScript의 콜백 지옥

위 이미지에서 일반적인 JavaScript 엔진은 힙 메모리와 호출 스택으로 구성됩니다. 호출 스택은 스택에 푸시될 때 기다리지 않고 모든 코드를 실행합니다.

힙 메모리는 필요할 때마다 런타임에 객체와 함수에 대한 메모리를 할당하는 역할을 합니다.

이제 우리의 브라우저 엔진은 DOM, setTimeout, 콘솔, 가져오기 등과 같은 여러 웹 API로 구성되며 엔진은 전역 창 개체를 사용하여 이러한 API에 액세스할 수 있습니다. 다음 단계에서 일부 이벤트 루프는 콜백 대기열 내에서 함수 요청을 선택하여 스택에 푸시하는 게이트키퍼 역할을 합니다. setTimeout과 같은 이러한 함수에는 특정 대기 시간이 필요합니다.

이제 setTimeout 함수 예제로 돌아가 보겠습니다. 함수가 발생하면 타이머가 콜백 대기열에 등록됩니다. 그 후 나머지 코드는 호출 스택에 푸시되고 함수가 타이머 제한에 도달하면 실행됩니다. 만료되고 콜백 대기열은 지정된 논리를 갖고 타임아웃 함수에 등록된 콜백 함수를 푸시합니다. . 따라서 지정된 시간 이후에 실행됩니다.

콜백 지옥 시나리오

이제 콜백 지옥에 대한 콜백, 동기, 비동기 및 기타 관련 주제에 대해 논의했습니다. JavaScript에서 콜백 지옥이 무엇인지 이해해 봅시다.

여러 콜백이 중첩된 상황은 코드 모양이 피라미드처럼 보이기 때문에 콜백 지옥으로 알려져 있으며, 이를 '파멸의 피라미드'라고도 합니다.

콜백 지옥은 코드를 이해하고 유지하기 어렵게 만듭니다. 우리는 node JS에서 작업하는 동안 이러한 상황을 대부분 볼 수 있습니다. 예를 들어, 아래 예를 고려해보세요:

 getArticlesData(20, (articles) =&gt; { console.log(&apos;article lists&apos;, articles); getUserData(article.username, (name) =&gt; { console.log(name); getAddress(name, (item) =&gt; { console.log(item); //This goes on and on... } }) 

위의 예에서 getUserData는 기사 목록에 종속되거나 기사 내부에 있는 getArticles 응답을 추출해야 하는 사용자 이름을 사용합니다. getAddress에도 getUserData의 응답에 따라 달라지는 유사한 종속성이 있습니다. 이런 상황을 콜백 지옥이라고 합니다.

콜백 지옥의 내부 작동은 아래 예를 통해 이해할 수 있습니다.

작업 A를 수행해야 한다는 점을 이해해 봅시다. 작업을 수행하려면 작업 B의 일부 데이터가 필요합니다. 마찬가지로; 우리는 서로 의존하고 비동기적으로 실행되는 다양한 작업을 가지고 있습니다. 따라서 일련의 콜백 함수를 생성합니다.

JavaScript의 Promise와 이것이 비동기 작업을 생성하는 방법을 이해하여 중첩된 콜백 작성을 방지해 보겠습니다.

자바스크립트 약속

JavaScript의 경우 ES6에서 Promise가 도입되었습니다. 구문론적 코팅이 적용된 객체입니다. 비동기 동작으로 인해 비동기 작업에 대한 콜백 작성을 방지하는 대체 방법입니다. 요즘에는 fetch()와 같은 웹 API가 서버에서 데이터에 액세스하는 효율적인 방법을 제공하는 Promise를 사용하여 구현됩니다. 또한 코드 가독성이 향상되었으며 중첩된 콜백 작성을 방지하는 방법입니다.

실제 생활에서의 약속은 둘 이상의 사람 사이의 신뢰와 특정 일이 반드시 일어날 것이라는 확신을 표현합니다. JavaScript에서 Promise는 미래에 (필요한 경우) 단일 값을 생성하도록 보장하는 객체입니다. JavaScript의 Promise는 비동기 작업을 관리하고 처리하는 데 사용됩니다.

Promise는 비동기 작업 및 해당 출력의 완료 또는 실패를 보장하고 나타내는 객체를 반환합니다. 정확한 출력을 알지 못한 채 값에 대한 프록시입니다. 비동기 작업에서 최종 성공 값이나 실패 이유를 제공하는 데 유용합니다. 따라서 비동기 메서드는 동기 메서드와 같은 값을 반환합니다.

일반적으로 Promise에는 다음 세 가지 상태가 있습니다.

  • 이행: 이행 상태는 적용된 작업이 해결되거나 성공적으로 완료된 경우입니다.
  • 보류 중: 보류 중 상태는 요청이 처리 중이고 적용된 작업이 해결되거나 거부되지 않았으며 여전히 초기 상태에 있는 경우입니다.
  • 거부됨: 거부됨 상태는 적용된 작업이 거부되어 원하는 작업이 실패하는 경우입니다. 거부의 원인은 서버 다운을 포함하여 무엇이든 될 수 있습니다.

약속의 구문은 다음과 같습니다.

엑셀 첫 문자 제거
 let newPromise = new Promise(function(resolve, reject) { // asynchronous call is made //Resolve or reject the data }); 

다음은 약속을 작성하는 예입니다.

약속을 작성하는 예입니다.

 function getArticleData(id) { return new Promise((resolve, reject) =&gt; { setTimeout(() =&gt; { console.log(&apos;Fetching data....&apos;); resolve({ id: id, name: &apos;derik&apos; }); }, 5000); }); } getArticleData(&apos;10&apos;).then(res=&gt; console.log(res)) 

위의 예에서 Promise를 효율적으로 사용하여 서버에 요청하는 방법을 확인할 수 있습니다. 콜백보다 위 코드에서 코드 가독성이 향상되는 것을 확인할 수 있습니다. Promise는 성공 또는 실패 시 작업 상태를 처리할 수 있는 .then() 및 .catch()와 같은 메서드를 제공합니다. 우리는 Promise의 다양한 상태에 대한 사례를 지정할 수 있습니다.

JavaScript의 비동기/대기

중첩된 콜백의 사용을 피하는 또 다른 방법입니다. Async/Await를 사용하면 Promise를 훨씬 더 효율적으로 사용할 수 있습니다. .then() 또는 .catch() 메소드 체인 사용을 피할 수 있습니다. 이러한 메서드는 콜백 함수에도 종속됩니다.

Async/Await는 Promise와 함께 정확하게 사용되어 애플리케이션 성능을 향상시킬 수 있습니다. 내부적으로 약속을 해결하고 결과를 제공했습니다. 또한 () 또는 catch() 메서드보다 더 읽기 쉽습니다.

일반 콜백 함수에는 Async/Await를 사용할 수 없습니다. 이를 사용하려면 function 키워드 앞에 async 키워드를 작성하여 함수를 비동기식으로 만들어야 합니다. 그러나 내부적으로는 체인도 사용합니다.

다음은 Async/Await의 예입니다.

 async function displayData() { try { const articleData = await getArticle(10); const placeData = await getPlaces(article.name); const cityData = await getCity(place) console.log(city); } catch (err) { console.log(&apos;Error: &apos;, err.message); } } displayData(); 

Async/Await를 사용하려면 async 키워드로 함수를 지정해야 하며, 함수 내부에 wait 키워드를 작성해야 합니다. 비동기는 Promise가 해결되거나 거부될 때까지 실행을 중지합니다. Promise가 처리되면 재개됩니다. 해결되면 대기 표현식의 값이 이를 보유하는 변수에 저장됩니다.

요약:

간단히 말해서, promise와 async/await를 사용하면 중첩된 콜백을 피할 수 있습니다. 이 외에도 주석 작성과 같은 다른 접근 방식을 따를 수 있으며 코드를 별도의 구성 요소로 분할하는 것도 도움이 될 수 있습니다. 하지만 요즘 개발자들은 async/await 사용을 선호합니다.

결론:

자바스크립트에서 콜백 지옥은 중첩된 콜백 함수가 과도하게 실행되는 상황을 말한다. 코드 가독성과 유지 관리가 줄어듭니다. 콜백 지옥 상황은 일반적으로 여러 API 요청을 생성하거나 복잡한 종속성이 있는 이벤트를 처리하는 등 비동기 요청 작업을 처리할 때 발생합니다.

JavaScript의 콜백 지옥을 더 잘 이해합니다.

JavaScript는 문자열, 배열, 함수 등 모든 것을 객체로 간주합니다. 따라서 콜백 개념을 사용하면 함수를 다른 함수의 인수로 전달할 수 있습니다. 콜백 함수가 먼저 실행을 완료하고 상위 함수가 나중에 실행됩니다.

콜백 함수는 비동기적으로 실행되며 비동기 작업이 완료될 때까지 기다리지 않고 코드가 계속 실행될 수 있도록 합니다.