자바스크립트 module

차민철·2022년 4월 22일
1

안녕하세요. tsconfig와 webpack에서 commonJS와 esModule의 컴파일 결과를 정리하는 도중, 기본적인 지식을 먼저 익혀야 된다고 생각이 들었습니다(이는 추후에 새로운 글로 소개 드리겠습니다). 바로 자바스크립트에서 module이 어떻게 동작하는지 아는게 더 중요하다고 느꼈습니다. 무릇 어떤 지식을 응용하고 활용하려면, 근간이 되는 지식을 먼저 알아야 되기 때문이죠. 그래서 이번 기회를 통해 자바스크립트에서 module에 대하여 정리 하려고 합니다. 목차는 다음과 같이 구성하였습니다.

목차

1.module이 무엇인가
2.자바스크립트의 module
3.commonJS
4.esModule
5.type="module"

1.module이 무엇인가

module이란 어플리케이션의 개별적 요소로 동작하는 코드 조각입니다. 또한 module은 기능을 기준으로 파일 단위로 분리하며, 자신만의 파일 스코프를 가지게 됩니다. 즉, 모듈에 포함되어 있는 변수, 함수, 객체 등은 기본적으로 비공개 상태이며, 캡슐화되어 다른 모듈에서 접근할 수 없습니다. 하지만 완전히 모듈끼리 분리되어 있다면 재사용이 불가능합니다. 따라서 모듈은 공개가 필요한 부분만 선택적으로 공개해야 됩니다. 그리고 다른 모듈쪽에서 공개 된 특정 모듈을 가져와서 재사용합니다. 그렇다면 독립적인 환경을 보장하면서도 코드의 재활용성을 함께 제공 할 수 있습니다.

2.자바스크립트의 module

자바스크립트는 웹페이지의 단순한 보조 기능을 처리하기 위해 태어난 언어입니다. 처음에는 code를 재사용하기 위해 내보내고 가져오는 기능이 존재하지 않았습니다. 그리고 HTML에 script를 JS파일별로 추가하여도, 각 파일마다 고유한 파일 스코프를 가지지 못했습니다. 즉, 분명 파일이 분리되어 있음에도 하나의 자바스크립트 파일 내에 있는 것처럼 동작했습니다. 이렇게 되면 변수들이 중복되고 오염되는 문제가 발생할 수 있게 됩니다. 이러한 태생적 한계를 극복하기 위해 먼저 등장한 라이브러리가 CommonJS와 AMD 입니다. 이로써 CommonJS와 AMD로 구현하여 자바스크립트에서도 모듈 시스템을 갖추게 되었습니다.

3.commonjs

commonJS는 웹 브라우저 밖의 자바스크립트를 실행시키기 위해 등장한 모듈 시스템입니다. 이후 NodeJS에서 commonJS 방식을 지원하게 되었습니다. 그래서 NodeJS는 기본적으로 commonJS를 사용 할 수 있게 되었습니다. 코드를 내보낼 때에는 exports, module.exports를 문법을 사용합니다. 그리고 코드를 가지고 올 때에는 require() 함수를 사용합니다. 그렇다면 과연 웹 브라우저 환경에서 commonJS의 모듈 시스템을 사용할 수 없는 지 확인하겠습니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <h1>commonjs</h1>
    <script src="./index.js"></script>
  </body>
</html>
// index.js
const foo = require("./foo");
const index = "index";
console.log(index + foo);

// foo.js
const foo = "foo";
module.exports = foo;

위와 같이 세팅하고 index.html을 열면 에러 문구를 확인 할 수 있습니다. Uncaught ReferenceError: require is not defined. 그러면 require() 대신에 esModule을 사용한다면 해결 될까요? 이번에는 require() 대신 import로 바꿔보도록 하겠습니다.

import foo from "./foo";
const index = "index";
console.log(index + foo);

이번에는 또다른 에러 문구로 변경되었습니다. Uncaught SyntaxError: Cannot use import statement outside a module. 모듈 외부에서 import 문법을 사용할 수 없다...이게 무엇을 의미할까요? 구글링을 한 결과 HTML script 태그에서 type="module" 속성을 추가해야 된다고 합니다. 그리고 로컬에서 file:// 프로토콜을 사용해 웹페이지를 열면 import, export 지시자가 동작하지 않습니다. 이를 해결하기 위해 vsCode에서 live server extension을 추가하고, live server로 실행하도록 하겠습니다. 링크

...
<script type="module" src="./index.js"></script>
...

그러면 다른 에러를 확인하게 됩니다. Uncaught SyntaxError: The requested module './foo.js' does not provide an export named 'default'. "foo.js"는 "default"라는 구문을 사용하지 않았다는 뜻이기 때문에, 파일을 다시 확인해보니 "default"라는 구문이 존재하지 않습니다. "default"는 esModule 문법이고, "default" 단독으로 못 오기 때문에 "export default"를 앞에다가 붙여봤습니다.

// foo.js
const foo = "foo";
export default module.exports = foo;

이어서 또 다른 에러로 변경되었습니다. Uncaught ReferenceError: module is not defined. 여기서 제가 생각하는 추측으로는 웹 브라우저에선 require()와 module이라는 지시자가 없다고 생각하였습니다. 그래서 이번에는 commonJS 대신에 esModule를 사용 해보려고 합니다.

4.esModule

commonJS가 Node.JS에서 지원하는 모듈 방식이라면, esModule은 브라우저 단에서 지원하는 모듈 방식입니다. 이는 ES6(ES2015)에 표준으로 등재되었습니다. 변수나 함수 앞에 export 지시자를 붙이면 모듈을 내보낼 수 있습니다. 반대로 import 지시자를 붙이면 모듈을 가지고 올 수 있습니다.

// index.js
import foo from "./foo.js";
const index = "index";
console.log(index + foo);

// foo.js
const foo = "foo";
export default foo;

foo.js에서는 foo 변수를 내보내기 위해 export default 지시자를 사용했습니다. 그리고 index.js에서는 이를 가지고 오기 위해 import 지시자를 사용했습니다. 그렇다면 HTML 문서에서 index.js를 적용해야 됩니다. 이때 브라우저에게 index.js가 모듈이란 것을 알려주기 위해 특수한 attribute를 추가합니다.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>
  <body>
    <script type="module" src="./index.js"></script>
  </body>
</html>

script 태그에 type="module"을 속성을 붙여줍니다. 그렇다면 HTML 문서를 열어서 결과를 확인해야 됩니다. 하지만 file:// 프로토콜에서는 import, export이 동작하지 않습니다. 그래서 live server extension을 실행시켜서 HTML 문서를 엽니다. 만약 script 태그에 type="module"을 붙여주지 않는다면 Uncaught SyntaxError: Cannot use import statement outside a module라는 에러가 발생합니다.

type="module"

module이 붙여진 script는 마치 defer 속성을 붙인 것처럼 실행 됩니다. 이는 script를 다운로드 할 때 HTML이 멈추지 않고, HTML 파싱과 함께 script 다운로드를 병렬적으로 처리합니다. 그리고 HTML 파싱이 완료되면 다운로드 된 script를 실행시킵니다.

출처

1.ESM
https://ko.javascript.info/modules-intro
2.commonJS와 ESM 차이점
https://yceffort.kr/2020/08/commonjs-esmodules
https://it-eldorado.tistory.com/92
3.도서
자바스크립트 딥 다이브
이팩티브 타입스크립트

profile
넥슨 -> 플라스크 -> 크래프톤

0개의 댓글