INTRO
개발 도중 다른 모듈을 사용하기 위해 아래와 같은 키워드들을 보거나 사용해 본 적이 있을것이다.
// ES6에서 모듈을 사용하는 방법
import { Express } from 'express';
// CommonJS에서 모듈을 사용하는 방법
const express = require('express');
이번 포스팅에서는 Javascript의 module에 대해 알아본다.
1. Module?
- 모듈(Module)은 다양한 곳에서 다양한 의미로 사용되며, 개발 분야로 범위를 제한해보자면 '프로그램의 다른 부분에서 재사용하고 가져올 수 있는 독립적인 단위로 구성된 코드' 라고 볼 수 있다.
- 그리고 이를 활용하여 아래와 같은 구조로 코드를 작성하는것을 '모듈 패턴을 사용한다' 라고 표현하며, 개발자들에 익숙한 패턴이다.
(혹은 모듈라 프로그래밍 이라고도 표현)
- 모듈은 아래와 같은 제약을 두고 파일 단위로 분리하는것이 일반적이며, 이렇게 개발되어야 개발 효율과 유지보수가 용이하다.
1. 모듈은 가능하면 다른 모듈에게서 독립적이어야 함
2. 특정 기능을 구현하기 위한(목적을 달성하기 위한) 코드들을 하나의 파일에 모아서 관리
3. 재사용성이 좋아야 함
- C에서는 #include, Java에서는 import와 같은 키워드를 통해 모듈을 가져와 사용한다.
- C의 #include는 언어의 개발 초기부터 지원되었고, Java또한 마찬가지로 초기 버전인 1.2부터 모듈을 import하는 기능을 지원했다.
- 이 말은 언어를 개발하는 단계부터 이러한 모듈 패턴을 고려하여 설계했다는 것.
- 반면에 JavaScript는 1995년에 처음 도입되었을 때 모듈을 정의하거나 로드하는 표준화된 방법이 포함되어있지 않았다.
- 그래서 개발자들은 IIFE(Immediately Invoked Function Expression) 패턴이나, <script> 태그를 사용하여 다른 모듈을 정의/로드하는 방법을 사용하였다.
2. IIFE(Immediately Invoked Function Expression) 패턴과 <script> 태그
- IIFE 패턴은 함수를 선언과 동시에 호출한다는 개념으로, 클로저가 활용된다.
var module = (function() {
// private variable
var privateVar = "This is private.";
// private function
function privateFunction() {
console.log("This is private function.");
}
// public API
return {
publicVar: "This is public.",
publicFunction: function() {
console.log("This is public function.");
console.log(privateVar); // private variable 접근
privateFunction(); // private function 호출
}
};
})();
// 모듈 사용
console.log(module.publicVar);
module.publicFunction();
- 이렇게 사용하면 모듈화가 가능하지만, 가독성이 떨어지고 모듈이 늘어날수록 관리가 힘들어진다는 단점이 있다.
- 웹 브라우저에서 모듈을 로드할 때에는 <script> 태그가 주로 사용되었으며, 현대에도 이러한 방식은 자주 사용된다.
// exampleModule.js 모듈을 선언
const exampleModule = {
exampleFunction: function() {
console.log('This is an example function.');
}
};
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Example</title>
</head>
<body>
<!-- 모듈 파일 로드 -->
<script src="exampleModule.js"></script>
<script>
// exampleModule을 사용하여 코드를 작성.
exampleModule.exampleFunction();
</script>
</body>
</html>
- 해당 방법도 단점이 존재하는데, 모듈을 로딩하는 순서에 따라 동작이 바뀔 수 있어 수동으로 관리가 필요하며, 캐싱이 불가하고 모듈이 증가할 수록 관리가 힘들어진다.
- 이를 이유로 Javascript 진영에서는 AMD(Asynchronous Module Definition), CommonJS와 같은 모듈 시스템이 제안되었다.
3. AMD(Asynchronous Module Definition)
- 비동기 방식으로 모듈을 정의하고 로드하는 방식이다.
- 아래는 AMD모듈 시스템을 RequireJS 라이브러리를 통해 구현한 간략한 예제이다.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>AMD Example</title>
<script src="https://requirejs.org/docs/release/2.3.6/minified/require.js"></script>
</head>
<body>
<script>
// 모듈 정의
define('math', [], function() {
var add = function(x, y) {
return x + y;
};
var sub = function(x, y) {
return x - y;
};
return {
add: add,
sub: sub
};
});
// 모듈 로딩 및 사용
require(['math'], function(math) {
console.log(math.add(2, 3)); // 5
console.log(math.sub(2, 3)); // -1
});
</script>
</body>
</html>
- 네트워크를 통해 파일을 비동기적으로 내려받아야 하는 웹 브라우저 환경에서 주로 사용되며 define()함수를 사용하는 것이 특징이다.
- 서버 사이드 애플리케이션 진영에서도 비동기로 로드하는 방식으로 사용이 가능하나, 서버 사이드 애플리케이션의 경우 필요한 모듈들을 볼륨에 저장해두고 기동되는 환경이 일반적이기에 CommonJS의 동기 로드 방식이 주로 사용된다.
4. CommonJs의 require(), module.exports
- 기존 Javascript는 웹 브라우저에서만 사용되던 언어였는데, Node.js 를 통해 서버 사이드에서도 Javascript를 사용하게 되면서 모듈 시스템에 대한 필요성이 증가했다.
- CommonJS는 Node.js 에서 이 모듈 시스템을 '정의하고 구현한 하나의 프로젝트' 이다.
https://ko.wikipedia.org/wiki/CommonJS
CommonJS - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전.
ko.wikipedia.org
- 앞서 설명한 AMD 방식도 CommonJS의 모듈 시스템에서 분리되어 나온 방식이라고 한다.
AMD = 비동기 방식으로 모듈을 로드하기 위한 표준 정의,
CommonJS = 브라우저를 탈출한 자바스크립트에서 모듈을 정의/로드하기 위한 표준 정의
이렇게 두 방식의 목적이 다르므로, 분리될 수 밖에 없었을 것 같다.
- 여기서부터 프론트엔드 진영에서 익숙한 require(), module.export 키워드가 등장한다.
// math.js
function add(a, b) {
return a + b;
}
function multiply(a, b) {
return a * b;
}
module.exports = { add, multiply };
// main.js
const math = require('./math');
console.log(math.add(2, 3)); // Output: 5
console.log(math.multiply(2, 3)); // Output: 6
- 잘 알려진 문제점으로는 CommonJS는 가져온 모듈을 객체화하므로, 순환 참조의 위험이 있다.
- 이후 AMD, CommonJS 두 가지 규격을 통일하고자 UMD(Univasal Module Definition) 방식도 등장했고 현재까지도 사용중인 곳이 많다.
- 현대 프론트엔드 프레임워크(React, Vue 등)를 사용하여 개발하는 경우 일반적으로 ES6 이상의 문법을 사용하며,
- ES6(ES2015)부터 Javascript 수준에서 모듈 시스템을 도입하였다.
5. Javascript 수준에서 ES6부터 지원하는 import, export
- ES6부터는 아래처럼 CommonJS나 AMD보다 간략한 문법으로 모듈을 정의/로드 하는 시스템을 제공한다.
- import, export 키워드로 모듈을 가져오고 내보내며, 변수, 함수, 클래스 모두 export할 수 있다.
- CommonJS의 순환참조 문제를 런타임 이전에 발견 가능하므로 이를 방지할 수 있다.
- 이외에도 네트워크 로드에 최적화 된 몇 가지 장점들을 가지고 있다.
// message.js
export const message = "Hello World!";
// main.js
import { message } from './message.js';
console.log(message); // "Hello World!"
마무리
require() 과 import 가 분명 목적이 같아 보임에도 왜 다르게 사용하는가? 에 대한 궁금증에서 시작하다보니 Javascript의 모듈에 대한 역사까지 공부하게 되었다.
현대의 프론트엔드 개발 트랜드는 ES6 의 import, export 를 주로 사용하고, Webpack, Babel등이 거의 필수적으로 사용되므로 이러한 내용에 대해서 깊게 고민할 필요가 많이 없어졌지만, 분명 이러한 내용들은 개발에 있어 도움이 되는 내용들이므로 미리 정리하고 넘어가는 것이 좋겠다.
참조 : https://d2.naver.com/helloworld/12864
https://d2.naver.com/helloworld/591319
https://poiemaweb.com/es6-module
-퍼가실 때는 출처를 꼭 같이 적어서 올려주세요!
'Dev > [Javascript]' 카테고리의 다른 글
Data Fetching 코드를 공통 모듈로 추상화 해보기 with React (0) | 2024.05.17 |
---|---|
[Javascript] 자바스크립트의 컴파일에 대하여 (0) | 2023.07.14 |
[Javascript] XMLHttpRequest와 Ajax에 대한 이야기 (0) | 2023.03.08 |
[javascript] 렉시컬 환경(Lexical Environment) (0) | 2023.01.17 |
[javascript] 스코프(Scope) (0) | 2023.01.16 |