모듈이란?

모듈이란 구현 세부사항을 캡슐화하고, 공개 API를 노출해 다른 코드에서 쉽게 로드하고 사용할 수 있도록 재사용가능한 코드조각입니다.

ES5의 모듈 패턴

과거에는 웹에서 동작하는 로직이 작아서, js파일의 크기도 작았고 때문에 js를 관리해야할 필요성을 못느꼈다고합니다. 비교적 최근에 이르러서야 js가 엄청나게 커졌다고합니다. ES5 및 이전 버전은 모듈을 염두해두고 디자인되지 않았다고합니다. ES6에 이르러서야 언어적으로 모듈화가 추가되었는데요. 그렇다고 ES6이전에 모듈화가 논의되지 않았던 것은 아닙니다. 수많은 개발자들이 자바스크립트의 모듈화 디자인을 시뮬레이션하기위해 다양한 패턴을 고려했습니다.

이러한 패턴들은 수많은 아이디어와 함께 탄생했는데요.

즉시 실행 함수 표현식(IIFE)와 노출식 모듈(Revealing Module)은 대표적인 아이디어의 시작점이었습니다.

즉시 실행 함수 표현식

(function() {
	console.log('hi');
})();

즉시 실행 함수 표현식은 “선언되었을 때 바로 실행되는 익명함수”입니다.

자바스크립트에서는 function으로 시작되는 줄은 “함수 선언”으로 간주하는데요.

function() {
	console.log('FD');
}

함수 선언은 즉시 실행하면 에러를 던집니다.

function() {
	console.log('FD');
}();

// => Uncaught SyntaxError: Unexpected token (

함수 주위에 괄호를 넣으면 “함수표현식”이 됩니다.

(function() {
	console.log('FD');
})

// => returns f () { console.log('FD'); }

함수 표현식은 함수를 반환하고, 즉시 이 함수를 호출할 수 있습니다.

(function() {
	console.log('FD');
})();

즉시 실행 함수 표현식은 다음과같은 특징이 있습니다.

  • IIFE 내부의 코드 복잡도를 캡슐화하여, IIFE코드가 무엇을하는지 이해하지 않아도 됩니다.
  • IIFE 내부에 변수를 정의하여 전역 스코프를 오염시키지 않습니다.(IIFE내부의 변수는 IIFE의 클로저 안에 남아있습니다.)

하지만, IIFE는 의존성 관리를위한 메커니즘을 제공하지 않습니다.

노출식 모듈 패턴

노출식 모듈 패턴은 IIFE와 유사하지만, 변수에 반환값을 할당한다는 차이점이 있습니다.

var singleton = function() {

	//Inner logic
	function greeting() {
		console.log('Good Morning~');
	}
	
	//Expose API
	return {
		greeting: greeting
	}
}();

이제 모듈 API에 변수를 통해서 접근할 수 있습니다.

singleton.greeting();

모듈은 싱글톤 대신 함수 생성자를 내보낼 수도 있습니다.

var Module = function() {

	//inner logic
	function greeting() {
		console.log('Good morning~');
	}
	
	//expose API
	return {
		greeting: greeting
	}
};

함수를 실행하지 않습니다.

함수를 바로 실행하지 않는 대신, Module 생성자 함수를 사용해서 모듈을 인스턴스화합니다.

var module = new Module();
module.greeting();
// => Good morning~

노출식 모듈패턴은 IIFE와 유사한 장점이 있지만, 마찬가지로 의존성 관리에대한 메커니즘을 제공하지 않습니다.

자바스크립트가 진화하면서 모듈을 정의하기위한 다양한 문법이 개발되었고, 이 문법들은 각각 고유의 장단점을 가집니다.

우리는 이것을 “모듈 포맷”이라고 부릅니다.

모듈포맷

모듈포맷은 모듈을 정의하기위해 사용할 수 있는 “문법”입니다.

ES6이전의 자바스크립트는 앞서말한대로 모듈을 정의하기위한 공식적인 문법을 가지고 있지 않았습니다.

