애플리케이션을 개발하다 보면 여러가지 기능을 추가하게 되고 계속 새로운 기능을 추가하다 보면 점점 크기가 커지게 됩니다. 소스코드의 크기가 커지며 관리하기가 힘들어지게 됩니다.
개발하는 애플리케이션의을 구성하는 각각의 기능을 가지는 파일을 여러 개로 분리해야 하는 시점이 오게되며 여러개의 파일로 분리하여 애플리케이션을 구성하게 됩니다.
이때 분리된 파일 각각을 '모듈(module)'이라고 부르는데, 모듈은 대개 클래스 하나 혹은 특정한 목적을 가진 복수의 함수로 구성된 라이브러리 하나로 구성됩니다.
모듈은 단지 파일 하나에 불과합니다. 스크립트 하나는 모듈 하나입니다.
모듈에 특수한 지시자 export와 import를 적용하면 다른 모듈을 불러와 불러온 모듈에 있는 함수를 호출하는 것과 같은 기능 공유가 가능합니다.
모듈 내보내기 : export 지시자를 변수나 함수 앞에 붙이면 외부에서 해당 변수나 함수에 접근할 수 있습니다.
모듈 가져오기 : import 지시자를 사용하면 외부 모듈의 기능을 가져올 수 있습니다.
1. 모듈 내보내기/가져오기
sayHi.js 파일을 다음과 같이 만듭니다.
export 명령어를 이용하여 sayHi()함수를 내보내기 하였습니다.
|
1
2
3
4
|
export function sayHi(user){
console.log(`sayHi(${user}) 호출됨!!!`);
return `반갑습니다. ${user}`;
}
|
cs |
module_ex01.html 파일을 다음과 같이 만듭니다.
import 명령어를 이용하여 현재 폴더에 있는 sayHi.js파일의 함수 sayHi를 읽어옵니다.
|
import { 함수이름, .... } from './경로/파일이름';
예) import {sayHi} from './my_module/sayHi.js'; |
모듈을 사용하려면 script태그의 타입이 반드시 type="module"이어야 합니다.

|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈</title>
<script type="module">
import {sayHi} from './sayHi.js';
document.body.innerHTML = sayHi('한사람');
</script>
</head>
<body>
</body>
</html>
|
cs |
실행 결과 입니다.

모듈을 읽어올때는 반드시 경로를 지정해야 합니다. 파일명만 사용하면 다음과 같은 에러가 발생합니다.
| Uncaught TypeError: Failed to resolve module specifier "sayHi.js". Relative references must start with either /", "./", or "../". |
경로를 생략시 발생하는 에러입니다.

별도의 폴더를 만들어 모듈을 보관한다면 다음과 같이 불러오기를 할 수 있습니다.
./my_module/sayHi.js 파일이 있는 경우의 모듈 불러오기
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈</title>
<script type="module">
import {sayHi} from './my_module/sayHi.js';
document.body.innerHTML = sayHi('한사람');
</script>
</head>
<body>
</body>
</html>
|
cs |
실행 화면 입니다.

2. 엄격 모드(use strict)
모듈은 항상 엄격 모드(use strict)로 실행됩니다.
선언되지 않은 변수에 값을 할당하는 등의 코드는 에러를 발생시킵니다.
F12키를 눌러 개발자 모드를 열고 콘솔창을 확인해 봅니다.
module_ex02.html 파일의 내용입니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 엄격 모드로 실행됨</title>
</head>
<body>
<script type="module">
num = 5; // 에러 : Uncaught ReferenceError: num is not defined
</script>
</body>
</html>
|
cs |
다음과 같이 에러가 발생합니다.

3. 모듈의 스코프
에러 : Uncaught ReferenceError: user is not defined
1) user03.js 파일을 다음과 같이 만듭니다.
|
1
|
let user = '한사람';
|
cs |
2) hello03.js 파일을 다음과 같이 만듭니다.
|
1
|
alert(`반갑습니다. ${user}님!!`);
|
cs |
3) module_ex03.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 스코프</title>
<script type="module" src="user03.js"></script>
<script type="module" src="hello03.js"></script>
</head>
<body>
모듈은 자신만의 스코프가 있습니다. <br>
</body>
</html>
|
cs |
4) 실행 결과 화면 입니다.
user라는 변수를 인식하지 못합니다.

