모듈은 import와 export라는 요소를 가지고 있는데, import의 경우 코드에서 사용하기 위한 모듈을 불러오는 기능이고 export는 다른 코드에서 사용할 수 있도록 public API로 만드는 기능이다.
파일 하나 당 모듈 하나를 작성한다.
import나 export는 호이스팅 때문에 함수나 코드 블록 안에서 사용하지 못한다. top-level에서만 사용할 수 있다.
앞선 슬라이드에서 모듈 파일을 다운로드 하는 것은 비동기로 진행된다고 되어 있지만, 모듈을 임포팅하는 것 자체는 동기로 진행된다. 이유는 동기로 진행을 해야 번들링과 사용하지 않는 코드를 삭제할 수 있기 때문이다. 수없이 많은 코드 중 사용해야 하는 함수나 값만 가져오려면 순차적으로 임포팅이 처리되어야 한다.
자바스크립트 모듈에서는 모듈 코드를 카피해서 사용하는 것이 아니라 live connection을 유지하고 있으므로, 모듈의 코드가 바뀌면 실행하는 코드에서도 바뀐 코드가 그대로 적용된다.
모듈 밖에서 사용할 수 있게 하려면 export 키워드를 앞에 붙여야 한다. 여러 개를 묶어서 export 하려면 {} 안에 작성해주고, 이름을 붙이려면 as를 사용한다. export 키워드만 사용한 요소는 named export라고 부른다. import 할 때 해당 이름을 그대로 사용하기 때문이다.
default export는 보통 한 모듈에서 한개의 요소만 export 할 때 사용한다. import 할 때 이름을 원하는대로 import 한다.
console.log('Importing module');
// import { addToCart, totalPrice as price, tq } from './shoppingCart.js';
// addToCart('bread', 5);
// console.log(price, tq);
// import * as ShoppingCart from './shoppingCart.js';
// ShoppingCart.addToCart('bread', 5);
import add, { cart } from './shoppingCart.js';
add('pizza', 2);
add('bread', 5);
add('apples', 4);
console.log(cart);
작성한 모듈을 그냥 사용하려고 하면 Uncaught SyntaxError: Cannot use import statement outside a module가 발생한다. html 파일에서 type=”module”을 명시해줘야 한다. (e.g. <script type="module" defer src="script.js"></script>)
메인 js 파일보다 모듈 js 파일이 먼저 실행되므로 Exporting module이 먼저 출력된다.
named export는 {}를 사용한다. default export 요소는 {} 안에 넣지 않고, 원하는 이름으로 import 하면 된다.
Top-level await(ES2022)
console.log('Start fetching');
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
console.log(data);
console.log('Something');
ES2022부터는 await을 async 밖, 즉 Top-level에서 사용할 수 있게 되었다. 하지만 Top-level에 있으므로 메인 코드의 실행을 블로킹하게 된다. 따라서 필요에 따라 잘 사용해야한다.
또한 모듈에서 Top-level await을 사용할 경우 모듈을 import 하는 코드에서도 블로킹이 발생하기 때문에 더욱 유의해야 한다.
const getLastPost = async function () {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const data = await res.json();
return { title: data.at(-1).title, text: data.at(-1).body };
};
const lastPost = getLastPost();
console.log(lastPost); // Promise 객체
// Not very clean
// lastPost.then(last => console.log(last));
const lastPost2 = await getLastPost();
console.log(lastPost2);
async 함수를 일반적으로 호출해서 데이터를 저장할 경우 Promise 객체가 리턴되지만, await을 사용하면 바로 해당 데이터를 리턴받을 수 있어서 코드가 간결해지고 이해하기 쉬워진다.