JavaScript/JavaScript기초

[Javascript] 콜백

박남수 2021. 10. 21. 17:03

콜백함수

콜백함수란 어떤 고차함수에 해당 함수를 인자로 넘겨주어 사용되는 함수를 말함

 

어떤 코드가 비동기적으로 실행될 때 콜백함수를 유용하게 사용할 수 있음

 

인자로 넘겨받은 스크립트를 html문서에 추가시켜주는 함수가 있다고 가정

function load(src){
	
    let script = document.createElement("script");
    script.src = src;
    document.head.append(script);
}

//함수를 실행
load("scripts/scriptOne.js");

 

위와같이 load라는 함수를 실행하면 비동기적으로 동작함

 

load로 불러온 "scripts/scriptOne.js" 에 myFunc(){} 라는 함수가 있다고 가정

function load(src){
	
    let script = document.createElement("script");
    script.src = src;
    document.head.append(script);
}

//함수를 실행(myFunc()함수가 scriptOne.js 에 정의되어 있음)
load("scripts/scriptOne.js");

//load가 비동기적으로 실행되므로 오류 발생할 수 있음
myFunc();

 

위에 코드를 실행시 load가 비동기적으로 실행되므로 load의 작업이 끝날때 까지 밑에 코드들이 기다려 주지 않으므로 scriptOne.js 가 불려오지 않은 상태에서 myFunc()함수가 실행되어 오류가 발생할 수 있음

 

이러한 문제를 해결하기 위해 callback함수를 사용

function load(src,callback){
	
    let script = document.createElement("script");
    script.src = src;
    
    //스크립트가 로드되면 callback함수를 실행
    script.onload = () => {
    	
        callback();
    }
    document.head.append(script);
}

//함수를 실행(callback함수를 2번째 인자로 전달)
load("scripts/scriptOne.js",function(){
	
    //로드가 된 상태에서 myFunc()을 호출하므로 정상적으로 실행
    myFunc();
});

 

위와같이 콜백함수를 이용하면 로드가 완료된 상태에서 동기적으로 myFunc를 호출할 수 있음

 

에러 핸들링

콜백 함수에서의 에러 핸들링은 다음과 같이 할 수 있음

function load(src,callback){
	
    let script = document.createElement("script");
    script.src = src;
    
    //스크립트가 로드되면 callback함수에 script 전달
    script.onload = () => callback(null,script)
    //스크립트를 불러오는데 에러가 발생하면 에러 전달
    script.onError = () => callback(new Error("${src} 로드 중 에러발생"))
    
    document.head.append(script);
}

//함수를 실행(callback함수를 2번째 인자로 전달)
load("scripts/scriptOne.js",function(error,script){
	
    //error가 존재할 시 에러 핸들링 실행
    if(error){
    	
        //...
    }else{ //정상적으로 스크립트 로드시
    
    }
});

 

콜백 지옥

아래와 같이 콜백 함수를 여러번 중첩해야 하는 상황이 있을 수 있음

function load(src,callback){
	
    let script = document.createElement("script");
    script.src = src;
    
    script.onload = () => callback(null,script)

    script.onError = () => callback(new Error("${src} 로드 중 에러발생"))
    
    document.head.append(script);
}

load("scripts/scriptOne.js",function(error,script){
	
    if(error){
    	
        //...
    }else{ 
    	//scriptOne.js의 호출이 완료되면 scripts/scriptTwo.js를 호출
        load("scripts/scriptTwo.js",function(error,script){
			
            if(error){
			
            }else{
			
            }
    	})
    }
});

 

위의 코드는 중첩이 한번밖에 발생하지 않아서 별 문제가 없지만 중첩이 수십개가 될 경우 코드가 오른쪽으로 끝없이 늘어나 코드 분석 및 유지보수가 매우 힘들어질 수 있음.

 

이러한 현상을 '콜백지옥' 또는 '멸망의 피라미드' 라고 함.

 

이러한 콜백지옥의 코드를 만들지 않기위해 아래와 같이 변경할 수 있음

function load(src,callback){
	
    let script = document.createElement("script");
    script.src = src;
    
    script.onload = () => callback(null,script)

    script.onError = () => callback(new Error("${src} 로드 중 에러발생"))
    
    document.head.append(script);
}
//scriptOne을 로드하고 func1을 호출
load("scripts/scriptOne.js",func1);

//func1이 호출되면 scriptTwo가 호출되고 func2가 호출
function func1(error,script){
	
    if(error)
    	//...
    else 
    	load("scripts/scriptTwo.js",func2);
}

//모든 스크립트가 로딩되면 실행
function func2(error,script){
	
    if(error)
    	//...
    else
    	//...
}

 

위와같이 함수를 분리해 놓으면 콜백지옥에 빠지지 않게 콜백 함수를 중첩해서 호출할 수 있음

 

하지만 이렇게 생성한 함수들은 재사용 하기도 힘들다는 단점이 있음.

 

이러한 함수들을 따로 분리해놓지 않고도 콜백 지옥을 피하는 방법은 '프라미스(Promise)' 를 사용하는 것임(next)

 

참조: https://ko.javascript.info/callbacks