애플리케이션을 구성하는 개별적 요소로서 재사용 가능한 코드 조각
세부 사항을 캡슐화하고 공개가 필요한 API만을 외부에 노출
동기로 동작하며 서버사이드 JavaScript 환경을 전제로 함
require / exports 키워드를 활용
// myModule.js
// require을 통해 변수에 담기
const calcModule = require('package/calculator');
function addAlert (a, b) {
alert(calcModule.add(a, b));
}
// 다른 모듈로 추출
module.exports = {
foobar: addAlert
};
// main.js
var my = require('myModule');
console.log(my.foobar);
// 동기로 동작
var foo = require('foo');
var bar = require('bar');
foo.log('It is foo');
bar.log('It is bar');
비동기적 모듈 선언으로 필요한 모듈을 네트워크를 이용해 내려받아야 하는 브라우저 환경에서도 모듈을 사용할 수 있도록 표준을 만드는 일
AMD 방식으로 구현된 가장 유명한 스크립트
define / require 키워드를 활용
<script src="require.js"></script>
// myModule.js
// 모듈을 선언부의 첫 번째 파라미터에 넣으면, 콜백 함수의 파라미터 안에 담김
define(['package/calculator'], function (calcModule) {
// 의존 모듈들이 로딩 완료되면 콜백 함수를 실행
// 로드된 종속 모듈 사용
function addAlert (a, b) {
alert(calcModule.add(a, b));
}
// 다른 모듈로 추출
return {
foobar: addAlert
};
});
// main.js
require(['package/myModule'], function (myModule) {
myModule.foobar(19, 2);
});
// 순서가 중요할 때 중첩으로 사용
require(['js/first'], function (first) {
require(['js/second'], function (second) {
//
});
});
코드라기 보다는 디자인 패턴,
AMD와 CommonJS를 쓰는 두 그룹으로 나누어지다보니 서로 호환이 안됨
어떤 모듈을 쓰든지 동작되게 하기 위한 것
(function (root, factory) {
if (typeof define === 'function' && define.amd) { // AMD
define(['jquery', 'moduleA'], factory);
} else if (typeof module === 'object' && module.exports) { // CommonJS
module.exports = factory(require('jquery'), require('moduleA'));
} else { // window
root.myModule = factory(root.$, root.A);
}
}(this, function($, A) {
return {
attr1: $,
attr2: A,
};
});
네이티브 자바스크립트 모듈은 import와 export 문(statement)에 의존적
❗️ 최상위 항목이어야 함 예를들어, 함수 안에서 export를 사용할 수 없음
//square.js
//한 개씩
export const name = 'square';
export const color = 'red';
// 여러 항목을 내보내는 더 편리한 방법
export { name, color };
//main.js
import { name, color } from './modules/square.js';
//square.js
export default randomSquare;
//main.js
import randomSquare from './modules/square.js'; // 괄호 없음
// import {default as randomSquare} from './modules/square.js';
방법 1
// inside module.js
export {
function1 as newFunctionName,
function2 as anotherNewFunctionName
};
// inside main.js
import { newFunctionName, anotherNewFunctionName } from './modules/module.js';
방법 2
// inside module.js
export { function1, function2 };
// inside main.js
import { function1 as newFunctionName,
function2 as anotherNewFunctionName } from './modules/module.js';
import * as Square from './modules/square.js';
import * as Circle from './modules/circle.js';
console.log(Square.name)
console.log(Circle.name)
import('/modules/myModule.js')
.then((module) => {
// Do something with the module.
});
squareBtn.addEventListener('click', () => {
import('//modules/dynamic-module/square.js').then((Module) => {
let square1 = new Module.Square(myCanvas.ctx, myCanvas.listId, 50, 50, 100, 'blue');
square1.draw();
square1.reportArea();
square1.reportPerimeter();
})
});
최신 프런트엔드 프레임워크에서 가장 많이 사용되는 모듈 번들러
자원을 모두 각각의 모듈로 보고 이를 조합해서 병합된 하나의 결과물을 만드는 도구
📃 webpack.config.json
var MiniCssExtractPlugin = require("mini-css-extract-plugin");
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// mode - 개발 / 운영 / none 등 설정
mode: 'production',
// entry - 최초 진입점이자 자바스크립트 파일 경로 : 여러개 가능
entry: {
login: './src/LoginView.js',
main: './src/MainView.js'
},
// output - 웹팩을 돌리고 난 결과물의 파일 경로
output: {
path: path.resolve(__dirname, 'build'),
filename: '[chunkhash].bundle.js' // 캐시 방지
},
// module(loader) - 자바스크립트 파일이 아닌 웹 자원들을 변환할 수 있도록 도와주는 속성
module: { // 해석하고 변환하는 과정에 관여
rules: [
{
test: /\.m?js$/, // 로더를 적용할 파일 유형 (일반적으로 정규 표현식 사용)
exclude: /(node_modules|bower_components)/, // 적용 제외시킬 대상
use: { // 해당 파일에 적용할 로더의 이름
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
},
{
test: /\.scss$/,
use: ['style-loader', 'css-loader', 'sass-loader'] // 오른쪽에서 왼쪽 순으로 적용
}
]
},
// resolve - 파일의 해석방식 정의
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
extensions: ['*', '.js', '.vue', '.json']
},
// plugins - 웹팩의 기본적인 동작에 추가적인 기능을 제공, 해당 결과물의 형태를 바꾸는 역할
plugins: [ // 플러그인의 배열에는 생성자 함수로 생성한 객체 인스턴스만 추가 가능
new MiniCssExtractPlugin(),
new HtmlWebpackPlugin({
// index.html 템플릿을 기반으로 빌드 결과물을 추가해줌
template: 'index.html',
}),
],
// devServer - 파일 변경시 바로 반영되는 서버 사용
devServer: {
historyApiFallback: true,
noInfo: true,
overlay: true
},
// performance - 성능 관련 설정
performance: {
hints: false
},
// devtool - 개발자도구 관련
devtool: 'source-map', // 난독화된 파일를 source탭에서 분리해서 보여주는 속성
};
📃 package.json
{
"name": "getting-started",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack" // npm run build로 실행 (--mode=none) // 설정가능 but 비효율 -> webpackConfig 사용
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"webpack": "^4.42.0",
"webpack-cli": "^3.3.11"
},
"dependencies": {
"lodash": "^4.17.15"
}
}
파일이 변경 되었을 때 매번 웹팩 명령어를 실행하지 않아도 저장하면 바로 반영해주는 서버
명령어를 치고 브라우저를 새로 고침하는 시간, 빌드 시간을 줄여주기 때문에
웹팩 기반의 웹 애플리케이션 개발에 필수로 사용
📃 webpack.config.json
var path = require('path');
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
devServer: {
port: 9000, // 포트 설정
hot: true, // 핫리로드 사용 ㅡ 화면전체 갱신이 아니라 바뀐부분만 리프래시
},
...
};
📃 package.json
{
...
"scripts": {
"dev": "webpack serve --progress", // npm run dev로 서버띄움
"build": "webpack"
},
...
}
웹팩 공식문서
loaders 문서
plugins 문서
웹팩 핸드북 - 캡틴판교님