4. 반드시 import/export 해줘야만 합니다.
1) user04.js 파일을 다음과 같이 만듭니다.
|
1
|
export let user = '한사람';
|
cs |
2) hello04.js 파일을 다음과 같이 만듭니다.
|
1
2
|
import {user} from './user04.js';
document.body.innerHTML = `반갑습니다. ${user}님!!`;
|
cs |
3) module_ex04.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : export/import</title>
<script type="module" src="./hello04.js"></script>
</head>
<body>
외부에 공개하려는 모듈은 export 해야 하고, 내보내진 모듈을 가져와 사용하려면 import 해줘야 합니다.
</body>
</html>
|
cs |
4) 실행 결과 화면 입니다.
user라는 변수를 인식합니다.

5. 브라우저 환경에서도 type="module"을 사용해 모듈을 만들면 독립적인 스코프가 만들어집니다.
1) module_ex05.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : type="module"</title>
</head>
<body>
<script type="module">
// user는 해당 모듈 안에서만 접근 가능합니다.
let user = "한사람";
</script>
<script type="module">
alert(`반갑습니다. ${user}님!!`); // Uncaught ReferenceError: user is not defined
</script>
</body>
</html>
|
cs |
2) 실행 화면 입니다.

6. 여러 곳에서 사용되더라도 모듈은 최초 호출 시 단 한 번만 실행
동일한 모듈이 여러 곳에서 사용되더라도 모듈은 최초 호출 시 단 한 번만 실행됩니다.
실행 후 결과는 이 모듈을 가져가려는 모든 모듈에 내보내 집니다.
이런 작동 방식은 중요한 결과를 초래합니다. 예시를 통해 이에 대해 알아봅시다.
alert 함수가 있는 모듈(alert06.js)을 여러 모듈에서 가져오기로 해봅시다.
얼럿 창은 단 한 번만 나타납니다.
1) alert06.js 파일입니다.
|
1
2
3
|
console.log('모듈은 최초 호출 시 단 한 번만 실행됩니다.');
alert('모듈은 최초 호출 시 단 한 번만 실행됩니다.');
|
cs |
2) module_ex06.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 단 한 번만 평가됨</title>
</head>
<body>
<script type="module">
import './alert06.js'
</script>
<script type="module">
import './alert06.js'
</script>
<script type="module">
import './alert06.js'
</script>
</body>
</html>
|
cs |
3) 실행 화면 입니다. 콘솔창을 확인해 보세요 3번을 import해도 단 1회만 실행됩니다.

동일한 모듈이 여러 곳에서 사용되더라도 모듈은 최초 호출 시 단 한 번만 실행됩니다.
실행 후 결과는 이 모듈을 가져가려는 모든 모듈에 내보내 집니다.
실무에선 최상위 레벨 모듈을 대개 초기화나 내부에서 쓰이는 데이터 구조를 만들고
이를 내보내 재사용하고 싶을 때 사용합니다.
4) admin07.js 파일을 다음과 같이 만듭니다.
|
1
2
3
|
export let admin = {
name: "최고관리자"
};
|
cs |
5) module_ex07.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 단 한 번만 평가됨</title>
</head>
<body>
<div id="root"></div>
<hr>
<script type="module">
import {admin} from './admin07.js';
document.querySelector("#root").innerHTML = admin.name + "<br/>";
admin.name = '나그네';
</script>
<script type="module">
import {admin} from './admin07.js';
document.querySelector("#root").innerHTML += admin.name + "<br/>";
admin.name = '지나는이';
</script>
<script type="module">
import {admin} from './admin07.js';
document.querySelector("#root").innerHTML += admin.name + "<br/>";
</script>
</body>
</html>
|
cs |
6) 실행 화면 입니다. 3번을 import해도 단 1회만 admin객체의 초기화가 실행됩니다.

7) init08.js 파일을 다음과 같이 만듭니다.
|
1
2
|
import {admin} from './admin08.js';
admin.name = '최고관리자';
|
cs |
8) admin08.js 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
|
export let admin = {};
export function sayHi() {
alert(`${admin.name}님, 안녕하세요!`);
}
|
cs |
9) module_ex08.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 단 한 번만 평가됨</title>
</head>
<body>
<div id="root"></div>
<hr>
<script type="module">
import './init08.js';
import {admin, sayHi} from './admin08.js';
document.querySelector("#root").innerHTML += admin.name + "<br/>";
sayHi();
</script>
</body>
</html>
|
cs |
10) 실행 화면 입니다. 이미 init08.js에서 admin객체가 초기화 되었기 때문에 admin08.js파일의 초기화는 이루어지지 않습니다.

