기업 공모 자격란에 ESM, ESModule 이라는 단어를 종종 볼 수 있다.
준비해야지~?
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
ES Module
- entry 파일의 구문 분석 후 의존성(import)을 확인해서 해당 의존성 파일을 찾아서 다시 구문 분석을 반복한다.
- 파일을 불러오는 동안 주 스레드를 차단하지 않는다.
- 인스턴스화, 평가는 더 이상 구문 분석할 의존성이 발견되지 않으면 실행하고, 모듈 지정자에 변수를 넣을 수 없다(다만 동적 import를 쓰면 가능)
- export는 참조를 반환하는 함수를 정의한다.
- 동적 import는 별개의 entry 파일로 취급되어 새로운 그래프를 만든다.
+더하기
프로세스(process)란?
- 프로세스(process)란 단순히 실행 중인 프로그램(program)이라고 할 수 있습니다.
- 즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것을 말합니다.
- 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성됩니다.
스레드(thread)란?
- 스레드(thread)란 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미합니다.
- 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행합니다.
- 또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스(multi-threaded process)라고 합니다.
이 차이는 어떤 결과를 부르는가?
- 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 붙이기
export default const a = 1
export default function fn(){}
export default class Class{}
- default export는 모듈 당 하나만 가능하다.
- 선언 후 내보내기
const a = 1
export default a
function fn(){}
export default fn
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'
import DD from 'class.js'
참조
https://velog.io/@jjunyjjuny/ES-Modules-%EC%A0%95%EB%A6%AC%ED%95%98%EA%B8%B0