자바스크립트의 내용이 많아지면서 자바스크립트 파일을 여러 개로 분리할 필요성이 생기게 되었다. 분리된 각각의 파일을 모듈이라고 부른다. 이렇게 분리된 모듈을 어디서든지 불러올 수 있게 하는 방법을 모듈 시스템이라고 한다. 기존에는 <script> 태그를 이용해서 스크립트 파일을 불러왔다. 이 경우 전역 스코프를 공유하기 때문에 문제가 생길 수 있다.
// a.js
var number = 10;
console.log('a.js > number :', number);
// b.js
var number = 20;
console.log('b.js > number :', number);
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<script src="./a.js"></script>
<script src="./b.js"></script>
<script>
console.log('number :', number);
</script>
</head>
<body>
</body>
</html>
a.js 파일에 number라는 변수를 선언하고 10이라는 값으로 초기화한 후 b.js 파일에서 다시 number 변수를 선언하고 20이라는 값으로 초기화를 하였다. 그 후 index.html 파일에서 두 자바스크립트 파일을 태그를 이용하여 넣고 number 변수를 출력한 결과 20이라는 값으로 출력된 것을 확인할 수 있다. 이는 b.js 파일이 후에 선언되면서 number 변수의 값이 20으로 덮어씌워졌기 때문이다. 만약 a.js 파일이 늦게 선언되게 되면 number 변수의 값은 10이 된다. 하나의 파일 안에 있는 것처럼 동작하는 방식으로 진행되는 것이다.
이를 해결하기 위해 각각의 자바스크립트 파일이 독립적으로 사용되는 모듈 시스템을 만들게 되었다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<script type="module" src="./a.js"></script>
<script type="module" src="./b.js"></script>
<script type="module">
console.log('number :', number);
</script>
</head>
<body>
</body>
</html>
위의 코드와 같은 상황에서 type="module" 속성을 추가해 주었다. 그 결과 해당 파일의 number 들은 각자의 스코프로 사용되기 때문에 index.html 파일에서 number는 정의되지 않은 상태로 출력되게 된다.
모듈로 만들기
// a.js
var number = 10;
console.log('a.js > number :', number);
export default number; // 추가
// b.js
var number = 20;
console.log('b.js > number :', number);
export default number; // 추가
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<script type="module">
import a_number from './a.js'
import b_number from './b.js'
console.log('a_number :', a_number);
console.log('b_number :', b_number);
</script>
</head>
<body>
</body>
</html>
a.js 와 b.js 파일에 export default number를 추가해 주고 index.html에서 import를 통해 두 파일을 선언해 주었다. 이렇게 되면 각 파일에서 export 한 각 number 변수를 a_number, b_number로 가지고 올 수 있게 된다.
모듈의 장점
코드의 양이 적을 경우 크게 상관없을 수 있지만 양이 많아지게 되는 경우 전역스코프로 사용되는 변수명이 동일하게 겹쳐지는 경우가 생기게 될 수 있다. 이 경우 원하는 방향이 아닌 형태로 데이터의 변경이 일어나게 된다. 이를 모듈로 분리하게 되면 각 모듈별로 스코프를 따로 가지게 되기 때문에 그러한 문제를 해결할 수 있다. 또한 같은 코드를 모듈로 만들어둔 후 필요한 순간마다 재사용할 수 있고 의존성이 줄어들 수 있어 기능을 개선하거나 수정하기 용이하다.
export로 내보내기
// module/math.js
export const perfectScore = 100;
export const sum = (num1, num2) => {
return num1 + num2;
}
export const avg = (num1, num2) => {
return (num1 + num2) / 2;
}
const subtract = (num1, num2) => {
return num1 - num2;
};
// module/index.js
import {perfectScore, sum, avg} from './math.js';
console.log('perfectScore :', perfectScore);
console.log('sum :', sum(80, 10));
console.log('avg :', avg(80, 90));
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
<script type="module" src="module/index.js"></script>
</head>
<body>
</body>
</html>
export와 import를 사용하는 방법이다. 이를 사용하면 math.js에서 export를 통해 내보낸 변수들을 import 한 index.js에서 사용할 수 있다. 이때 주의할 점은 이 경우 import 시 변수명을 변경할 수 없다.
+) 별명으로 import 하기
import * as math from './math.js';
console.log('perfectScore :', math.perfectScore);
console.log('sum :', math.sum(80, 10));
console.log('avg :', math.avg(80, 90));
import 시 math.js 파일에 있는 변수들을 math에 담아서 import 하는 방법도 있다. 이 경우 해당 모듈에 존재하는 변수명과 함수들은 위와 같이 꺼내쓸 수 있게 된다.
export default로 내보내기
// module/math.js
export default subtract;
// module/index.js
import subtract from './math.js';
console.log('subtract :', subtract(80, 90));
// 똑같이 동작
import subtract2 from './math.js';
console.log('subtract :', subtract2(80, 90));
export default의 경우 한 모듈 당 하나에만 적용가능한 옵션이다. 이를 이용하면 import 시에 다른 변수명으로 선언해도 사용이 가능하다.
// module/math.js
const perfectScore = 100;
const sum = (num1, num2) => {
return num1 + num2;
}
const avg = (num1, num2) => {
return (num1 + num2) / 2;
}
const subtract = (num1, num2) => {
return num1 - num2;
};
export default {
perfectScore,
sum,
avg,
subtract
};
// module/index.js
import math from './math.js';
console.log('perfectScore :', math.perfectScore);
console.log('sum :', math.sum(80, 10));
console.log('avg :', math.avg(80, 90));
console.log('subtract :', math.subtract(80, 90));
이를 활용하여 모듈 파일에서 전체 변수명과 함수명을 객체로 담아서 export default를 통해 내보내고 이를 특정 변수명으로 import 하면 해당 모듈에 있는 변수와 함수를 편하게 사용할 수 있다.
CommonJS 사용하기
CommonJS모듈의 경우 노드 js 환경에서 사용하기 위해 만들어진 모듈이다.
// commonjs/math.js
exports.perfectScore = 100;
exports.sum = (num1, num2) => {
return num1 + num2;
}
exports.avg = (num1, num2) => {
return (num1 + num2) / 2;
}
exports.subtract = (num1, num2) => {
return num1 - num2;
};
// commonjs/index.js
const {perfectScore, sum, avg, subtract} = require('./math');
console.log('perfectScore :', perfectScore);
console.log('sum :', sum(80, 10));
console.log('avg :', avg(80, 90));
console.log('subtract :', subtract(80, 90));
모듈에서 내보낼 변수나 함수에 exports. 을 붙여주고 가지고올 파일에서 변수와 같이 선언한 후 require을 통해 가지고 온다. 위의 코드를 작성 후 터미널에서 'node commonjs/index.js'라고 작성해 주면 코드가 실행되는 것을 확인할 수 있다.
const math = require('./math');
console.log('perfectScore :', math.perfectScore);
console.log('sum :', math.sum(80, 10));
console.log('avg :', math.avg(80, 90));
console.log('subtract :', math.subtract(80, 90));
위와 같이 math객체에 한꺼번에 담아서 실행하는 방식도 가능하다.
const perfectScore = 100;
const sum = (num1, num2) => {
return num1 + num2;
}
const avg = (num1, num2) => {
return (num1 + num2) / 2;
}
const subtract = (num1, num2) => {
return num1 - num2;
};
module.exports = {
perfectScore,
sum,
avg,
subtract
};
내보내는 모듈의 경우에도 module.exports를 통해 모든 함수와 변수를 하나의 객체에 담아 내보내는 방식도 가능하다.
node js 환경에서 ES6 모듈 시스템 실행하기
node js 환경에서 원래의 상태로 module 폴더 속의 index.js 파일을 실행할 경우 에러가 발생한다. 이때 package.json 파일을 생성하여 다음과 같은 코드를 작성한 후 실행하면 제대로 실행이 되는 것을 확인할 수 있다.
// package.json
{
"type": "module"
}
위의 파일을 추가해 주면 node js 환경에서도 ES6 모듈 시스템 역시 문제없이 실행할 수 있다.
댓글