자바스크립트 모듈로드 방식
회사에서 자바스크립트 모듈을 로드하는 코드를 보고 있는 중입니다.
하여 자바스크립트 모듈 로드하는 방법에 대해서 공부하고자 합니다.
CommonJS
Node.js가 가장 널리 알려진 CommonJS 모듈 시스템을 사용하는 환경
CommonJS 모듈은 자바스크립트에서 재사용 가능한 코드 모듈을 생성하고 사용하기 위한 방법을 제공
CommonJS 모듈의 주요 특징
- 모듈 정의
- 파일 하나가 모듈 하나
- 각 모듈 파일은 자체 스코프를 가지며, 모듈 외부에서 접근할 수 있는 내보내기(Exports)만이 모듈 외부에서 사용
require
- 다른 모듈을 현재 모듈에 가져오기 위해
require
함수를 사용 - 이 함수는 모듈의 경로를 인자로 받아 모듈의 내보내기를 반환
- 다른 모듈을 현재 모듈에 가져오기 위해
exports
객체- 모듈이 공개하려는 기능이나 값을 외부에 내보내기 위해
exports
객체를 사용module.exports
를 사용해 전체 모듈을 대체할 수도 있음
- 모듈이 공개하려는 기능이나 값을 외부에 내보내기 위해
다른 모듈을 사용할 때는 require를, 모듈을 해당 스코프 밖으로 보낼 때에는 module.exports 를 사용하는 방식
hello world를 출력하는 함수를 가진 파일을 funcTest.js
라고 하고 그 함수를 가져와서 사용하는 파일을 funcTest.js
라고 하면 다음과 같이 사용할 수 있음
printHelloWorld.js
const printHelloWorld = () => {
console.log("Hello Wolrd");
};
const printTest = () => {
console.log("test");
};
module.exports = {
printHelloWorld,
printTest,
};
console.log(module);
funcTest.js
const func = require("./printHelloWorld.js");
console.log(func);
func.printHelloWorld();
module.exports
의 module
은 현재 모듈에 대한 정보를 갖고 있는 객체
이는 예약어이며 그 안에 id
, path
, parent
등의 속성이 있고 exports
객체를 가진다.
require
는 항상 module.exports
를 리턴
그래서 require
를 출력해보면 객체 형태로 존재한다.
AMD(Asynchronous Module Definition)
비동기적 모듈 로딩을 지원하는 JavaScript 모듈 정의 시스템
AMD는 주로 브라우저 환경에서의 사용을 목적으로 설계
모듈과 그 의존성이 필요할 때 즉시 로드되도록 한다.
AMD의 가장 대표적인 구현체는 RequireJS
AMD의 주요 특징:
- 비동기 모듈 로딩
- 모듈과 그 의존성들이 비동기적으로 로드
- 이는 페이지 로딩 시간을 단축시키고, 필요한 자원만을 로드하여 성능을 최적화
- 의존성 관리
- AMD는 모듈 정의 시 의존성 목록을 명시적으로 선언
- 모듈이 실행되기 전에 해당 의존성들이 먼저 로드되고 준비되도록 함
- 환경 독립성
- AMD 모듈은 브라우저 뿐만 아니라 다른 JavaScript 환경에서도 사용할 수 있도록 설계
모듈 정의
dependency1
과 dependency2
라는 두 개의 의존성을 가진 모듈을 정의한다.
모듈의 기능은 moduleFunction
에 구현되어 있으며, 이 함수를 모듈의 공개 인터페이스로 내보낸다.
이 의존성 모듈을 정의하는 배열에는 모듈의 식별자(ID)나 파일 경로를 나타내며, 이를 통해 모듈 시스템은 해당 모듈이 실행되기 전에 필요한 모든 의존성들을 먼저 로드하고 준비한다.
define(['dependency1', 'dependency2'], function(dep1, dep2) {
function moduleFunction() {
// 모듈 기능 구현
}
return {
moduleFunction: moduleFunction
};
});
모듈 사용
이 코드는 require
함수를 사용하여 module
을 로드하고, 로드가 완료되면 해당 모듈의 moduleFunction
함수를 실행
require(['module'], function(module) {
module.moduleFunction();
});
AMD 모듈을 실행 예제
RequireJS와 같은 모듈 로더를 사용하여 브라우저에서 실행하는 것이 일반적
여기서는 간단한 HTML 페이지를 만들어 RequireJS를 사용하여 모듈을 로드하고 실행하는 방법을 예시
require.js
를 RequireJS 공식 웹사이트에서 다운로드받아public/scripts/lib
폴더에 저장한다.- 아래 폴더 구조와 파일을 생성
/my-app
/public
/scripts
/lib
- require.js
/models
- user.js
- utils.js
- main.js
- index.html
- server.js
3. 터미널에서 your-app-directory
로 이동한 후 node server.js
명령을 실행하여 서버를 시
4. 웹 브라우저에서 http://localhost:8080
으로 접속합니다. 페이지가 로드되면 main.js
가 실행되어 user.js
와 utils.js
모듈을 로드하고, 콘솔에 "Doing something useful with John Doe" 메시지가 출력
예제 코드
my-app\public\index.html
<!DOCTYPE html>
<html>
<head>
<title>AMD Module Test</title>
</head>
<body>
<h1>AMD Module Loading Example</h1>
<script data-main="scripts/main" src="scripts/lib/require.js"></script>
</body>
</html>
my-app\public\scripts\main.js
require.config({
baseUrl: "scripts",
});
require(["models/user", "utils"], function (User, Utils) {
var user = new User();
Utils.doSomethingUseful(user);
});
my-app\public\scripts\utils.js
define([], function () {
var doSomethingUseful = function (user) {
console.log("Doing something useful with " + user.getName());
};
return {
doSomethingUseful: doSomethingUseful,
};
});
my-app\public\scripts\models\user.js
define([], function () {
var User = function () {
this.name = "John Doe";
};
User.prototype.getName = function () {
return this.name;
};
return User;
});
페이지가 로드되면 main.js
가 실행되어 user.js
와 utils.js
모듈을 로드한다.
main.js
파일은 애플리케이션의 진입점(entry point) 역할을 하며, 여기서 모듈의 로딩, 의존성 관리, 그리고 초기화 코드를 실행
동작원리
HTML 페이지에 RequireJS 스크립트 포함: 웹 페이지의 <script>
태그를 통해 RequireJS를 포함시키고, data-main
속성을 사용하여 애플리케이션의 메인 스크립트 파일(예: main.js
)을 지정한다.
script data-main="scripts/main" src="scripts/lib/require.js"></script>
- 이 태그는 두 가지 작업을 수행합니다
- RequireJS 라이브러리(
require.js
)를 로드 - RequireJS에게
data-main
속성에 지정된main.js
파일을 애플리케이션의 진입점으로 사용하라고 지시
- RequireJS 라이브러리(
- 메인 스크립트 실행: RequireJS는
data-main
에 지정된main.js
파일을 비동기적으로 로드합니다.main.js
는 애플리케이션의 구성, 모듈 정의, 의존성 설정 등을 포함 가능 - 모듈 정의 및 의존성 로딩:
main.js
에서는require
또는define
함수를 사용하여 모듈을 정의하고 의존성을 선언합니다. RequireJS는 이 의존성들을 비동기적으로 로드하고, 모든 의존성이 로드되고 준비된 후에 콜백 함수를 실행 javascriptCopy code require(['models/user', 'utils'], function(User, Utils) { // 모듈 로딩 완료 후 실행될 코드 });
- 초기화 코드 실행: 모든 의존성이 로드되면,
main.js
내의 콜백 함수가 실행됩니다. 이 콜백 함수 내에서 애플리케이션의 초기화 코드나 메인 로직을 실행할 수 있음. 이 단계에서User
와Utils
모듈이 사용
ES6(ES2015)
ES6(ES2015)는 JavaScript에 공식적인 모듈 시스템을 도입한 버전
ES6 모듈은 코드를 모듈화하고 재사용성을 높이며, 의존성 관리를 용이하게 하는 데 중점
이 모듈 시스템은 import
와 export
두 가지 주요 키워드를 사용
ES6 모듈의 기본
- Export: 모듈에서 함수, 객체, 원시 값 등을 외부로 공개할 때 사용합니다. 공개하려는 항목 앞에
export
키워드를 붙임 - Import: 다른 모듈에서 공개된 항목을 가져올 때 사용, 가져오려는 항목을 지정하고, 그 항목이 정의된 모듈의 경로를
from
키워드와 함께 명시.
Export 예제
모듈에서 함수를 내보내는 방법:
// math.js
export function sum(x, y) {
return x + y;
}
export function multiply(x, y) {
return x * y;
}
또는 모듈에서 하나의 객체를 기본으로 내보내는 방법:
// calculator.js
const calculator = {
sum(x, y) {
return x + y;
},
multiply(x, y) {
return x * y;
}
};
export default calculator;
Import 예제
다른 모듈에서 함수를 가져오는 방법:
// app.js
import { sum, multiply } from './math.js';
console.log(sum(1, 2)); // 3
console.log(multiply(3, 4)); // 12
또는 모듈에서 기본 객체를 가져오는 방법:
// useCalculator.js
import calculator from './calculator.js';
console.log(calculator.sum(5, 6)); // 11
console.log(calculator.multiply(7, 8)); // 56
ES6 모듈은 주로 정적 로딩을 위해 설계되었지만, 동적으로 모듈을 로드하는 것도 가능
이는 import()
함수를 사용하여 수행할 수 있으며, 이 함수는 Promise를 반환
import()
를 사용하면 런타임에 필요에 따라 모듈을 로드할 수 있으므로, 코드 분할(code splitting)이나 지연 로딩(lazy loading) 같은 고급 로딩 시나리오를 구현할 수 있음
ES6 모듈로 동적 Import 예제
// 동적으로 'math.js' 모듈 로드
import('./math.js').then(math => {
console.log(math.sum(1, 2)); // 'math.js'에서 'sum' 함수 사용
});
이 예제에서 import()
함수는 'math.js' 모듈을 비동기적으로 로드하고, 해당 모듈이 로드되고 나면 Promise가 성공적으로 해결(resolve)되어 then
메소드의 콜백 함수가 실행
콜백 함수 내에서는 로드된 모듈을 사용할 수 있음
동적 Import의 사용 사례
동적 Import는 다음과 같은 경우에 유용하게 사용될 수 있음
- 코드 분할(Code Splitting)
- 애플리케이션의 시작 시간을 단축하기 위해 초기 로드에 필요하지 않은 코드를 별도의 청크로 분할하고, 필요한 시점에 동적으로 로드
- 지연 로딩(Lazy Loading)
- 사용자가 실제로 필요로 하는 기능이나 컴포넌트를 그 시점에서 로드하여, 애플리케이션의 퍼포먼스를 향상
- 조건부 모듈 로딩
- 실행 시점의 조건에 따라 다른 모듈을 로드해야 할 때 사용할 수 있음
주의 사항
import()
는 Promise를 반환하므로, 모듈 사용 시.then()
메소드를 사용하거나,async/await
구문을 사용하여 처리- 동적 Import를 사용할 때는 모듈 로딩이 비동기적으로 이루어진다는 점을 고려해야 하며, 이로 인해 애플리케이션의 흐름이나 상태 관리에 영향을 줄 수 있음
동적 Import는 ES6 모듈의 유연성을 더욱 확장하며, 현대 웹 애플리케이션의 다양한 요구 사항을 충족시키는 데 도움 준다.
'자바스크립트' 카테고리의 다른 글
자바스크립트 클래스 표현식으로 익명 클래스 사용하기 (0) | 2024.02.29 |
---|---|
자바스크립트 캡슐화 및 모듈 패턴 예시 (0) | 2024.02.22 |
자바스크립트 String.prototype.search() (0) | 2023.10.25 |