수정 사항
8/19 : 콜백 선수지식 - 동기와 비동기 추가
8/19 : 콜백, 고차함수 정의 추가
콜백 함수를 알아보기 앞서, 동기와 비동기가 무엇인지 알아야합니다.
콜백 함수는 비동기 처리를 위한 패턴이기 때문입니다.
동기(Synchronouse)는 코드가 순차적으로 실행된다는 의미입니다.
현재 작업이 끝나야만 다음 코드를 실행합니다.
원래 우리가 알고 있는 프로그래밍의 흐름입니다.
코드를 하나씩 실행하기 때문에, 코드에 대한 실행 순서가 보장됩니다.
다만, 작업이 끝나기 이전에는 다음 작업이 멈춰있습니다. 이를 블로킹(Blocking)이라고 합니다.
비동기는 실행 중인 코드가 완료되지 않더라도, 다음 코드로 넘어갑니다.
비동기(보통, 시간이 걸리는)작업은 브라우저에 맡기고, 다음 코드로 넘어갑니다.
실행 순서가 보장되지 않지만, 블로킹을 막을 수 있습니다.
자바스크립트는 한번에 한가지 일만 처리하는 싱글 쓰레드입니다.
하지만, 사진을 불러오는 중에도 사용자의 입력을 처리하는 등 여러 작업을 하는 것 처럼 보입니다.
비동기 처리가 필요한 이유입니다.
콜백 함수란, 매개 변수로 받은 함수를 콜백 함수라고 부릅니다.
함수를 매개변수로 받는 함수를 고차 함수라고 부르기도 합니다.
고차 함수에서 원하는 때에, 콜백 함수를 실행시켜 비동기를 처리합니다.
스크립트를 읽어오는 함수 loadScript(src)
입니다.
function loadScript(src) {
let script = document.createElement('script');
script.src = src;
document.head.append(script);
}
loadScript
를 실행하고, 함수 실행에 의해 로딩되는 스크립트를 활용하려고 합니다.
로딩이 된 후에, 스크립트를 활용해야 합니다.
하지만, loadScript
이 후의 코드들은 함수의 로딩을 기다리지 않습니다.
loadScript('./src.js');
func(); // src내의 함수 func() : 존재하지 않는 함수 에러
이처럼, 로딩 후에 실행되어야 할 코드들은 에러가 발생합니다.
해결하기 위해 loadScript
에 두번째 인자로 콜백 함수를 추가합니다.
function loadScript(src,callback) {
let script = document.creatElement('script');
script.src = src;
script.onload = () => callback(script); // *
document.head.append(script);
}
//호출
loadScript('./src.js', script => {
alert(`${script.src}가 로드됨`);
func() // 사용가능
})
script
가 load되면, 콜백 함수를 호출하도록 작성합니다.
이런 방식을 콜백기반 비동기 프로그래밍 이라고 합니다.
반드시 함수가 다 처리되고 실행 되야하는 함수가 들어갈 인수를 제공합니다.
지금까진, 스크립트 로딩중에 에러가 발생하는 상황을 고려하지 않았습니다.
이러한 작업은 에러가 자주 발생하고, 핸들링 할 수 있어야 합니다.
loadScript(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);
}
로딩에 성공하면 callback(null,sciprt)
를
실패하면, callback(new Error(...))
을 호출합니다.
이제 loadScript
를 이렇게 호출합니다.
loadScript(src, (err,script)=>{
if(err) {
//error 처리
return;
}
// 스크립트 로딩이 성공적일 때 코드
})
이렇게 콜백 함수 첫 인자에는 오류 객체를 담는 패턴을 오류 우선 콜백이라 부릅니다.
이번엔 스크립트 두 개를 불러오려 합니다.
두 번째 스크립트는 첫 번째 스크립트를 불러온 이후에 불러옵니다.
loadScript(src1, script=> {
alert(`${script.src}로딩 완료`);
loadScript(src2, script=> {
alert(`${script.src}로딩 완료`);
})
})
첫 함수의 콜백 함수 내에서, 다시 함수를 호출합니다.
다만, 중첩이 생깁니다.
세 번째, 네 번째 스크립트를 불러오려고 하면, 중첩은 더욱 깊어집니다.
꼬리에 꼬리를 무는 비동기 동작이 많아지면 중첩이 깊어집니다.
func1
을 실행하여, 스크립트를 로드합니다. 에러가 없으면,
func2
을 실행하여, 스크립트를 로드합니다. 에러가 없으면,
func3
을 실행하여, 스크립트를 로드합니다. 에러가 없으면,
func4
을 실행하여, 스크립트를 로드합니다. 에러가 없으면,
.
.
비동기 호출이 계속 중첩되면서, 코드가 깊어집니다.
중간 중간, 조건문이나 반복문이 들어가면 더욱 보기 힘들어집니다.
이러한 중첩코드가 만들어내는 패턴을 멸망의 피라미드라고 합니다.
이제, ES6 이후 등장한 Promise
를 통해 이런 문제를 해결합니다.