개요
-
ES Module이란 무엇인가
-
왜 사용하는가
-
어떻게 동작하는가
-
어떻게 사용하는가
ES Module이란 무엇인가?
- ES6에 도입된 모듈 시스템.
- import, export를 사용해 분리된 자바스크립트 파일끼리 서로 접근할 수 있다.
왜 사용하는가?
brower에서의 ESM
기존의 문제점
-
초기 자바스크립트는 독립적인 작업을 수행하며 큰 스크립트가 필요하지 않았다. jQuery가 생겨나고 어플리케이션의 규모가 커지면서 script파일을 나누기 시작했고, 파일간의 변수, 함수 등을 전달하고 받는 방법이 필요했다.
-
ESM 이전에는 각각의 script 파일을 전역 스코프처럼 사용했다. HTML 파일에서 보다 위에 있는 script 파일은 전역 스코프처럼 하위의 script 태그에서 접근, 변경이 가능했다.
-
이 때문에 jQuery script를 최상단에 두고, 순서를 올바르게 구성하는게 중요했다.
- 이 구조는 파일 순서가 뒤틀리면 에러가 발생하고,
- 하위 script가 상위 script의 값을 쉽게 변경시키는 '전역 오염'이 발생하기 쉬우며,
- 해당 script가 어떤 script에 의존성을 갖고 있는지 파악하기 힘들다.
- 즉, 유지보수가 힘들다.
해결책 - 모듈화
-
이러한 문제속에서 모듈화에 대한 필요성이 높아져 ES Module이 등장하게 되었다.
-
모듈은 함수와 변수를 모듈 스코프에 넣고, 각 함수는 함수 스코프를 가진다.
-
다만 export로 해당 변수, 함수를 "다른 모듈에서 import를 통해 의존할 수 있도록" 지정할 수 있다.
-
이러한 모듈화는 다음과 같은 장점을 가진다.
- import - export의 명시적 관계로, 하나의 모듈이 제거되면 어떤 모듈이 손상되었는지 알 수 있다. 즉, 의존성 파악에 용이하다.(A가 import B를 하고 있을 때, B가 사라지거나 오류가 생기면 not found B 에러가 뜨는 등)
- 코드들을 각각 독립적으로 작동할 수 있는 단위로 나누기 수월하다. 이는 모듈을 재사용함으로써 다양한 종류의 어플리케이션을 만들 수도 있다.
- import - export로 관계되지 않은 모듈간의 오염은 일어나지 않는다.
Node.js에서의 ESM
- nodejs는 brower보다 빨리 모듈화를 위한 대책을 보유하고 있었다.
- CommonJS ( require ), AMD, Webpack-Babel 등
- 이중 CommonJS가 가장 널리 사용되었으며, ESM과의 차이는 아래 동작부분에서 설명하겠다.
어떻게 동작하는가
-
브라우저에서 ES Module은 구성, 인스턴스화, 평가의 단계를 거쳐 동작한다.
-
브라우저의 자바스크립트는 파일 자체를 사용할 수 없다. (그런 일은.. 일어나선 안 돼..) 때문에 모듈 레코드(Module Record)라고 하는 데이터 구조로 변환해야한다. 이 과정에서 해당 파일들의 모든 구문을 분석할 필요가 있다.
-
파일을 불러오는 것은 로더(loader)가 하며, 이는 ES 모듈 명세가 아닌 HTML 명세를 따른다. script태그에 type="module"을 적어두어 entry 파일로 지정한다.
구성
- (로더) entry 파일부터 import문을 찾아가며 필요한 모든 파일을 모듈 레코드로 구문 분석한다.
- 모듈이 들어있는 파일을 어디서 다운로드 할 것인지 확인
- 파일을 가져옴(URL을 통해 or 파일 시스템에서)
- 파일을 모듈 레코드로 구문 분석
인스턴스화
- export된 값을 모두 배치하기 위한 메모리 공간을 찾는다.
- export와 import들이 이런 메모리 공간을 가리키도록한다.
- 이를 연결(linking)이라고 한다.
- 이 과정은 메모리 공간을 찾고 지정할 뿐 실제 값을 채우진 않는다.
평가
- 코드를 실행하여 메모리를 변수의 실제 값으로 채운다.
CommonJS와 차이점
CommonJS
- 파일 시스템에서 파일을 로드한다.
- 파일을 불러오는 동안 주 스레드를 차단한다.
- 그렇기에 파일 로드 - 구문 분석 - 인스턴스화 - 평가가 각 파일마다 바로 실행된다.
- 그렇기에 모듈 지정자에 변수를 넣을 수 있다.
- export 객체에 값을 복사해서 넣는다.
ES Module
- entry 파일의 구문 분석 후 의존성(import)을 확인해서 해당 의존성 파일을 찾아서 다시 구문 분석을 반복한다.
- 파일을 불러오는 동안 주 스레드를 차단하지 않는다.
- 인스턴스화, 평가는 더 이상 구문 분석할 의존성이 발견되지 않으면 실행한다
- 그렇기에 모듈 지정자에 변수를 넣을 수 없다(다만 동적 import를 쓰면 가능)
- export는 참조를 반환하는 함수를 정의한다.
- 동적 import는 별개의 entry 파일로 취급되어 새로운 그래프를 만든다.
이 차이는 어떤 결과를 부르는가?
- export하는 파일에서 비동기 처리로 값이 바뀐다면, CommonsJS에서는 반영이 되지 않지만 ESM은 반영 될 수 있다(비동기로 다시 호출한다면).
- 순환 참조의 경우 CommonJS는 빈 객체를, ESM은 RefernceError를 발생시킨다.
- 자세한 설명은 여기에서 확인
어떻게 사용하는가
export
named export
- 내보내고자 하는 변수, 함수 앞에 export 붙이기
export const a = 1
export function fn(){}
export class Class{}
- 묶어서 내보내기
const a = 1
function fn(){}
class Class{}
export { a, fn, Class }
default export
- 내보내고자 하는 변수, 함수 앞에 export default 붙이기
// 변수값은 default로 선언, 내보내기가 동시에 되지 않는다
export default const a = 1 // xx
// fn.js
export default function fn(){}
// Class.js
export default class Class{}
default export는 모듈 당 하나만 가능하다.
- 선언 후 내보내기
//a.js
const a = 1
export default a
// fn.js
function fn(){}
export default fn
// Class.js
class Class{}
export default Class
import
named export를 import하는 경우
import {a} from 'a.js'
import {fn} from 'fn.js'
import {Class} from 'class.js'
defualt export를 import하는 경우
import a from 'a.js'
import fn from 'fn.js'
import Class from 'class.js'
//default의 경우 변수명은 원하는대로 바꿔도 된다.
import DD from 'class.js'
이 외에도 다양한 방식이 있다. mdn / js-examples 참고 하기
참고
브라우저에서 모듈이 어떻게 동작하는지 몰랐었는데, 알게 되어서 정말 좋았습니다! 감사합니다~