훌륭한 선배들이 자바스크립트에서 모듈을 정의하기 위해 다양한 포맷을 고안해냈는데요..

유명한 모듈포맷들은 다음과 같습니다.

  • 비동기 모듈정의(AMD)
  • CommonJS
  • 만능 모듈정의(UMD)
  • System.register
  • ES6의 모듈 포맷

사실 AMD나 CommonJS나 컴파일되어 나오는 결과물은 조금 다르더라도, 본질적으로 하고자하는 행위는 같기때문에 취향차이일 수 있겠네요.

모듈로더 : 런타임(실행시간)에 스크립트를 로드해서 html에 부착합니다.

모듈로더는 위에서 언급했던 주요 모듈 포맷으로 작성된 모듈을 해석하고 로드합니다.

모듈로더는 런타임에 실행되는데요.

즉, 클라이언트 사이드에서 불러오는 것입니다.

  • 브라우저에서 모듈로더를 로드합니다.
  • 모듈로더에게 어떤 메인 어플리케이션파일을 로드할 것인지 알려줘야합니다.
  • 모듈로더는 메인 어플리케이션 파일을 다운로드하고 해석합니다.
  • 필요한 경우, 모듈로더가 파일을 다운로드합니다.

예를 들어, 우리가 모듈 10개를 불러온다면, 스크립트태그 10개가 추가된다고 보면 됩니다.

지금도 많이 쓰이는 방식이지만, 한가지 아쉬운 점은 스크립트를 이렇게 나눠서 로딩하게 되면 로딩하는데 시간이 매우 많이 걸리게 된다는 것입니다.

하나의 js파일에 쓸 수 있는것을, 나눠서 30개의 js로 코딩해서 모듈로더를 통해 불러오게된다면, 30번 불러오게 됩니다.

브라우저 개발자콘솔에서 네트워크 탭을 열면, 모듈 로더에 의해 30개의 파일들이 로드된 것을 볼 수 있습니다..

http는 비용이 큰 프로토콜이라 매우 큰 부하를 주게된다고 합니다.

그래서 나온 발상이 “빌드시간에 합치면 되지 않을까?”였습니다.

이게 모듈 번들러입니다.

모듈 번들러: 빌드타임(컴파일시간)을 만들어서 한개의 js파일로 내보냅니다.

모듈 번들러는 모듈 로더를 대체합니다.

모듈 로더와는 반대로 모듈 번들러는 빌드타임에 실행됩니다.

  • 빌드 타임에 번들 파일을 생성하기위해 모듈 번들러를 실행합니다.(ex: bundle.js)
  • 브라우저에서 번들 파일을 로드합니다.

기본적으로 js에는 빌드라는 것이 없다고 합니다.

그렇기에 차라리 빌드타임(컴파일 타임)을 만들어내서 한개의 js파일로 보내자는 시도를 하게 된 것입니다.

모듈 번들러는 정말 심플한 역할입니다.

“묶어주는 역할” 이게 끝입니다.

브라우저 개발자 콘솔에서 네트워크탭을 열면, 1개 파일만 로드된 것을 볼 수 있습니다.

요약

모듈: 구현 세부사항을 캡슐화하고 공개API를 노출하여 다른 코드에서 쉽게 로드하고 사용할 수 있는 재사용가능한 코드 조각

모듈 포맷: 모듈을 정의하기위해 사용하는 문법. AMD, CommonJS, UMD, System.register같은 여러 모듈포맷이 등장했으며, ES6부터는 내장된 모듈 포맷을 사용할 수 있다.

모듈 로더: 주요 모듈 포맷으로 작성된 모듈을 런타임때 로드하고 해석. 대표적으로 RequireJS와 SystemJS가 있음.

모듈 번들러: 모듈 로더를 대체하고 빌드타임에 모든 코드의 번들을 생성. 대표적으로 Browerify와 Webpack이 있음.

공부 출처

자바스크립트 모듈, 모듈 포맷, 모듈 로더와 모듈 번들러에 대한 10분 입문서

js를 하나의 파일로 묶기