module
모듈화?
- 모듈화의 사전적 정의는 다음과 같습니다.
- 어떤 큰 문제를 해결하려고 할 때 그대로 놓고 해결하는 것은 매우 어려운 일이다. 따라서 일반적으로 큰 문제를 작은 단위로 쪼개어 그것을 하나씩 해결한다. 소프트웨어 개발에서도 마찬가지이다. 가장 먼저 하는 작업이 실제로 개발할 수 있는 작은 단위로 나누는 것이다. 이렇게 작은 단위로 나누는 것을 '모듈화'라고 한다. [참고]
- 즉 모듈화는 코드의 가독성과 관리의 효율성을 위해 단위 별로 기능을 쪼개어 개별 파일로 만들어 관리하는 것을 의미합니다.
- 자주 사용되는 코드를 별도의 파일로 만들어서 필요할 때마다 재활용할 수 있다.
- 코드를 개선하면 이를 사용하고 있는 모든 애플리케이션의 동작이 개선된다.
- 코드 수정 시에 필요한 로직을 빠르게 찾을 수 있다.
- 필요한 로직만을 로드해서 메모리의 낭비를 줄일 수 있다.
- 한번 다운로드된 모듈은 웹브라우저에 의해서 저장되기 때문에 동일한 로직을 로드 할 때 시간과 네트워크 트래픽을 절약 할 수 있다. (브라우저에서만 해당)
- 모듈화는 파일 스코프를 따른다. 즉 변수와 함수가 파일 내부에서만 유효하다. 따라서 이러한 파일에 접근하는 방법을 export, 불러오는 방법을 import라고 한다.
자바스크립트의 모듈?
자바스크립트는 웹페이지의 단순한 보조 기능을 처리하기 위한 용도로 만들어진 언어이다. 본래는 import와 export를 지원하지 않는다. script로 여러 JS 파일들을 하나의 HTML에서 로드 하면, 모두 하나의 전역 객체를 공유한다. 즉 하나의 파일 내에 있는 것처럼 동작한다. 따라서 전역변수가 중복 될 수 있는 문제가 발생한다. 즉 자바스크립트에서는 표준 모듈 시스템을 제공하지 않았다.
// 모듈화 가 없을 때에는 모두 객체로서 처리
// 각 파일 별로 전역 공간을 지정하는 객체를 설정.
var NAMESPACE = NAMESPACE || {};
NAMESPACE.plus = funvtion(){}
- 따라서 모듈화의 필요성에 따라서 제안 된 것이 CommonJS와 AMD다.
CommonoJS
-
Javascript Everywhere를 목적으로 한 자발적 워킹 그룹(Node.Js 이전)
// 파일을 분리하고 필요한 파일을 주입 받아 활용할 수 있게 한다. // math.js module.exports = { sum: function (a, b) { return a + b; }, }; // sub1.js const sum = require('./math.js').sum; module.exports = { plusTwo: function (a) { return sum(a, 2); }, }; // main.js const plusTwo = require('./sub1.js').plusTwo; console.log(plusTwo(10)); // 12 // main.js <- sub1.js <- math.js
-
장점
- 단순하고 직관적이다.
- 파일을 비동기적으로 로딩 가능한 서버환경에 적합(Node.js).
-
단점
-
script 파일 로딩시 blocking이 발생하는 브라우저 환경에서는 성능저하 발생.
-
이를 극복하기 위해 script 태그 동적 삽입 방식을 활용하나, 깔끔한 해결책이 될 수는 없다.
var myScript = document.createElement('script'); myScript.setAttribute('src', 'script.js'); myScript.setAttribute('crossorigin', 'anonymous'); myScript.setAttribute('async', ''); myScript.onload = function () {}; document.getElementById('root').appendChild(myScript);
-
-
노드 JS에서는 파일 비동기 접근이 되어 그 순간에 바로 변수에 할당한다. 하지만 브라우저 환경에서는 그렇지 않다. 요청은 하지만 동기적으로 이루어지기에 해당 변수에는 undefined가 담기고 넘어간다. 이후 다운로드가 완료되면, 그때 추가 동작이 없기에 변수는 영원히 undefined이다.
AMD (Asynchronous Module Definition.)
- CommonJS가 브라우저환경에서의 비동기 모듈 로드에 대한 관심이 저조한 것에 대한 반감으로 분리되어 나온 집단.
// math.js 배열안에 필요한 의존성이 들어온다.
define([], function () {
function sum(a, b) {
return a + b;
}
return {
sum: sum,
};
});
// sub1.js math는 math.js 에서 return한 sum 객체가 된다.
define(['math'], function (math) {
function plusTwo(a) {
return math.sum(a, 2);
}
return {
plusTwo: plusTwo,
};
});
// main.js
require(['sub1'], function (sub) {
console.log(sub.plusTwo(10)); // 12
});
-
현재는 클라이언트 사이드 자바스크립트에서 동작하는 ES6 모듈을 사용가능하다. (export, import)
- 장점
- browser 환경에서 비동기 네트워크 로딩 처리가 가능.
- 단점
- 의존성 주입 개념을 이해하기에 난이도가 다소 높음.
- 코드가 복잡하며, 주입 소스의 순서를 지켜야 하므로 오류 발생 가능성이 높음
- 장점
-
정리
- 현재는 CommonJS 모듈화를 많이 사용한다. npm으로 설치하고 webpack으로 브라우저의 처리 방식 또한 수정할 수 있기 때문이다. ES6 이전의 코드를 보면 CommonJS로 모듈화가 되어 있다.
- 지금은 ES6의 export와 import를 많이 사용한다.
CommonJS 모듈
- Node.js에서 많이 사용하는 방법이다. export와 require 방식이다.
<script>
태그를 사용하는 브라우저 환경에서는 물론이고, NodeJS에서도 CommonJS를 기본 모듈 시스템으로 채택하고 있기 때문에, Babel과 같은 ES6 코드를 변환(transpile)해주는 도구를 사용할 수 없는 상황에서는 좋든 싫든require
키워드를 사용해야 한다.- 하나의 파일에서 여러 개의 객체를 내보낼 경우,
exports
변수의 속성으로 할당한다. - 하나의 파일에서 하나의 객체를 내보낼 경우,
module.exports
변수 자체에 할당한다.
exports 활용
// Math.js
const plus = (num1, num2) => num1 + num2;
const minus = (num1, num2) => num1 - num2;
const multi = (num1, num2) => num1 * num2;
const division = (num1, num2) => num1 / num2;
//모듈 내보내기
exports.plus = plus;
exports.minus = minus;
exports.multi = multi;
exports.division = division;
console.log(module);
/*
Module {
id: '.',
path: 'C:\\Users\\name\\Desktop\\Github\\codestate\\deepJs',
exports: {
plus: [Function: plus],
minus: [Function: minus],
multi: [Function: multi],
division: [Function: division]
},
filename: 'C:\\Users\\name\\Desktop\\Github\\codestate\\deepJs\\Math.js',
loaded: false,
children: [],
paths: [
'C:\\Users\\name\\Desktop\\Github\\codestate\\deepJs\\node_modules',
'C:\\Users\\name\\Desktop\\Github\\codestate\\node_modules',
'C:\\Users\\name\\Desktop\\Github\\node_modules',
'C:\\Users\\name\\Desktop\\node_modules',
'C:\\Users\\name\\node_modules',
'C:\\Users\\node_modules',
'C:\\node_modules'
]
}
*/
// index.js
//math 변수에 Math.js 파일에서 exports한 객체를 담는다.
const math = require('./Math');
console.dir(math);
math.plus(1, 2);
math.minus(2, 1);
math.division(6, 2);
math.multi(3, 4);
module.exports
//Math.js
const mathObj = {};
mathObj.plus = (num1, num2) => num1 + num2;
mathObj.minus = (num1, num2) => num1 - num2;
mathObj.multi = (num1, num2) => num1 * num2;
mathObj.division = (num1, num2) => num1 / num2;
module.exports = mathObj;
//index.js
const math = require('./Math');
console.log(math.plus(1, 2)); //3
console.log(math.minus(2, 1)); //1
console.log(math.division(6, 2)); //3
console.log(math.multi(3, 4)); //12
ES6 모듈화 (import export)
import
,from
,export
,default
처럼 모듈 관리 전용 키워드를 사용하기 때문에 가독성이 좋다.- 비동기 방식으로 작동하고 모듈에서 실제로 쓰이는 부분만 불러오기 때문에 성능과 메모리 부분에서 유리하다.
- CommonJS에서 지원하지 않는 기능들이 있다.
- Node.js에서
import
를 사용할 떄는 반드시 .js 확장자를 붙여야 한다.
export
-
NodeJS에서 import module을 사용하기 위해서는 다음과 같이 package.json의 수정이 필요하다. type: ‘module’로 변경해주어야 한다. default type은
CommonJs
이기에require
방식으로 모듈화가 진행되기 때문이다. 이를 변경하지 않으면 다음과 같은 Error를 볼 수 있다.”SyntaxError: Cannot use import statement outside a module”
{ "name": "deepjs", "version": "1.0.0", "main": "ex.js", "devDependencies": {}, "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "description": "", "dependencies": { "loadsh": "^0.0.4" }, "type": "module" }
-
이는 브라우저에서
script
방식으로 JS 파일을 로드할 때도 같다. JS 파일에 모듈화를 적용하여 이를 브라우저에서 활용하려면script
의type
attribute에module
을 설정해야 한다.<script src="./src/js/script.js" type="module"></script>
//Math.js export const plus = (num1, num2) => num1 + num2; export const minus = (num1, num2) => num1 - num2; export const multi = (num1, num2) => num1 * num2; export const division = (num1, num2) => num1 / num2; //index.js 방법 1 import { plus, minus, multi, division } from './Math.js'; console.log(plus(1, 2)); //3 console.log(minus(2, 2)); //0 console.log(multi(3, 2)); //6 console.log(division(4, 2)); //2 //index.js 방법 2 import * as math from './Math.js'; // export로 내보낸 모든 메서드 및 파라미터를 math라는 객체에 저장하여 사용하겠다. console.log(math.plus(1, 2)); console.log(math.minus(2, 2)); console.log(math.multi(3, 2)); console.log(math.division(4, 2));
export default
-
export default 키워드를 사용하는 경우 var, let, const 키워드는 사용할 수 없다
.
-
하나의 파일에서 하나의 값만 export 하는 경우 사용한다.
//Math.js
const Mathobj = {};
Mathobj.plus = (num1, num2) => num1 + num2;
Mathobj.minus = (num1, num2) => num1 - num2;
Mathobj.multi = (num1, num2) => num1 * num2;
Mathobj.division = (num1, num2) => num1 / num2;
export default Mathobj;
// index.js
import Math from './Math.js';
console.log(Math.plus(1, 2));
console.log(Math.minus(2, 2));
console.log(Math.multi(3, 2));
console.log(Math.division(4, 2));
- default 키워드와 함께 export한 모듈은
{}
없이 이름으로 import 한다. - 이때 이름은 임의로 정할 수 있지만, 파일의 이름, 혹은 export default한 변수와 동일한 이름으로 호출하는 경우가 일반적이다.
참조
- 모듈화?
- 자바스크립트의 모듈?
- - CommonoJS
- - AMD (Asynchronous Module Definition.)
- CommonJS 모듈
- - exports 활용
- module.exports
- ES6 모듈화 (import export)
- - export
- - export default
- 참조