모듈이란 여러 기능들에 관한 코드가 모여있는 파일로 다음과 같은 기능이 있다.
CommonJS
이다. 현재 NodeJS에서는 이 방식을 사용하고 있다.require
를, 모듈을 해당 스코프 밖으로 보낼 때에는 module.exports
를 사용한다. webpack
은 CommonJS를 지원하기 때문에 NodeJS
가 아닌 환경에서도 사용 가능하다.//a.js
const printHelloWolrd = () => {
console.log('hello world')
}
module.exports = { printHelloWorld }
//b.js
const aObj = require('./a.js') // 같은 dir라고 가정.
aObj.printHelloWorld() // 'hello world'
module.exports
의 module
은 현재 모듈에 대한 정보를 갖고있는 객체이다. id
, path
, parent
등 의 속성이 있고 exports
라는 프로퍼티를 갖고 있다.
exports
vs module.exports
module.exports
는 빈 객체를 참조한다.exports
는 module.exports
를 참조한다.require
는 항상 module.exports
를 리턴받는다.즉, 함수를 모듈 밖으로 내보내고자 할 때는 아래 2가지를 모두 사용할 수 있다.
exports.printHelloWolrd = printHelloWolrd
module.exports = { printHelloWolrd }
이는 exports
에 어떤 값을 할당하거나 새로운 객체를 할당했다고 하더라도 결국 require
는 module.exports
를 리턴받기 때문에 가능하다.
CommonJS의 모듈 명세는 모든 파일이 로컬 디스크에 있어 필요할 때 바로 불러올 수 있는 상황을 전제로 한다. 다시 말해 서버사이드 JavaScript 환경을 전제로 한다.
따라서 필요한 모듈들을 다운받을 때 까지 브라우저에서는 아무것도 할 수 없는 문제가 생긴다. CommonJS 그룹은 처음에 <script>
태그를 동적으로 삽입하는 방법을 사용했다. 하지만 브라우저에서는 파일들간에 scope가 구분되지 않기 때문에 전역변수가 오염될 가능성이 높다. 이를 해결하기 위해 CommonJS 그룹은 모듈 전송 포맷 이라는 것을 만들었다.
모듈 전송 포맷의 예시는 여기를 참고하자.
CommonJS 그룹에서 의견이 맞지 않아 나온 사람들이 만든 그룹으로 비동기 모듈에 대한 표준안을 다루는 그룹이다
define
함수( 클로저를 이용한 패턴)를 이용해 모듈을 구현하므로 전역변수 문제가 없다. 또한 필요할 때 모듈을 불러오는 방식으로 lazy-loading
기법을 응용할 수 있다.require
와 define
이며, AMD 스팩을 가장 잘 구현한 모듈로더는 RequireJS
이다.<DOCTYPE html>
<html lang="en">
<head>
<title>hi</title>
</head>
<body>
<script data-main="index.js" src="require.js"></script>
</body>
</html>
require.js
가 로드되면 index.js
가 실행되는 구조이다.
require.config({
baseUrl: '/',
paths: {
a: 'a',
b: 'b'
}
})
require(['a'], (a) => { a.printA() })
require.config
는 설정부분으로 기본 경로와 각 모듈에 해당하는 경로를 설정해준다. 그 다음 require
를 통해서 첫번째 인자에 해당하는 모듈이 로드되었을 경우에 그걸 a
로 받아서 printA()
함수를 호출하는 콜백함수를 실행한다. 의존성 모듈을 지정해주는 것이다.
define(()=>{
return {
printA: () => console.log('a')
} // 네임스페이스 객체 반환
})
모듈을 구현하는 파일이다. define
함수를 통해서 네임스페이스화 한다.
babel
의 @babel/plugin-transform-modules-commonj
를 통해 변환시켜서 사용한다. 모듈 A,B가 있고 각각을 export
로 내보내는 방식이다.//A.js
const A = () => {}
export default A
//B.js
export const B = () => {}
//index.js
import A from './A.js'
import { B } from './B.js'
위와 같은 방법도 있고 *
와일드 카드를 이용해서 한번에 내보내고 가져오기도 한다.
그 밖의 모듈 시스템으로는 UMD
라는게 있다. 모듈 시스템을 AMD
와 CommonJS
두 그룹으로 나누다보니 호환성에 문제가 많았다. 그래서 나온것이 UMD
이다. 자세한 내용은 여기 를 참고하자.
지금까지가 모듈에 관한 개념이었다! 마지막으로 모듈 번들러와 트랜스파일러에 대해 정리해보자~~
모듈단위의 개발은 다음과 같은 문제를 발생시킬 수 있다.
위 문제들을 해결하기 위해 등장한 것이 모듈 번들러(Module Bundler)로 각각의 모듈 의존성을 해결하여 하나의 자바스크립트 파일로 만듦과 동시에 ES6+ 스펙을 ES5로 변환해주는 도구이다. 대표적인 번들러로는 Webpack, Parcel, Rollup 등이 있다.
예시로는 다음과 같다.