게발하는 애플리케이션의 크기가 커지면서 파일을 여러 개로 분리하게 되는데 이때 분리된 파일 각각을 '모듈(module)'이라고 부릅니다.
모듈은 대개 클래스 하나 혹은 특정한 목적을 가진 복수의 함수로 구성된 라이브러리 하나로 구성됩니다.
스크립트의 크기가 점차 커지고 기능도 복잡해지자 자바스크립트 커뮤니티에서 특별한 라이브러리를 만들어 필요한 모듈을 언제든지 불러올 수 있게 해준다거나 코드를 모듈 단위로 구성해 주는 방법을 만드는 등을 통해 모듈 시스템을 개발하게 되었습니다.
- AMD - 가장 오래된 모듈 시스템 중 하나로 require.js라는 라이브러리를 통해 처음 개발
- CommonJS - node.js 서버를 위해 만들어진 모듈 시스템
- UMD - AMD와 CommonJS와 같은 다양한 모듈 시스템을 함께 사용하기 위해 만들어짐
위와 같은 모듈 시스템은 현재는 잘 쓰이지 않고, 이후로 관련 문법은 진화를 거듭해 대부분의 주요 브라우저와 Node.js가 모듈 시스템을 지원하고 있다.
모듈이란
스크립트 파일 하나는 모듈 하나입니다.
모듈에 export, import를 적용하면 다른 모듈을 불러와 불러온 모듈에 있는 함수를 호출하는 것과 같은 기능 공유가 가능합니다.
- export 지시자를 변수나 함수 앞에 붙이면 외부 모듈에서 해당 변수나 함수에 접근할 수 있습니다. (모듈 내보내기)
- import 지시자를 사용하면 외부 모듈의 기능을 가져올 수 있습니다. (모듈 가져오기)
모듈은 특수한 키워드나 기능과 함께 사용되므로 <script type="module"> 같은 속성을 설정하여 해당 스크립트가 모듈이란 걸 브라우저가 알게 해줍니다.
아래는 브라우저에서 모듈이 동작하는 방법입니다.
🔎 모듈은 로컬 파일에서 동작하지 않고, HTTP/HTTPS 프로토콜을 통해 동작하기 때문에 vscode liver server를 통해 예시를 확인해야하는 점 주의
say.js
export function sayHi(user) {
return `Hello, ${user}!`;
}
index.html
<!DOCTYPE html>
<script type="module">
import { sayHi } from "./say.js";
document.body.innerHTML = sayHi("John");
</script>
모듈 핵심 기능
일반 스크립트와 모듈에는 약간의 차이가 있습니다. 모든 호스트 환경(자바스크립트가 돌아가는 환경)에 공통으로 적용되는 모듈의 핵심 기능입니다.
엄격모드
모듈은 항상 엄격 모드(use Strict)로 실행됩니다. 선언되지 않은 변수에 값을 할당하는 등의 코드는 에러를 발생시킵니다.
모듈 레벨 스코프
모듈 내부에서 정의한 변수나 함수는 다른 스크립트에서 접근할 수 없습니다.
외부에 공개하려는 모듈은 export 해야 하고, 내보내진 모듈을 가져와 사용하려면 import 해줘야 합니다.
브라우저 환경에서 <script type="module">을 사용해 모듈을 만들면 독립적인 스코프를 가집니다.
단 한 번만 실행
동일한 모듈이 여러 곳에서 사용되더라도 모듈은 최초 호출시 단 한 번만 실행됩니다. 실행 후 결과는 이 모듈을 가져가려는 모든 모듈에 내보내집니다.
정리하면, 모듈은 단 한 번만 실행되고 실행된 모듈은 필요한 곳에 공유되므로 어느 한 모듈에서 특정 코드를 수정하면 다른 모듈에서도 그 변경사항을 확인할 수 있습니다.
이를 이용하면, 최상위 레벨 모듈을 불러와서 초기화나 내부에서 쓰이는 데이터 구조를 만들고 이를 내보내 재사용 하는 등의 모듈 설정을 쉽게 할 수 있습니다.
브라우저에서 모듈(type="module")의 특정 기능
지연 실행
모듈 스크립트는 항상 지연 실행됩니다. 마치 defer 속성을 붙인 것처럼 동작합니다.
- 외부 모듈 스크립트 <script type="module" src="...">를 다운로드할 때 브라우저의 HTML 처리가 멈추지 않습니다. 브라우저는 외부 모듈 스크립트와 기타 리소스를 병렬적으로 불러옵니다.
- 모듈 스크립트는 HTML 문서가 완전히 준비될 때까지 대기 상태에 있다가 HTML 문서가 완전히 만들어진 이후에 실행됩니다.
- 스크립트의 상대적 순서가 유지됩니다. 문서상 위쪽의 스크립트부터 차례로 실행됩니다.
위의 특징 때문에 모듈 스크립트는 항상 완전한 HTML 문서를 보고 문서 내 DOM 요소 등에도 접근할 수 있습니다.
index.html
<!DOCTYPE html>
<script type="module"> // 모듈 스크립트는 지연 실행되기 때문에 페이지가 모두 로드되고 난 다음에 실행됩니다.
alert(typeof button); // Object
</script>
<script> // 일반 스크립트는 페이지가 완전히 구성되기 전이여도 바로 실행됩니다.
alert(typeof button); // undefined
</script>
<button id="button">Button</button>
위의 html을 실행하면 1) undefined 알럿 창 2) Object 알럿 창 + button 이 표시됩니다.
페이지 내 특정 기능이 모듈 스크립트에 의존적인 경우, 모듈이 완전히 로딩되기 전에 페이지가 먼저 사용자에게 노출되면 혼란을 느낄 수 있습니다.
투명 오버레이나 로딩 인디케이터를 보여주어 혼란을 예방할 수 있습니다.
인라인 스크립트의 비동기 처리
모듈 스크립트에서 async 속성을 적용하면, 필요한 모듈의 로드가 끝난 후 문서나 다른 script가 로드되길 기다리지 않고 바로 실행됩니다.
이러한 특징은 어디에도 종속되지 않는 기능을 구현할 때 유용하게 사용할 수 있습니다.
<!-- 필요한 모듈(analytics.js)의 로드가 끝나면 -->
<!-- 문서나 다른 <script>가 로드되길 기다리지 않고 바로 실행됩니다.-->
<script async type="module">
import {counter} from './analytics.js';
counter.count();
</script>
외부 스크립트
type="module" src="..." 가 붙은 외부 모듈 스크립트엔 두 가지 특징이 있습니다.
- src 속성값이 동일한 외부 스크립트는 한 번만 실행됩니다.
<!-- my.js는 한 번만 로드 및 실행됩니다. -->
<script type="module" src="my.js"></script>
<script type="module" src="my.js"></script>
- 외부 사이트같이 다른 오리진에서 모듈 스크립트를 불러오려면 해당 모듈이 저장된 원격 서버가
Access-Control-Allow-Origin : * 헤더를 제공해야만 외부 모듈을 불러올 수 있습니다.
<!-- another-site.com이 Access-Control-Allow-Origin을 지원해야만 외부 모듈을 불러올 수 있습니다.-->
<!-- 그렇지 않으면 스크립트는 실행되지 않습니다.-->
<script type="module" src="http://another-site.com/their.js"></script>
경로가 없는 모듈은 금지
브라우저 환경에서 import는 반드시 상대 혹은 절대 URL 앞에 와야합니다.
Node.js나 번들링 툴은 경로가 없어도 해당 모듈을 찾을 수 있기 때문에 경로가 없는 모듈을 사용할 수 있습니다.
(e.g. import react from 'react')
빌드 툴
브라우저 환경에서 모듈을 단독으로 사용하는 경우는 거의 없습니다.
대게 Webpack 같은 빌드 툴을 사용해 모듈을 번들링하여 서버에 올리는 방식을 사용합니다.
- HTML의 <script type="module"> 에 넣을 '주요' 모듈(진입접 역할을 하는 모듈)을 선택합니다.
- '주요' 모듈에 의존하고 있는 모듈 분석을 시작으로 모듈 간의 의존관계를 파악합니다.
- 모듈 전체를 모아 하나의 큰 파일을 만듭니다.(설정에 따라 여러 파일 만드는 것도 가능) 이 과정에서 import문이 번들러 내 함수로 대체되므로 기존 기능은 그대로 유지됩니다.
- 이 과정 중에 변형이나 최적화도 함께 수행됩니다.
- 도달 가능하지 않은 코드는 삭제
- 내보내진 모듈 중 사용하지 않는 모듈 삭제
- console, debugger 같은 개발 관련 코드 삭제
- 최신 자바스크립트 문법이 사용된 경우 바벨(Babel)을 사용해 버전 변환
- 공백 제거, 변수 이름 줄이기 등으로 산출물의 크기 최적화
번들링 툴을 사용해 하나 혹은 여러 개의 파일로 번들링되면 이전에 import, export문은 특별한 번들러 함수로 대체됩니다.
기존 스크립트에서 import, export가 사라지기 때문에 type="module"이 필요 없어지므로, 번들링 과정을 거친 스크립트는 일반 스크립트처럼 취급할 수 있습니다.
<!-- 웹팩과 같은 툴로 번들링 과정을 거친 스크립트인 bundle.js -->
<script src="bundle.js"></script>
'Language > javascript' 카테고리의 다른 글
[JS] Promise.all() (1) | 2023.10.31 |
---|