eatthefrog

모던 자바스크립트 모듈 Top-Level await (ES2022) 본문

프론트엔드 노트

모던 자바스크립트 모듈 Top-Level await (ES2022)

eater_forg 2025. 1. 2. 17:35

Top-Level Await은 자바스크립트에서 ES2022(ES13)부터 도입된 기능으로, 모듈의 최상위 레벨에서 await를 사용할 수 있도록 허용합니다. 최상위 레벨에서 비동기 작업을 기다릴 수 있게 되어 코드가 더 간단하고 직관적입니다.

 

 

 

1. Top-Level Await이란?

  • 기존에는 await를 함수 내부(특히 async 함수 내)에서만 사용할 수 있었습니다.
  • Top-Level Await은 코딩 과정의 취상위에서 "기다려야하는 일을 기다렸다가" 다음으로 넘어가는 것을 의미한다. 즉, 기다려야 하는 일이 끝날때까지 코드 전체를 멈춥니다. 

예시

//일반 스크립트

function boilWater() {
  return new Promise(resolve => setTimeout(() => resolve("물이 끓었어요!"), 2000));
}

(async () => {
  const water = await boilWater(); // `async` 함수 내부에서만 기다림
  console.log(water); // 출력: 물이 끓었어요!
})();
// 모듈 방식 :  최상위 레벨에서 await 사용

const boilWater = () => new Promise(resolve => setTimeout(() => resolve("물이 끓었어요!"), 2000));

const water = await boilWater(); // 최상위에서 기다림
console.log(water); // 출력: 물이 끓었어요!

2. 스크립트와 모듈의 차이점

2.1 스크립트 (Script)

일반 스크립트 파일(.js)에서는 최상위 레벨에서 await를 사용할 수 없습니다. 이는 await가 비동기 컨텍스트에서만 작동하기 때문입니다.

예시

// script.js
const result = await Promise.resolve("Hello");
// SyntaxError: Unexpected reserved word
  • 이유: 일반 스크립트는 비동기 컨텍스트에서 실행되지 않습니다.
  • 해결 방법: 일반 스크립트에서 await를 사용하려면 async 함수 내부에서 사용해야 합니다.
// script.js
(async () => {
  const result = await Promise.resolve("Hello");
  console.log(result); // 출력: Hello
})();

2.2 모듈 (Module)

ECMAScript 모듈(type="module")에서는 최상위 레벨에서 await를 사용할 수 있습니다. 이는 모듈이 비동기적으로 실행되기 때문입니다.

예시

// 레스토랑 요리 (모듈)
const boilingWater = await boilWater(); // 물이 끓을 때까지 기다림
console.log(boilingWater); // "물이 끓었어요!"
  • 이유: 모듈은 항상 비동기 컨텍스트에서 실행되므로 await를 사용할 수 있습니다.

3. Top-Level Await의 작동 방식

3.1 모듈 로딩 지연

Top-Level Await이 사용된 모듈은 해당 비동기 작업이 완료될 때까지 다른 의존 모듈의 실행을 차단합니다.

예시

// a.mjs
export const data = await fetch("https://jsonplaceholder.typicode.com/posts").then(res => res.json());
console.log("a.mjs 완료");

// b.mjs
import { data } from "./a.mjs";
console.log("b.mjs 완료", data);

출력 결과:

a.mjs 완료
b.mjs 완료 [...data]

b.mjs는 a.mjs의 await 작업이 완료된 후 실행됩니다.


3.2 의존성 간 대기

Top-Level Await이 의존성 모듈에 영향을 미칠 수 있습니다. 여러 모듈이 연결된 경우, 모든 비동기 작업이 체인처럼 순서대로 처리됩니다.


4. Top-Level Await의 주요 사용 사례

  1. 데이터 초기화
    • config.mjs export const config = await fetch("/config.json").then(res => res.json());
    • 앱이 시작되기 전에 필요한 데이터를 가져와, 다른 코드에서 안전하게 사용할 수 있도록 한다.
    • Top-Level Await을 사용하면 데이터를 가져오는 작업을 동기적으로 기다렸다가, 준비된 데이터를 내보내도록 보장할 수 있어요.
  2. 동적 데이터 로드
    • const module = await import("./dynamicModule.mjs");
    • 동적 모듈 로드(import())는 본질적으로 비동기 작업이다. 모듈을 가져오는 작업은 네트워크 요청이나 파일 읽기 등 시간이 걸릴 수 있는 작업을 포함한다. Top-Level Await이 없었다면, 동적 로드 작업을 최상위에서 바로 처리할 수 없고, 비동기 함수 안에서 처리해야 했어야 한다.
      • top-level await없는 비동기 함수에서 동적 모듈 로드하기
      • // dynamicLoader.js
        const loadModule = async () => {
          const module = await import('./someModule.js');
          module.doSomething();
        };

        loadModule();
    • Top-await을 사용하면 코드 실행을 중단하지 않고 필요한 모듈을 로드하고 작업을 이어갈 수 있다.
  3. 테스트 및 디버깅
    • console.log(await Promise.resolve("Testing Top-Level Await"));
    • 비동기 작업의 결과를 쉽게 확인할 수 있다.
    • Top-Level Await을 사용하면 간단히 테스트하고 결과를 출력할 수 있다.

5. 스크립트와 모듈 사용 요약

특징 스크립트 (Script) 모듈 (Module)

최상위 await 사용 지원하지 않음 지원
실행 컨텍스트 동기적으로 실행 비동기적으로 실행
사용 방법 async 함수 내에서만 가능 최상위 레벨에서 바로 사용 가능
모듈 의존성 의존 모듈과 독립적으로 실행 Top-Level Await 사용 시 의존성 차단 가능