자바스크립트 모듈

패스트 캠퍼스 프론트엔드 개발 스쿨에서 배운 내용을 정리합니다.
내용에 오류가 있다면 댓글 남겨주시기 바랍니다.

모듈

큰 규모의 프로젝트에서 JavaScript 코드를 여러 파일과 폴더에 나누어 작성하고, 충돌하지 않게 서로가 서로를 효율적으로 불러올 수 있도록 해주는 시스템이 필요해졌다.

ES2015 모듈: script태그에 type="module" 어트리뷰트 추가

1
2
<!-- 모듈로서 동작한다, 파일 확장자는 대개 .mjs -->
<script type="module" src="index.mjs"></script>

단, 다음과 같은 이유로 아직 브라우저에 내장된 모듈 기능을 사용하는 경우가 별로 없다.

  • 이전까지의 JavaScript 파일의 동작방식과 다른 동작방식을 가지고 있다.
  • 모듈이 제대로 동작하기 위해 몇가지 조건을 충족시켜야 한다.
  • 구형 브라우저는 모듈을 지원하지 않는다.

대신 Webpack, Parcel 등의 모듈번들러를 통해 변환과정을 거친 뒤, 브라우저에서 일반적인 JavaScript파일로 불러오는 방법이 널리 사용되고 있는 추세이다.

모듈 번들러는 여러 자바스크립트 파일을 설정에 따라 하나 이상의 파일로 합쳐주는 프로그램이다. 최신문법을 트랜스파일링 해주는 기능까지 갖추고 있다.

모듈 번들러들은 파일간 관계를(의존성) 분석해서 모듈을 하나로 합쳐줄 때 사용하는 방식이 각자 다르다.

  • Webpack은 import를 통해 분석한다.
  • Parcel은 html 태그를 보고도 분석한다.(html 안에 link태그 쓰고, scss를 불러오는 식으로 쓸 수 있다.)

1. 모듈이란?

ES2015 모듈: JavaScript코드를 담고 있는 파일

  • import 혹은 export구문을 사용할 수 있다.
  • 기본으로 엄격모드로 동작한다.
  • 모듈 파일의 가장 바깥쪽에서 선언된 이름은 전역 스코프가 아니라 모듈 스코프에서 선언된다.

2. 모듈 스코프

1
2
3
4
5
// variables.js
var foo = 'bar';

// 이 파일이 모듈로서 사용되고 있다면, `undefined`가 출력된다.
console.log(window.foo);

이 모듈스코프는 모듈 파일마다 개별적으로 존재한다.

따라서 여러 모듈의 가장 바깥쪽에서 같은 이름으로 변수, 함수, 클래스를 선언하더라도,
서로 다른 스코프에서 선언되기 때문에 이름의 충돌이 생길 일이 없다.

3. export & import

값이 아니라 이름이 export되는 것

  • 모듈 스코프에서 정의된 이름은 export 구문을 통해 다른 파일에서 사용할 수 있다.

    • variables.js

      1
      2
      3
      4
      const foo = 'bar';
      const spam = 'eggs';

      export {foo, spam};
    • functions.js

      1
      2
      3
      4
      5
      import {foo, spam} from './variables.js';

      console.log(foo);
      console.log(spam);
      `
  • 함수나 클래스도 export를 통해 여러 모듈에서 재사용할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    function add(x, y) {
    return x + y;
    }
    class Person {
    // ...
    }

    export { add, Person };
  • 다른 모듈에 있는 이름을 사용하려면, 반드시 모듈에서 이름을 export 해주어야 한다.
    모듈 실행환경에 따라 에러나, 이름에 undefined가 들어가는 식으로 의도치 않게 동작한다.

4. 선언과 동시에 export 하기

이름을 선언하는 구문 앞에 export를 붙여주면, 선언과 export를 한꺼번에 할 수 있다.

1
2
3
4
5
6
7
export const foo = 'bar';
export function add(x, y) {
return x + y;
}
export class Person {
// ...
}

5. default export

모듈을 대표하는 하나의 을 지정하고, 그 값을 다른 모듈에서 편하게 불러와서 사용할 수 있다.
이렇게 사용하는 값을 default export라고 부른다.

  • foo.js

    1
    export default 'bar';
  • main.js

    1
    2
    3
    4
    // 중괄호를 생략하면, 모듈의 default export를 가져온다.
    import foo from './foo.js'

    console.log(foo); // bar

※ 모듈당 하나의 default export밖에 허용되지 않는다.
모듈에는 하나의 default export와 여러개의 named export(일반적인 export)를 지정할 수 있다.

import 구문에서 default export와 일반적인 export를 동시에 가져올 수 있다.

1
2
                        // node_modules에 설치된 것은 따로 path없이 불러오면 됨
import React, {Component, Fragament} from 'react';

1
2
3
export default class React {}
export class Componet { /*...*/ }
export class Fragment { /*...*/ }

6. 다름 이름으로 export & import 하기

export혹은 import 하는 이름의 뒤에 as를 붙여 다른 이름이 대신 사용되게 할 수 있다.

  • export 이름 뒤에 as

    • foo.js

      1
      2
      const foo = 'bar';
      export {foo as FOO};
    • main.js

      1
      import {FOO} from './foo.js'
  • import 이름 뒤에 as

    • foo.js

      1
      2
      const foo = 'bar';
      export foo;
    • main.js

      1
      2
      import {foo as FOO} from './foo.js'
      console.log(FOO)

7. 모듈 사용 시 주의할 점

모듈 번들러는 import를 하지 않으면 아예 JavaScript코드를 실행시키지 않는다.
모듈간의 의존관계를 다 파악한 뒤 필요한 것만 실행시킨다.

log.js

1
2
console.log('log~~~~~')
export const foo = bar;

main.js

1
2
3
import {foo} from './log'

// log~~~~가 콘솔에 출력된다.

만약 다른 모듈에서도 ‘log.js’를 import 하더라도 ‘log~~~~’는 한번만 출력된다.
이는 import구문이 코드를 실행시키라는 명령이 아니기 때문이다.
exportimport 구문은 단지 모듈간의 의존관계를 나타내는 것일 뿐이다.

ECMAScript 공식 명세에는 모듈을 불러오는 방법에 대한 내용이 포함되어 있지 않다.
모듈번들러, 브라우저마다 모듈을 불러오는 방법이나 구체적인 동작 방식은 다를 수 있다.

8. ES2015 이전의 모듈들

ES2015 모듈 이전에 CommonJS, AMD 등의 모듈 시스템이 있었다.

1
2
3
4
5
6
// Node.js에서 쓰는 모듈 객체
const os = require('os')
module.export = {
a: a
b: b
}
Share Comments