[ JavaScript ] ES Modules 정리하기

DD·2021년 3월 8일
28

JavaScript

목록 보기
3/5
post-thumbnail

개요

  • 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

  1. 내보내고자 하는 변수, 함수 앞에 export 붙이기
export const a = 1
export function fn(){}
export class Class{}
  1. 묶어서 내보내기
const a = 1
function fn(){}
class Class{}

export { a, fn, Class }

default export

  1. 내보내고자 하는 변수, 함수 앞에 export default 붙이기
// 변수값은 default로 선언, 내보내기가 동시에 되지 않는다
export default const a = 1 // xx

// fn.js
export default function fn(){}

// Class.js
export default class Class{}

default export는 모듈 당 하나만 가능하다.

  1. 선언 후 내보내기
//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 참고 하기

참고

profile
기억보단 기록을 / TIL 전용 => https://velog.io/@jjuny546

2개의 댓글

comment-user-thumbnail
2021년 3월 15일

ES Module은 구성, 인스턴스화, 평가의 단계를 거쳐 동작한다.

브라우저에서 모듈이 어떻게 동작하는지 몰랐었는데, 알게 되어서 정말 좋았습니다! 감사합니다~

답글 달기
comment-user-thumbnail
2023년 9월 26일

잘봤습니다!

답글 달기