7. 변수와 상수 내보내기
1) data09.js 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
|
// 배열 내보내기
export let names = "한놈,두시기,석삼,너구리,오징어,육개장,칠면조,팔푼이,구급차,십자가".split(",");
export let longevity = "해,산,물,돌,구름,소나무,불로초,거북이,학,사슴".split(",");
export let zodiacSigns = "쥐(자,子)|소(축,丑)|호랑이(인,寅)|토끼(묘,卯)|용(진,辰)|뱀(사,巳)|말(오,午)|양(미,未)|원숭이(신,申)|닭(유,酉)|개(술,戌)|돼지(해,亥)".split("|");
export let heavenlyStems = "갑(甲),을(乙),병(丙),정(丁),무(戊),기(己),경(庚),신(辛),임(壬),계(癸)".split(",");
// 상수 내보내기
export const YEAR = new Date().getFullYear();
export const MONTH = new Date().getMonth() + 1;
export const DATE = new Date().getDate();
export const DAY = new Date().getDay();
|
cs |
하나의 파일에서 export명령를 여러개 사용이 가능합니다.
상수를 내보낼때는 변경할 수 없기 때문에 일반 변수와 구분하기 위해서 모두 대문자로 표기 합니다.
2) module_ex09.html 파일을 다음과 같이 만듭니다.
하나의 모듈에서 내보내기를 여러번 했을 경우에는 "import {가져올 변수, 상수, 함수 ... } from 파일명"을 사용합니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 내보내고 가져오기</title>
</head>
<body>
<div id="root"></div>
<hr>
<script type="module">
import {names, longevity, zodiacSigns, heavenlyStems} from './data09.js';
import {YEAR, MONTH, DATE, DAY} from './data09.js';
console.log(typeof(names) + " : " + names);
for(let name of names) console.log(name);
console.log(typeof(longevity) + " : " + longevity);
for(let evity of longevity) console.log(evity);
console.log(typeof(zodiacSigns) + " : " + zodiacSigns);
for(let sign of zodiacSigns) console.log(sign);
console.log(typeof(heavenlyStems) + " : " + heavenlyStems);
for(let stems of heavenlyStems) console.log(stems);
let dayOfWeek = "일월화수목금토";
console.log(`현재 날짜 : ${YEAR}년 ${MONTH}월 ${DATE}일(${dayOfWeek[DAY]}요일)`);
let output = "<ul>";
for(let name of names) output += `<li>${name}</li>`;
output += "</ul>";
output += "<hr/><ul>";
for(let evity of longevity) output += `<li>${evity}</li>`;
output += "</ul>";
document.querySelector("#root").innerHTML = output
</script>
</body>
</html>
|
cs |
3) 실행 화면 입니다.

