프로그래밍/프론트엔드

javascript 콜백문제 발생 이유

브래드 킴 2023. 1. 31. 15:33
728x90

비동기, 콜백, promise, async/await등 javascript 관련한 글들을 읽다보면, 동기식 코딩에 익숙한 java 등의 개발자들은 다소 어리둥절 할 것 같다. 일단, 콜백상황자체가 일어나는 이유를 이해하기가 힘들기 때문이다.

오늘은 왜 자꾸 자바스크립트에서 비동기이슈가 튀어나오고, 그에 따른 콜백은 왜 나오는 것인지에 대해 글을 써볼까 한다. 콜백지옥 및 콜백지옥을 해결하기 위한 promise, async/await 등은 이 글을 읽고 난 뒤에 다시 검색해 보면 더 잘 이해가 될 것이다.

먼저, 콜백이라는 것은 "A함수 실행 -> B함수 실행 -> C함수 실행" 등 일련의 실행 순서를 보장하기 위해서 A함수 안에 또다른 B함수를 매개변수로 집어 넣고, B함수안에 C함수를 매개변수로 넣는것을 말한다.

그런데, 왜 순서를 보장하기 위해 저런 방법을 써야 하지? 순서를 보장한다는 것은 동기식 아닌가? 동기식에서는 그저 순차적으로 코딩만 하면 됐었는데, 왜 javascript에서는 콜백이 필요한거지?

javascript 언어자체가 비동기적인가?
물론, 그것은 아니다. 자바스크립트도 기본적으로 코드가 순차적으로 실행되는 동기적 동작을 한다.

함수 안에 함수를 매개변수로 넣는 연쇄적인 콜백이 필요한 이유는, 콜백의 스타트가 되는 맨처음 실행되는 함수(위의 A함수)가 비동기함수이기 때문이다. 비동기함수는 말그대로 비동기적으로 동작하기 때문에, A함수의 결과값을 A함수 실행 이후의 함수에서 사용하고자 할때 그 결과값을 순차적으로 가져오는 것이 보장되지 않게 된다.

즉, javascript에서 코드가 동기식으로 코딩이 되어 있으면, 함수에 함수를 호출하는 콜백상황 자체가 일어날 이유가 없다. 그러나, 코드라인의 어디선가 비동기적인 코딩이 있었고, 이 비동기함수를 통해 받아 오는 데이터를 코드의 다음라인에서 사용하기 위해서는 비동기식이 처리한 결과값을 넘겨받아 순차적으로 처리하기 위한 콜백로직이 필요하게 되는 것이다.

그렇다면 왜 유독 javascript에서만 비동기적인 상황이 언급될까?

이는 자바스크립트가 화면단에서 시작된 언어이기에 서버에 data를 호출하는일이 빈번했고, 서버에서 데이터를 가져오는데는 시간이 오래 걸린다. 이는 빠른 화면렌더링을 어렵게 만든다. 만약 script 안에 서버data요청을 하는 코드가 있고, 이 코드들이 동기적으로 실행되게 되면 서버에서 응답이 올때까지 나머지 script가 실행되지 않아, 유저 입장에선 화면로딩이 느려 답답한 상황이 벌어진다. 이를 위해, 외부통신을 해야 하는 상황에서 비동기적으로 동작되도록 설계된 비동기 함수를 사용하게 된다.

javascript에서 대표적으로 api 호출 및 통신에 사용되는 fetch 함수가 비동기함수이다. react등에서 많이 사용되는 axios도 마찬가지로 비동기객체인 Promise객체를 return하는 비동기함수이다. 외부와 통신하는데는 아무래도 시간이 오래 걸릴수 밖에 없기 때문에 비동기적인 함수를 구현해놓고 사용을 하게 되는 것이다.

예를 들어 아래와 같은 코드가 있다고 하자, 이때 fetch함수는 비동기함수이기 때문에, 빨간박스로 묶인 로직이 코드전체적으로 보았을때는 통으로 비동기적으로 움직이다. 그러나, 해당 박스내에서 then으로 이어지는 일련의 콜백함수는 실행의 순서가 보장되기 때문에 내부적으론 동기적으로 움직이게 되는 것이다. (편의상 콜백지옥에 빠지는 콜백함수가 아니라, Promise객체 형식을 사용했습니다.)

실행을 시켜보면 hello1 -> hello2 -> 빨간박스내부실행(result1 -> resulet2) 실행순서가 결정되게 된다. 이유는 빨간박스에는 fetch가 있어 비동기적으로 코드가 실행되게 되었고, 서버와의 통신은 시간이 오래 걸리므로 빨간박스통째가 한묶음으로 마지막으로 실행되게 되는 것이다.

빨간박스 내부에서는 then -> then 등으로 콜백을 하기에 순서가 보장될 수 있어, result1 -> result2의 순서가 보장되게 된다. 물론, 현실코드에서는 fetch를 통해 받아온 데이터를 순차적으로 처리하는 로직이 들어가 있을 것이다.

결론)
javascript는 태생적으로 프론트언어이다 보니 비동기함수의 필요성이 있는 언어였고, 이 비동기함수를 사용하다보니 발생할 수 있는 순차적 실행문제를 콜백으로 풀수 밖에 없었다. 그런데, 콜백 방식은 함수안에 함수를 넣게 되는 복잡하고 가독성이 떨어지는 구조를 가지고 있었다. 이 어려움을 풀기 위해 Promise객체형식과 이것의 발전된 형태인 async/await방식이 나타나게 된 것이다.

728x90