정의 및 사용 방법
자바스크립트 애플리케이션 개발 중, 브라우저의 'fetch' 기능이나 Node.js의 'readFile'과 같은 비동기적 동작을 하는 함수를 경험했을 것입니다. 이러한 함수들을 사용할 때, 때로는 예상치 못한 결과를 마주할 수 있는데, 이는 이러한 함수들이 비동기적으로 작동하기 때문입니다. 이 글에서는 비동기 함수의 의미와, 이를 능숙하게 활용하는 방법을 설명하고자 합니다.
동기 함수에 대한 이해
자바스크립트는 한 번에 하나의 작업만을 처리하는 단일 스레드 언어입니다. 즉, 프로세서가 시간이 오래 걸리는 함수를 만나면, 해당 함수가 완전히 실행될 때까지 프로그램의 다른 부분을 처리하지 않고 기다립니다.
대부분의 함수는 프로세서에 의해 직접 실행됩니다. 이는 함수 실행에 걸리는 시간과 관계없이 프로세서 자원을 모두 소모한다는 의미입니다. 이러한 함수를 동기 함수라고 하며, 아래에 예시를 제시합니다.
function add(a, b) {
for (let i = 0; i < 1000000; i ++) {
// 아무 작업도 하지 않음
}
return a + b;
}
// 이 함수를 호출하면 상당한 시간이 걸림
sum = add(10, 5);
// 하지만 프로세서는 sum 값이 계산될 때까지 다음 작업으로 넘어갈 수 없음
console.log(sum);
이 함수는 두 인수의 합을 반환하기 전에 긴 반복문을 실행하여 시간을 소모합니다. 함수를 호출하고 결과를 'sum' 변수에 저장한 후, 그 값을 콘솔에 출력합니다. 'add' 함수 실행 시간이 길더라도, 프로세서는 실행이 완료될 때까지 'sum'의 값을 출력할 수 없습니다.
대부분의 함수는 이처럼 예측 가능한 방식으로 작동하지만, 일부 함수는 비동기적으로 작동하며 일반적인 함수와 다르게 동작합니다.
비동기 함수란 무엇인가
비동기 함수는 대부분의 작업을 프로세서 외부에서 처리합니다. 즉, 함수 실행이 다소 시간이 걸릴 수 있지만, 프로세서는 다른 작업을 처리할 수 있습니다.
다음은 비동기 함수의 예시입니다.
fetch('https://jsonplaceholder.typicode.com/users/1');
효율성을 높이기 위해 자바스크립트는 프로세서가 비동기 함수 실행이 완료되기 전에도 다른 작업을 처리할 수 있도록 합니다.
비동기 함수가 완료되기 전에 프로세서가 다른 작업을 처리하러 이동했기 때문에, 그 결과를 즉시 사용할 수 없습니다. 결과는 아직 대기 중인 상태입니다. 만약 프로세서가 대기 중인 결과에 의존하는 프로그램의 일부를 실행하려고 하면 오류가 발생합니다.
따라서 프로세서는 대기 중인 결과에 의존하지 않는 프로그램 부분만 실행해야 합니다. 이를 위해 최신 자바스크립트는 'Promise'를 사용합니다.
자바스크립트에서 Promise의 역할
자바스크립트에서 'Promise'는 비동기 함수가 반환하는 임시적인 값입니다. Promise는 최신 비동기 프로그래밍의 핵심입니다.
Promise가 생성된 후에는 두 가지 결과 중 하나가 발생합니다. 비동기 함수의 결과 값이 성공적으로 생성되면 '해결(resolve)'되고, 오류가 발생하면 '거부(reject)'됩니다. 이러한 상태 변화는 Promise의 수명 주기 내에서 발생합니다. 따라서 Promise가 해결되거나 거부될 때 호출될 이벤트 핸들러를 연결할 수 있습니다.
비동기 함수의 결과 값이 필요한 코드는 Promise가 '해결'될 때 실행되도록 이벤트 핸들러에 연결할 수 있습니다. '거부'된 Promise의 오류를 처리하는 코드도 해당 이벤트 핸들러에 연결됩니다.
다음은 Node.js에서 파일 데이터를 읽는 예시입니다.
const fs = require('fs/promises');
fileReadPromise = fs.readFile('./hello.txt', 'utf-8');
fileReadPromise.then((data) => console.log(data));
fileReadPromise.catch((error) => console.log(error));
첫 번째 줄에서 'fs/promises' 모듈을 가져옵니다.
두 번째 줄에서는 'readFile' 함수를 호출하여 파일 이름과 인코딩을 전달합니다. 이 함수는 비동기적이므로 Promise를 반환하며, 이를 'fileReadPromise' 변수에 저장합니다.
세 번째 줄에서는 Promise가 '해결'될 때 실행될 이벤트 리스너를 연결했습니다. 'then' 메서드를 사용하여 Promise가 '해결'되면 실행할 함수를 인수로 전달합니다.
네 번째 줄에서는 Promise가 '거부'될 때 실행될 이벤트 리스너를 연결했습니다. 'catch' 메서드를 사용하여 오류 처리 함수를 인수로 전달합니다.

