반응형

1. 콜백이란?

비동기 코딩 시 순서를 지키기 위하여 함수 안에 함수를 파라미터를 넣어서 쓰는 방식을 콜백 함수라고 한다.

라고만 대충 알면 그 뒤의 심오한 뜻을 알지 못합니다.

 

이번 포스팅에서는 콜백함수를 익명 함수의 관점으로 바라볼 때 어떤 의미가 있는가?입니다.

 

2. 콜백을 진지하게 다시 봅시다.

 

콜백을 진지하게 보기 전에, 프로미스 함수에 대해서 잠깐 알고 갑시다. 왜냐면 제가 이 프로미스 함수를 예시로 콜백 함수를 설명할 건데 그전에 프로미스 함수에 대해서 함수 스타일을 알고 가야 하기 때문에 이해하기 쉽기 때문입니다. 

(제 블로그에 프로미스에 대한 글도 있으니 궁금하시면 보세요.)

 

일단 그냥 프로미스 함수를 적으면 이렇게 나옵니다. 

const promise = new Promise( (resolve, reject) => {
	// 프로미스에게 할 일을 적고
    console.log();
    
	// 성공시 resolve() 함수 호출
    resolve();
    
    // 실패시 reject() 함수 호출
    reject();
})

	// 나중에 프로미스의 반환된 값을 보고 싶다면 반드시 .then()으로 받아야 함
    .then( (data) => {
    	console.log(data);
    })
    
    // 실패시 catch()문으로 에러값을 받음
    .catch( (error) => {
    	console.error(error);
    })

프로미스를 쓸려면 위에서 처럼 반드시 콜백 함수를 써야합니다. 

그럼 이 프로미스의 콜백함수를 좀 자세히 봅시다.

 

vscode에서 javasctipt 관련 플러그인을 설치하고

new Promise() 한 다음 가로 안에 커서를 두고 도움말을 기다리시면, 가로 안에 어떤 형식이 와야 하는지 예시 도움말이 나옵니다. 그것을 복사하면 다음과 같습니다.

 

Promise(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void): Promise <any>

 

 저도 그랬도 우리같이 초보자분들은 은근 이런 도움말을 무시하고 바로 그냥 구글 서치 들어갑니다... 물론 어떤 방식으로든 결과만 같다면 좋습니다. 다만.. 시간이라는 호율성이 문제가 있겠죠. 더 이상 얘기하면 딴 얘기로 빠지므로 이만

하겠습니다. ( 편집기의 도움말을 항상 이해하고 참조해야 한다는 소리는 아닙니다.) 

 

 

순서대로 설명하겠습니다.

executer: 는 함수 이름입니다. 
(꼭 함수 이름을 executer라고 만들어서 쓰라는 소리가 아닙니다. 대부분그냥 익명 함수로 씁니다. 여기서는 개념상 Promise객체가 필요한 파라미터가 있는데 그 파라미터가 함수이고, 그 함수의 역할을 보니 실행? 같은 부분을 맡으므로 함수이름을 executer라고 지어논겁니다. 하지만 프로미스를 쓸 때 대부분 걍 익명함수로 씁니다. )

 

그러니깐 첫 번째 파라미터로 익스큐터 함수라는 것이 오는데, " : " 다음으로는 해당 함수의 형식을 보여줍니다. 

익스큐터의 함수는 어떻게 작성해야 하나 보니 다음과 같습니다.

(resolve: (value?: any) => void, reject: (reason?: any) => void) => void):

 

잘 보면 resolve라는 함수가 파라미터로 들어가 있습니다.

또 reject라는 함수도 파라미터로 들어가 있습니다.

 

resolve: (value?: any) => void,

이 부분이 resolve함수를 설명하는 부분이고 파라미터로 어느 값이든 들어올 수 있고, 리턴되는 값은 void, 즉 없다고 나오네요. (value라고 명명한 이유는 흔히 resolve()에서 데이터 값을 받고 저장하기 때문입니다. 그래서 value값으로 이름을 지었군요.)

 

reject: (reason?: any) => void

이역시 위와 마찬가지, reject라는 함수이고 파라미터도 any, 아무것이나 들어올 수 있다는 것이고, 리턴되는 값은 역시 없습니다. (reject() 함수는 프로미스가 작업 도중 실패했을 때 호출하는 함수입니다. 그러니 실패한 이유를 필요로 하겠죠? 그래서 reason으로 명명했군요.)

 

그리고 끝에 => void 이 남게 되는데 executer() 함수의 리턴 값을 의미하는 부분입니다. 보시다시피 아무것도 반환하지 않는군요.

 

여기까지 (resolve: (value?: any) => void, reject: (reason?: any) => void) => void): 설명이고 마지막에 하나 더 있죠?

끝에 Promise <any>는 이 Promise() 함수가 반환하는 객체를 의미합니다. 프로미스 객체를 반환하는군요. <any>는 제너릭 타입인데 이건 나중에 설명하겠습니다. 

일단 지금 당장은 어느 결괏값이든 Promise() 객체 안에 담겨서 반환된다.라고 생각하시면 되겠습니다.

 

여기까지 이해가 되었다면, 위로 다시 올라가서 프로미스 함수를 다시 보시죠.

콜백 함수의 resolve, reject 인자는 둘 다 함수였구나 라는 것을 알게 됩니다. 

 

