비동기 처리와 콜백 함수

Seokjun Moon·2023년 3월 2일
0

javascript

목록 보기
1/2

2023.03.01

sql 서버에서 데이터를 주고받는 코드를 작성해보면서 await, async 등의 구문이 궁금해지기도 했고, body-parser 등의 undefined 오류가 왜 나타나는지도, 그리고 node.js의 특징 중 하나가 비동기 처리로 인해 빠른 고성능 서버를 구축할 수 있다는 것인데, 비동기 처리가 무엇인지, 그리고 이를 어떻게 다루는지가 궁금해져서 찾아보게 되었습니다.




비동기 처리

자바스크립트에서의 비동기 처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 말합니다. 원래는 해당 구문이 종료된 후에 다음 구문이 실행되야 하지만, 비동기 처리를 하게 되면 해당 구문이 종료되지 않았음에도 불구하고 다음 구문을 실행합니다.




비동기 처리 - ajax 통신

비동기 처리의 가장 흔한 예시는 jquery의 ajax 입니다. 화면에 표시할 이미지나 데이터를 서버에서 불러와 표시해야 하는데 이때 ajax 통신으로 해당 데이터를 서버로부터 가져온다고 합니다. 간단한 예시를 가져오면

function getData() {
	var dat;
	$.get('https://mydomain.com/abc/1', (response) => {
		dat = response;
	});
	return dat;
}

console.log(getData());

$.get() 이 ajax 통신을 하는 부분인데, response 에 서버에서 받은 데이터가 담겨있습니다. 하지만 위 코드는 undefined 를 출력합니다. 그 이유는 $.get() 로 데이터를 요청 후 받을 때까지 기다리지 않고 return dat; 을 실행했기 때문입니다.

특정 구문의 실행이 끝날 때까지 기다리지 않고 다음 구문을 실행하는 것이 비동기 처리

입니다. 자바스크립트에 이러한 특징이 필요한 이유는, 서버에 데이터를 요청했을 때 언제 그 응답이 돌아올지 모르는데 다음 코드를 실행하지 않고 무작정 기다릴 수 없기 때문입니다. 만약 1개가 아닌 100개 이상의 요청을 보내야 하는 상황이라고 하면, 비동기 처리를 하지 않았다면 순차적으로 다 기다려야 하기 때문에 매우 비효율적이라고 할 수 있습니다.




비동기 처리 - setTimeout()

지정한 시간 이후에 코드를 실행하는 setTimeout() 또한 비동기 처리입니다. 아래 예시에서 보면

console.log('시작');
setTimeout(() => {
    console.log('언제');
}, 3000);
console.log('출력');

비동기 처리로 인해 위 코드는

시작
출력
언제

이렇게 출력을 하게 됩니다. 그 이유는 console.log('시작'); 를 실행한 후에 setTimeout() 을 실행하지만, 비동기 처리로 3초 대기가 걸려있기 때문에 다음 구문인 console.log('출력'); 를 실행한 후 3초가 지났을 때 console.log('언제'); 를 출력하기 때문입니다.




콜백 함수

위에서 본 비동기 처리시 발생하는 상황을 피하기 위해서는 콜백 함수를 사용하면 됩니다. 예를 들어

function getData(callbackFunc) {
	$.get('https://mydomain.com/abc/1', (response) => {
		callbackFunc(response);
	});
}

getData((dat) => {
	console.log(dat);
});

위와 같이 코드를 작성하면, $.get() 으로 서버에 요청한 데이터를 response에 넘겨주고 이를 dat 으로 전달하여 우리가 원하는 값을 출력할 수 있습니다. 즉, 콜백 함수를 사용하면 데이터가 준비된 시점에 원하는 동작을 수행할 수 있게 됩니다.




콜백 지옥 (Callback hell)

콜백 지옥은 콜백 함수를 연속적으로 사용할 때 발생하는 문제입니다.


콜백 지옥이란?

예시를 들자면

step1((value1) => {
    step2((value2) => {
        step3((value3) => {
            step4((value4) => {
                ...
                //do something
            });
        });
    });
});

웹 서비스에서 데이터를 받아와 화면에 표시하기 전에 인코딩, 사용자 인증 등을 처리해야 하는 경우가 있는데, 이 모든 과정을 비동기로 처리해야 하므로 위와 같이 콜백 안에 콜백이 계속 사용하게 됩니다. 이러한 구조는 가독성도 떨어지고 로직을 변경하기도 어렵습니다. (유지보수 매우 어려움)


해결 방법

콜백 지옥은 결국 리팩토링이 해결 방법입니다. 중첩해서 선언했던 익명의 콜백 함수들을 각각의 함수로 분리시켜 호출하면 됩니다. 예를 들어

const step1Done = (a) => {
	step2(a, step2Done);
};
const step2Done = (b) => {
	step3(b, step2Done);
};
const step3Done = (c) => {
	//do something
};
$.get('asdf', function(response) {
	step1(response);
});

이렇게 각각의 콜백을 분리시키면 됩니다. 결국 안되는건 아니지만 가독성 및 유지보수 입장에서 bed smell에 해당하기에 이를 리팩토링하는 과정인 것 같습니다. 위와 같은 리팩토링이 아니라 Promise 혹은 async를 이용하면 더 편하게 구현할 수 있다고 합니다.

profile
차근차근 천천히

0개의 댓글