다른 방법으로는 'async'와 'await' 키워드를 사용하는 것이 있으며, 이는 다음 섹션에서 설명합니다.
'async'와 'await'에 대한 설명
'async'와 'await' 키워드는 비동기 자바스크립트 코드를 더 가독성 좋게 작성하는 데 사용됩니다. 이 섹션에서는 이 키워드들의 사용법과 코드에 미치는 영향에 대해 알아보겠습니다.
'await' 키워드는 비동기 함수가 완료될 때까지 함수의 실행을 일시 중지하는 데 사용됩니다. 예를 들어:
const fs = require('fs/promises');
function readData() {
const data = await fs.readFile('./hello.txt', 'utf-8');
// 데이터가 준비될 때까지 이 줄은 실행되지 않음
console.log(data);
}
readData()
'readFile' 함수를 호출하는 동안 'await' 키워드를 사용했습니다. 이는 파일 읽기가 완료될 때까지 프로세서가 다음 줄('console.log')을 실행하지 않도록 지시합니다. 즉, 비동기 함수의 결과에 의존하는 코드가 결과가 제공될 때까지 실행되지 않도록 보장합니다.
위의 코드를 실행하면 오류가 발생합니다. 'await'는 'async' 함수 내에서만 사용할 수 있기 때문입니다. 함수를 비동기 함수로 선언하려면 함수 선언 전에 'async' 키워드를 추가해야 합니다.
const fs = require('fs/promises');
async function readData() {
const data = await fs.readFile('./hello.txt', 'utf-8');
// 데이터가 준비될 때까지 이 줄은 실행되지 않음
console.log(data);
}
// 함수를 실행합니다
readData()
// readData 함수가 완료되기를 기다리는 동안 이 코드가 먼저 실행됩니다.
console.log('데이터 완료를 기다리는 중')
이 코드를 실행하면 자바스크립트가 텍스트 파일에서 읽은 데이터를 사용할 수 있을 때까지 기다리는 동안 외부의 'console.log'가 먼저 실행되는 것을 확인할 수 있습니다. 데이터가 사용 가능해지면 'readData' 함수 내부의 'console.log'가 실행됩니다.

'async' 및 'await' 키워드를 사용할 때 오류 처리는 일반적으로 try/catch 블록을 사용하여 수행됩니다. 또한 루프 내에서 'async/await'를 사용하는 방법도 중요합니다.
'async'와 'await'는 최신 자바스크립트에서 사용 가능하며, 기존에는 비동기 코드를 콜백 함수를 사용하여 작성했습니다.
콜백 함수에 대한 설명
콜백 함수는 비동기 작업의 결과가 준비되면 호출되는 함수입니다. 결과 값이 필요한 코드는 모두 콜백 함수 내부에 배치됩니다. 콜백 함수 외부에 있는 코드는 결과에 의존하지 않으므로 자유롭게 실행될 수 있습니다.
다음은 Node.js에서 파일 읽기를 수행하는 예시입니다.
const fs = require("fs");
fs.readFile("./hello.txt", "utf-8", (err, data) => {
// 이 콜백 함수 안에 결과가 필요한 모든 코드를 작성합니다.
if (err) console.log(err);
else console.log(data);
});
// 여기서는 결과가 필요하지 않은 모든 작업을 자유롭게 처리할 수 있습니다.
console.log("프로그램에서 안녕하세요")
첫 번째 줄에서 'fs' 모듈을 가져왔습니다. 다음으로 'fs' 모듈의 'readFile' 함수를 호출했습니다. 'readFile' 함수는 지정된 파일에서 텍스트를 읽습니다. 첫 번째 인수는 파일이고, 두 번째 인수는 파일 형식을 지정합니다.
'readFile' 함수는 비동기적으로 텍스트를 읽습니다. 이를 위해 함수를 인수로 받습니다. 이 함수 인수는 콜백 함수이며, 데이터를 읽은 후에 호출됩니다.
콜백 함수가 호출될 때 전달되는 첫 번째 인수는 오류이며, 함수 실행 중에 오류가 발생하면 값을 가집니다. 오류가 발생하지 않으면 'undefined' 상태입니다.
콜백에 전달되는 두 번째 인수는 파일에서 읽은 데이터입니다. 이 함수 내부의 코드는 파일 데이터에 접근할 수 있습니다. 이 함수 외부의 코드는 파일 데이터를 필요로 하지 않으므로 파일을 기다리는 동안 실행될 수 있습니다.
위의 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다.