이제 콜백 함수에 대해서 천천히 살펴봅시다. 

프로미스라는 객체는 자바스크립트 ES6에서 기본적으로 제공되는 객체지만, 설명을 위해 없다고 치고 제가 프로미스라는 함수를 만들어서 쓴다는 가정을 해봅시다. 

 

자 프로미스라는 함수를 정의해 봅시다.

function myPromise( callBackFunc  ) {
	getApi("https://url요청주소.com/api")	// 무언가 네트워크 통신 작업을 하는 코드라고 칩시다.
	.then(data => console.log(data));       // 결과값을 받고 console.log로 출력하는 코드라고 칩시다. 
	
    return callBackFunc(resolve, reject, data)    // resolve(), reject()는 외부에서 이미 정의되어있는 함수라고 칩시다.
}


헷갈리지 마세요. 위는 함수를 '정의' 한 거라고 생각하세요

이 함수의 역할을 대충 

1. 어떤 곳으로 네트워크 통신 요청을 하고

2. 그 결괏값을 받아 console.log로 찍어줍니다.

3. 그리고 파라미터로 받은 callBackFunc함수에 resolve, reject 함수를 넣고 호출한 뒤 그 값을 리턴하네요. 

(참고로 위의 는 슈도 코드이니 코드 하나하나 이해하려고 하지 마세요.)

(슈도 코드란: 개발자끼리 의사소통만을 위해, 코드 규칙은 지키지 않고 코드의 역할이나 목적만 알 수 있도록 대충 적은 코드를 말합니다. 슈도 코드에서 중요한 건 각각의 코드의 역할과 로직적인 흐름입니다.)

 

그럼 위에 정의된 myPromise() 함수를 호출해서 써 봅시다.

myPromise( (resolve, reject, data) => {
    
    // 이번엔 getApi받아온 데이터를 다른 서버에 보내서 잘 갔나 응답도 받아봅시다.
    let result = 0;               // 우선 result 변수를 하나 잡고 여기에다 서버의 응답을 받아봅시다.
    postApi("https://어디가에요청.com/api", data).then(serverResult => {
        result = serverResult;    // 위의 result변수에다가 서버가 준 응답결과를 저장한다 칩시다.
                                  // 성공해서 서버에서 데이터를 잘 받았다 치면 1을 준다 치고, 실패하면 -1를 준다고 합시다.
    });
    // result의 내용물을 보면 1, 0, -1 에 따라 결과를 알 수 있겠죠?
    if(result) {
        resolve(result);          // 성공했으므로 resolve()함수를 호출해서 값을 저장해줍시다.
    }else{
        reject(new Error("0, -1 이든 쨋든 서버에서 데이터를 잘 받지 못했네요")); // 실패시 에러문구를 보여줍시다.
    }
    return result;
    
});

 

 여기서 핵심은 상세하게 제가 쓴 의미 없는 코드들을 이해하는 게 아니라, 

제가 callBackFunc 함수에 들어갈 것들을 정의했다입니다. 즉 익명 함수를 즉석에서 만들어서 myPromise() 함수의 콜백 함수로 넣어준 것이죠. 이게 핵심입니다.

 아시다싶히, myPromise() 함수를 정의하는 곳에서는 익명함수를 정의하지 않습니다. 그냥 그 익명함수를 호출하고 그 결과값을 반환해라 라고 정의되어있죠. 

 

myPromise()함수를 정의한 것을 다시 보면 

1. 이 함수는 어찌 되었든 무조건 getApi를 통해 어디선가로부터 데이터를 일단 받아옵니다.

2. 그리고 다시 reject(), resolve(), 함수들과 함께 위에서 받아온 데이터를 callBackFunc 함수의 파라미터로 넣어서 호출하죠.

3. 그리고 그 리턴 값을 리턴합니다. 

 

그리고 호출해서 쓸 때를 보면, callBackFunc자리에 냅다 바로 익명 함수를 정의합니다. 

 

해석해보면 myPromise()는 데이터를 받아서 익명 함수에 다시 줍니다.

그리고 익명 함수가 하는 일은? 우리가 호출할 때 정의해줬죠?  즉석에서 바로, 이렇게 정의하는 겁니다. 그럼 굳이 myPromise() 함수를 정의할 때 콜백 함수까지 정의하지 않아도 되죠? 그렇게 하려고 익명 함수를 쓰는 거고요.

 이런 식의 콜백 함수는 즉석에서 상황에 맞게 알아서 쓰라고 둬 야하기 때문에 미리 사전에 정의할 수 없습니다. 

 

결과적으로 저렇게 호출하게 되면 getApi로 일단 데이터를 받아와서 다시 postApi로 보내서 해당 서버에서 데이터가 잘 왔으면, 응답을 해줄 것이고 그 응답을 받아서 잘 받았으면 resolve() 호출하고, 뭔가 문제로 인해 잘 못 받았으면 reject()를 호출하는 함수가 됩니다. 

 

이상 포스팅을 마치겠습니다.

 

 

 

 

 

 

 

 

 

 

*틀린 점이 있다면 댓글로 달아주세요. 아직 배우는 학생이랍니다.
*질문도 댓글로 적어주시면 답변할 수 있는 한도내에서 답변해드리겠습니다.

 

 

 

 

 

 

 

 

 

 

 

반응형

+ Recent posts