8. export / export default
모듈은 크게 두 종류로 나눕니다.
1. 하나의 파일에 복수의 함수가 있는 라이브러리 형태의 모듈
2. 하나의 파일에 하나의 개체 하나만 선언되어 있는 모듈
요약하자면, 1번은 export, 2번은 export default 경우에 해당합니다.
1) calc10.js 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
|
let add = (x, y) => x+y;
let sub = (x, y) => x-y;
let mul = (x, y) => x*y;
let div = (x, y) => x/y;
export {add, sub, mul, div}; // 네 함수를 내보냄
export {add as aa, sub as ss, mul as mm, div as dd}; // 네 함수를 이름 바꿔 내보냄
|
cs |
2) say10.js
|
1
2
3
4
5
|
const Say = () => {
console.log("export default 는 하나의 파일에 개체 하나만 선언되어있는 모듈을 내보낼 때 많이 사용한다.");
}
export default Say;
|
cs |
3) SaySay10.js
|
1
2
3
4
5
6
|
const SaySay = () => {
console.log("Say!!! Say!!!");
}
// 함수 선언부 앞에 'export default'를 붙여준 것과 동일합니다.
export {SaySay as default};
|
cs |
4) module_ex10.html 파일을 다음과 같이 만듭니다.
원하는 모듈만 가져올수 있고 이름을 변경해서도 사용이 가능합니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 내보내고 가져오기</title>
</head>
<body>
<script type="module">
// 필요한 함수만 가져오기
import {add, mul} from './calc10.js'
let x = 20, y=10;
console.log(`${x} + ${y} = ${add(x, y)}`);
console.log(`${x} * ${y} = ${mul(x, y)}`);
console.log("-".repeat(50));
// 필요한 함수만 이름 바꿔서 가져오기
import {add as a, mul as m} from './calc10.js'
console.log(`${x} + ${y} = ${a(x, y)}`);
console.log(`${x} * ${y} = ${m(x, y)}`);
console.log("-".repeat(50));
// calculator란 이름으로 모두 가져오기
import * as calculator from './calc10.js'
console.log(`${x} + ${y} = ${calculator.add(x, y)}`);
console.log(`${x} - ${y} = ${calculator.sub(x, y)}`);
console.log(`${x} * ${y} = ${calculator.mul(x, y)}`);
console.log(`${x} / ${y} = ${calculator.div(x, y)}`);
console.log("-".repeat(50));
// 가져온 후 이름 바꾸기
const aa = calculator.add;
const ss = calculator.sub;
const mm = calculator.mul;
const dd = calculator.div;
console.log(`${x} + ${y} = ${aa(x, y)}`);
console.log(`${x} - ${y} = ${ss(x, y)}`);
console.log(`${x} * ${y} = ${mm(x, y)}`);
console.log(`${x} / ${y} = ${dd(x, y)}`);
console.log("-".repeat(50));
// 이름 바꿔서 내보낸것 사용하기
import * as calculator2 from './calc10.js'
console.log(`${x} + ${y} = ${calculator2.aa(x, y)}`);
console.log(`${x} - ${y} = ${calculator2.ss(x, y)}`);
console.log(`${x} * ${y} = ${calculator2.mm(x, y)}`);
console.log(`${x} / ${y} = ${calculator2.dd(x, y)}`);
console.log("-".repeat(50));
// export default 로 내보내면 {}를 사용하지 않는다.
import Say from './say10.js';
// export default 로 내보낸것 이름바꿔서 사용
import SaySaySay from './say10.js';
Say();
SaySaySay();
import SaySay from './SaySay10.js';
SaySay();
</script>
</body>
</html>
|
cs |
5) 실행 화면 입니다. 개발자 모드의 콘솔창을 참조하시면 됩니다.

9. ‘default’ export 하나와 다수의 named export가 있는 경우
하나의 모듈에 ‘default’ export 하나와 다수의 named export가 있다고 해봅시다. 이때는 어떻게 사용할 까요?
1) Person11.js 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 하나의 모듈에 ‘default’ export 하나와 다수의 named export가 있다고 해봅시다.
export default class Person{
constructor(name='무명', age=18, gender=false){
this.name = name;
this.age = age;
this.gender = gender;
}
toString = ()=>`${this.name}(${this.age}세, ${this.gender ? "남":"여"})`
}
export function greeating(person){
console.log(`${person}`);
console.log(`${person.name}씨 반갑습니다.`);
console.log(`${person.age}세네요 형님이라고 불러라~~~`);
}
|
cs |
2) module_ex11.html 파일을 다음과 같이 만듭니다.
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>모듈 : 내보내고 가져오기</title>
</head>
<body>
<script type="module">
// 하나의 모듈에 ‘default’ export 하나와 다수의 named export가 있다고 해봅시다.
// 아래와 같은 방식을 사용하면 default export와 named export를 동시에 가져올 수 있습니다.
import {default as Person, greeating} from './Person11.js';
let p1 = new Person('한사람',11,true);
let p2 = new Person('두사람',22);
greeating(p1);
greeating(p2);
</script>
</body>
</html>
|
cs |
3) 실행 화면 입니다.

이제 모듈을 자유롭게 사용하시기 바랍니다.
'Front End > Javascript' 카테고리의 다른 글
| [JavaScript] Spread Operator(전개 연산자) 객체의 결합/복사 (1) | 2024.10.17 |
|---|---|
| [JavaScript] Spread Operator(전개 연산자) 배열의 결합/복사 (0) | 2024.10.17 |
| [JavaScript] Spread Operator(전개 연산자) 기본 사용법 (0) | 2024.10.17 |
| [JavaScript] json-server 사용해보기 (6) | 2024.10.17 |
| [JavaScript] Set(중복을 불허하는 컬렉션) 사용하기 (2) | 2024.10.16 |