주요 자바스크립트 특징
비동기 자바스크립트의 작동 방식에 영향을 미치는 몇 가지 주요 특징과 특성이 있습니다. 아래 비디오에 잘 설명되어 있습니다.

아래에서 두 가지 중요한 특징을 간략하게 설명합니다.
#1. 단일 스레드
프로그래머가 여러 스레드를 사용할 수 있도록 하는 다른 언어들과는 달리, 자바스크립트는 단 하나의 스레드만을 사용합니다. 스레드는 서로 논리적으로 의존하는 일련의 명령어들입니다. 여러 스레드를 사용하면 차단 작업이 발생했을 때 프로그램이 다른 스레드를 실행할 수 있습니다.
그러나 다중 스레드는 복잡성을 증가시키고, 스레드를 사용하는 프로그램을 이해하기 어렵게 만듭니다. 이는 코드에 버그가 발생할 가능성을 높이고 코드를 디버깅하기 어렵게 합니다. 자바스크립트는 단순성을 위해 단일 스레드로 만들어졌습니다. 단일 스레드 언어로서 차단 작업을 효율적으로 처리하기 위해 이벤트 기반 시스템에 의존합니다.
#2. 이벤트 기반
자바스크립트는 또한 이벤트 기반입니다. 이는 자바스크립트 프로그램의 수명 주기 동안 여러 이벤트가 발생한다는 것을 의미합니다. 프로그래머는 이러한 이벤트에 함수를 연결할 수 있으며, 이벤트가 발생할 때마다 연결된 함수가 호출되어 실행됩니다.
일부 이벤트는 차단 작업의 결과를 사용할 수 있게 되었을 때 발생합니다. 이 경우 관련 함수는 결과와 함께 호출됩니다.
비동기 자바스크립트 코드를 작성할 때 고려해야 할 사항
마지막으로 비동기 자바스크립트 코드를 작성할 때 고려해야 할 몇 가지 사항을 살펴보겠습니다. 여기에는 브라우저 지원, 모범 사례, 중요성 등이 포함됩니다.
브라우저 지원
다음은 다양한 브라우저에서 Promise를 지원하는 정도를 보여주는 표입니다.
출처: caniuse.com
다음은 다양한 브라우저에서 'async' 키워드를 지원하는 정도를 보여주는 표입니다.
출처: caniuse.com
모범 사례
- 항상 'async/await'를 사용하세요. 코드 가독성을 높이고 이해하기 쉽게 만들어줍니다.
- 'try/catch' 블록을 사용하여 오류를 처리하세요.
- 함수의 결과 값을 기다려야 하는 경우에만 'async' 키워드를 사용하세요.
비동기 코드의 중요성
비동기 코드를 사용하면 단일 스레드를 사용하여 보다 효율적인 프로그램을 작성할 수 있습니다. 자바스크립트는 네트워크 요청과 디스크에서 파일을 읽고 쓰는 등 많은 비동기 작업을 처리하는 웹사이트 개발에 사용되므로 비동기 코드는 매우 중요합니다. 이러한 효율성 덕분에 NodeJS와 같은 런타임이 애플리케이션 서버의 기본 런타임으로 인기를 얻을 수 있었습니다.
마무리
이 글은 다소 길었지만, 비동기 함수가 일반적인 동기 함수와 어떻게 다른지 설명할 수 있었습니다. 또한 Promise, 'async/await' 키워드, 그리고 콜백 함수를 사용하여 비동기 코드를 작성하는 방법도 다루었습니다.
자바스크립트의 주요 특징들을 살펴보고, 마지막 섹션에서는 브라우저 지원과 모범 사례를 논의하며 마무리했습니다.
다음으로는 Node.js 관련 자주 묻는 인터뷰 질문들을 확인해보세요.