Doby's Lab

[자바스크립트/JavaScript #4] 비동기 처리(Part 2), Call-Back에 대해 본문

카테고리 없음

[자바스크립트/JavaScript #4] 비동기 처리(Part 2), Call-Back에 대해

도비(Doby) 2021. 8. 4. 01:28

자바스크립트에서 비동기 처리를 시키는 방법 중 콜백에 대해 알아보자.
콜백에 대해 간단히 정의를 내려두면

  1. 매개변수(Parameter)에 함수를 할당한다.
  2. 나중에 때가 돼서 필요할 때 호출한다.

이 두 가지를 기억해야 한다.
기억하고 있다고 해도 더 깊이 파고들었을 때 음..? 하는 경우가 많았다.


콜백(Call-Back)의 뜻

예시 두 가지를 보자.

const temp = 3; 
function print(temp, printYes, printNo){ 
	if(temp >= 3){ 
    	printYes(); 
    } 
    else{ 
    	printNo(); 
    } 
} 

function printYes(){
	console.log("Yes");
} 

function printNo(){ 
	console.log("No"); 
} 

print(temp, printYes, printNo);

다음 코드가 콜백 함수의 예시이다.

print라는 함수에서 매개변수에 printYes와 printNo를 할당하였다.
temp라는 변수가 3 이상이면 printYes가 호출되고,
조건을 만족하지 않으면 printNo가 호출된다.
>>print라는 함수가 수행하는 일

다만 여기서 궁금했던 건 "왜 Call-Back일까?"

이유는 다음과 같다.
맨 마지막 줄에서 print라는 함수가 호출(Call)이 되는데
매개변수로 받은 temp에 따라 printYes나 printNo의 호출이 결정된다.

즉, "print가 먼저 call 되고 printYes와 printNo는 때가 되면 필요할 때 불러줄게(Call-Back)"
이런 느낌이다.

콜백은 다음과 같은 표현도 가능하다.
(필자는 여기서 많이 헷갈렸다.)

const temp = 3; 
function print(temp, printYes, printNo){
	if(temp >= 3){ 
		printYes(); 
	} 
	else{ 
		printNo(); 
	} 
} 

print(temp, () => { 
	console.log("Yes"); 
}, () => {
	console.log("No");
});

'Arrow function'을 사용한 콜백이다.
print라는 함수를 선언할 때, 블록 안에서 printYes는 이럴 때 쓰고,
printNo는 이럴 때 쓰자라고 코드를 작성했다.
바로 print를 call 하면 print는 printYes와 printNo가 무엇인지 모른다.
그래서 위의 코드에서 더 간단하게 호출할 때 print 매개변수에서 바로
함수를 Arrow function을 통해 선언하며 호출하는 것이다.

이해가 안 될 수 있다😂

더 간단하게 상황극을 예시로 들어보겠다.

코드 블록 (1), Arrow Function을 쓰지 않았을 때
코드 작성자: print야 printYes랑 printNo 데리고 와
print(): 알겠어 printYes랑 printNo는 누군지도 알고 어디 있는지도 아니까 내가 데려갈게


코드 블록 (2), Arrow Function을 썼을 때
코드 작성자: print야 printYes랑 printNo 데리고 와
print(): 난 걔네가 누군지도 모르고 어딨는지도 모르는데?
코드 작성자: printYes는 이런 애고 어디에 있고, printNo는 이런...(생략)

이런 느낌이다.


그럼 Call-Back이 왜 비동기 처리지?

콜백의 개념을 알고 나서 이것이 왜 필요하며 왜 비동기 처리라고 불리는지 궁금했다.
전 포스팅에서 비동기 처리에 대해 설명했지만 다시 간단하게 필요한 이유에 대해 설명하자면
자바스크립트는 싱글 스레드로 하나의 스레드가 일을 처리한다.
그렇기에 code를 실행하다가 API를 호출하는 코드(데이터의 크기가 크다고 가정)를
만나면 호출하는 데에 시간을 잡아먹힌다.
그래서 API를 호출하는 부분(그 외에 커다란 데이터를 처리하는 일)은 시간을 잡아먹지 않게 콜백을 통해 비동기 처리를 시킨다.


콜백이 비동기 처리되는 과정을 한 번 알아보자.
이해를 돕기 위해 다음과 같이 가정하겠다.

1~4까지 순서에 상관없이 숫자들을 출력하고 싶다.
(단, 빠르게 출력할 수 있는 것은 빠르게 출력한다.)
각 숫자들을 출력은 다음의 일을 수행한다.
----
1을 출력: 1을 출력함 (독립적인 일)
2를 출력: 2를 출력하기 위해 API를 호출하는 일이 요구됨
3을 출력: 동기적으로 출력될 수 있으나 2의 출력이 되어야 3이 출력될 수 있다고 가정 (2의 일에 종속됨)
4를 출력: 4를 출력함 (독립적인 일)
----

동기 처리

다음 그림은 일반적인 동기적 처리로 코드로는 다음과 같이 나타낼 수 있다.

console.log(`1`);

function print(){
    //Doing some heavy work
    //2를 출력하기 위해 시간이 걸린다는 것을 흉내
    setTimeout(() => {
        console.log(`2`);
        console.log(`3`);
        console.log(`4`);
    }, 1000);
}

print();

/** 출력 결과 
1 출력 
1초 후 2, 3, 4 출력 
*/

2를 출력하는 데에 1초의 시간이 걸려 4를 출력하는 일까지 딜레이가 발생한다.
이를 위해 비동기 처리를 하려면

비동기 처리

다음 그림과 같이 처리를 시켜야 한다.
코드를 통해 나타내면 다음과 같다.

 console.log('1');

function print_2(print_3){
    //Doing Some Heavy Work
    //시간이 걸림을 나타내기 위해 setTimeout 사용
    setTimeout(() => {
        console.log(`2`);
        print_3();
    }, 1000);
}

print_2(()=> {
    console.log(`3`);
});

console.log(`4`);

/** 출력 결과 1, 4 출력 1초 후 2, 3 출력 */

다음 코드를 통해 3을 출력할 때는 2의 출력이 필요하여 둘 다 1초 딜레이가 되었지만
4를 출력하는 데는 피해가 가지 않도록 비동기 처리를 해줬음을 알 수 있다.

(1,2,3,4의 출력은 비동기 처리의 이해를 돕기 위한 예시일 뿐이다.)


콜백 지옥

콜백을 적당히 사용하여 비동기 처리를 시키는 것은 좋지만
너무 많이 사용하게 되면 다음과 같이 '콜백 지옥'이라는 것이 발생한다.

class UserStorage{
    loginUser(id, password, onSuccess, onError){// method
        setTimeout(() => {
            if(
                (id === 'ellie' && password === 'dream') ||
                (id === 'coder' && password === 'academy')
            )
            {
                onSuccess(id);
            }
            else{
                onError(new Error('not Found'));
            }
        }, 2000);
    }

    getRoles(user, onSuccess, onError){// method
        setTimeout(() => {
            if(user === 'ellie'){
                onSuccess({name: 'ellie', role: 'admin'});
            }
            else{
                onError(new Error('no access'));
            }
        }, 1000);
    }
}

const userStorage = new UserStorage();
const id = prompt('enter your id');
const password = prompt('enter your password');
userStorage.loginUser(
    id,
    password, 
    (user) =>{// onSuccess를 통해 id라는 매개변수는 user이다.
        userStorage.getRoles(
            user,
            userWithRole => {
                alert(`hello ${userWithRole.name}, you have a ${userWithRole.role} role`);
            },
            error => {
                alert(error);
            }
        );
    },
    (error) => {console.log(error)}
);

이 코드는 콜백을 무수히 많이 사용한 것인데
보기만 해도 가독성이 떨어지는 것이 보인다.
자바스크립트에서는 콜백 외에도 비동기 처리를 시키는 법이 있다.
다른 방법을 통해 비동기 처리를 더 간단하게 표현할 수 있다.
다음 포스팅에서는 비동기 처리 Promiseasync, await에 대해 작성하겠다.


참고

https://jason9319.tistory.com/406