프런트 개발 환경을 이해하기 위한 필수적인 개념인 모듈(module), 웹팩(webpack)과 같은 번들러(bundler)를 왜 사용하는지 정리해보고자 한다
1. 모듈(module)이란?
모듈은 파일 하나하나, 특정 기능을 갖는 작은 코드 단위를 의미한다
우리가 개발을 하면서 규모가 커지면 언젠가 파일을 여러 개로 분리해야 하는 시점이 온다
이때 분리된 파일 각각을 모듈이라고 부르는 것이다
목적에 따라, 기능별로 여러 개의 파일로 분리해서 관리할 수 있으며 모듈로 분리하는 과정을 모듈화라고 한다
2. 모듈(module)은 왜 필요한가?
- 네임스페이스 관리: 전역 네임스페이스의 오염을 방지하여 이름 충돌을 피할 수 있고, 각 모듈은 자체 스코프를 가지고 있어 다른 모듈의 변수나 함수와 독립적이다
- 재사용성: 공통 기능을 모듈로 분리하여 다른 프로젝트나 프로젝트의 다른 부분에서 재사용할 수 있다
- 의존성 관리: 모듈은 다른 모듈에 대한 의존성을 명시적으로 선언하고, 이를 통해 의존성을 관리하고 코드의 실행 순서를 제어할 수 있습니다.
- 유지보수성: 기능별로 코드를 모듈화함으로써, 코드의 유지보수성이 향상된다 모듈은 독립적으로 개발, 테스트, 디버깅될 수 있다
- 지연 로딩: 필요한 모듈만을 로드하거나, 필요할 때까지 로딩을 지연시킬 수 있어, 웹 애플리케이션의 성능을 향상할 수 있게 된다
브라우저 내에서 자바스크립트는 여러 파일로 분리해도 하나의 파일 안에 있는 것처럼 전역(window)을 공유하게 되는데
그래서 어떤 파일을 먼저 실행할지가 중요해지게 됩니다 아래 샘플 소스는 네임스페이스 관리의 오염이 된 케이스 예제입니다
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Global Namespace Pollution Example</title>
</head>
<body>
<script src="script1.js"></script>
<script src="script2.js"></script>
<script>
// script1.js와 script2.js에서 선언한 함수를 호출
console.log(add(2, 3)); // 결과가 충돌로 인해 예상과 다를 수 있음
</script>
</body>
</html>
// script1.js
var add = function(a, b) {
return a + b;
};
// script2.js
var add = function(a, b) {
return a - b;
};
이런 경우 의도한 거는 script1.js가 실행되어 2+3=5의 결과를 원했지만 결과적으로는 script2.js가 실행되어 2-3=-1이라는 결과를 얻게 됩니다
간단한 예시지만 위의 모듈의 정의를 생각하면 모듈화가 되도록 수정하면 아래와 같이 수정될 수 있습니다
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Module Example</title>
</head>
<body>
<script type="module">
import { add as addFunction } from './script1.js';
import { subtract } from './script2.js';
console.log(addFunction(2, 3)); // script1.js의 add 함수를 사용, 5 출력
console.log(subtract(2, 3)); // script2.js의 subtract 함수 사용, -1 출력
</script>
</body>
</html>
// script1.js
export const add = (a, b) => a + b;
// script2.js 다른 이름의 함수를 내보내어 충돌 방지
export const subtract = (a, b) => a - b;
3. 모듈(Module)의 동작
각각의 모듈은 독립적인 스코프를 가져야 하고, 정의된 모듈을 가져다 사용할 수 있어야 한다
위에 수정된 script1.js, script2.js 보면 export를 사용하여 모듈을 내보내고 index.html에서 import 하여 해당 모듈을 가져오고 있다
- export, import를 사용하여 모듈을 내보내고 가져오는 것이 가능
// export
// 단일 값 내보내기
export const myVariable = 123;
// 여러 값 내보내기
export { myFunction, myVariable };
// 기본값으로 내보내기
export default myFunction;
// import
// 특정 값 가져오기
import { myVariable } from './myModule.js';
// 전체 모듈을 객체로 가져오기
import * as myModule from './myModule.js';
// 기본값 가져오기
import myDefault from './myModule.js';
- html파일 내에서 script 태그에 type속성을 module이란 값으로 지정해 사용
<script type="module" src="app.js"></script>
JavaScript의 모듈 시스템은 다양하고, 각기 다른 접근 방식과 목적을 가진 여러 형식이 있습니다
AMD (Asynchronous Module Definition)
- 특징: AMD는 비동기적 모듈 로딩에 초점을 맞춘 설계이고, 이는 웹 환경에서 스크립트 로딩 시 발생할 수 있는 지연 시간을 최소화하기 위해 만들어졌다 RequireJS가 가장 유명한 AMD 모듈 로더이다
- 사용법: AMD는 define 함수를 사용하여 모듈을 정의하고, 모듈의 의존성은 배열로 전달되며, 모듈의 내용은 콜백 함수 내에서 정의된다
define(['dependency1', 'dependency2'], function(dep1, dep2) {
function moduleFunction() {
// 모듈 기능
}
return moduleFunction;
});
CommonJS
- 특징: CommonJS는 주로 서버 사이드에서 사용되며, Node.js에서 가장 널리 사용되는 모듈 시스템이다
동기적 로딩에 초점을 맞추고 있어, 파일 시스템에서 즉시 사용할 수 있는 모듈
- 사용법: CommonJS는 require 함수로 모듈을 로드하고 module.exports 또는 exports 객체를 사용하여 모듈을 내보낸다
// 모듈 가져오기
const moduleA = require('./moduleA');
// 모듈 내보내기
module.exports = function() {
// 함수 내용
};
UMD (Universal Module Definition)
- 특징: UMD는 AMD와 CommonJS 같은 다른 모듈 시스템을 함께 사용할 수 있도록 설계된 패턴
UMD는 서버 사이드와 클라이언트 사이드 양쪽에서 작동하도록 고안되었으며, 모듈 호환성을 극대화하려는 목적으로 만들어졌다
- 사용법: UMD는 조건부 검사를 통해 실행 환경에 따라 적절한 모듈 시스템을 사용하며, 모듈이 AMD 또는 CommonJS 환경에서 실행되는 경우 해당 환경에 맞게 로딩하고, 그렇지 않은 경우 전역 변수를 사용하여 모듈을 정의한다
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD 환경
define(['dependency'], factory);
} else if (typeof exports === 'object') {
// CommonJS 환경
module.exports = factory(require('dependency'));
} else {
// 브라우저 전역 객체
root.returnExports = factory(root.dependency);
}
}(typeof self !== 'undefined' ? self : this, function(dependency) {
// 모듈 정의
return {};
}));
모듈을 브라우저에서 단독으로 사용하는 경우는 흔치 않고, 번들러를 통해 모듈을 번들링해 사용한다
번들러와 번들링을 왜 하는지는 이어서 다음 포스팅에서 정리하겠습니다
References
https://bribrie.tistory.com/84
https://velog.io/@ctdlog/모듈-번들러-톺아보기
'IT > javascript' 카테고리의 다른 글
javascript와 node js의 차이점과 개념 쉽게 정리 하기 (0) | 2024.06.18 |
---|