모듈(module)
모듈은 간단하게, 자바스크립트 파일 하나라고 할 수 있다.
내가 어떤 프로그램을 만든다고 했을 때 다양한 기능을 추가하다보면 어느순간 한 파일에 작성한 코드가 100줄, 1000줄이 넘어가는 경우가 생긴다. 하나의 파일에 많은 코드를 작성하게 되면 각 코드들의 의미를 빠르게 파악하기가 어렵고 기능수정이 필요할 때도 그부분을 찾기가 쉽지 않아 코드를 관리하기 어려워진다. 그래서 많은 기능이 필요한 프로그램은 하나의 파일로 관리하는게 아니라 각 기능별로 여러개의 파일로 분리해서 관리하는 것이 좋다.
공통된 기능이나 특별한 목적에 따라 각각의 파일로 분리하는 과정을 모듈화(Modularization)라고하고 각 파일 하나하나를 모듈이라고 부른다.
복잡하고 많은 양의 코드를 기능에 따라 각각의 파일로 나눠 관리하면
1. 코드를 좀 더 효율적으로 관리할 수 있고,
2. 비슷한 기능이 필요할 때 다른 프로그램에서 재사용 할 수도 있다는 장점이 있다.
모듈 파일이 가져야 할 조건
1. 모듈이 되는 파일은 이 파일만의 독립적인 스코프를 가져야 한다. 모듈 파일이 가지는 독립적인 스코프를 모듈 스코프 라고 한다.
모듈 스코프(Module Scope)
모듈 파일 안에서 선언한 변수나 함수는 외부에서 자유롭게 접근할 수 없고, 이 파일 안에서만 사용이 가능해야한다.
하나의 폴더 안에 index.html, index.js, print.js 파일들이 위치해 있다고 생각해보자. 서로 다른 파일에 자바스크립트 코드가 존재하지만 마치 하나의 파일에 존재하는 것처럼 스코프를 공유하게 된다.
//index.html
<body>
<script src="print.js"></script>
<script src="index.js"></script>
</body>
//print.js
const title = 'Hello Welcome to the Code World!';
function print(value) {
console.log(value);
};
//index.js
print(title);
//print(title) 출력값
Hello Welcome to the Code World!
print.js에서 선언한 변수와 함수를 index.js에서 사용하고 있지만 코드를 실행해보면 에러가 발생하지 않고 실제로 두 파일에 나눠진 코드가 마치 하나의 파일에 존재하는 것처럼 잘 동작하는 모습을 확인 할 수 있다.
이 현상이 에러가 발생하지 않아 문제가 없는것처럼 느껴질 수 있지만 생각보다 큰 문제가 될 수 있다.
html에서 script 파일을 연결할 때 print.js를 먼저 연결하고 아랫줄에 index.js를 연결해줬다. 예를들어 아래쪽 파일인 index.js에서 코드를 작성할 때 위쪽 파일인 print.js에서 선언한 변수나 함수와 이름이 중복되면 이 값이 덮어써지면서 코드를 실행했을 때 의도치 않은 결과가 나타날 수 있고 SyntaxError가 발생할 수도 있다.
//print.js
const title = 'Hello Welcome to the Code World!';
function print(value) {
console.log(value);
};
//index.js
print(title);
function print(valie) {
console.log('이건 새로운 print 입니다.');
};
//print(title) 출력 값
이건 새로운 print 입니다.
------------------------------------------
//print.js
const title = 'Hello Welcome to the Code World!';
function print(value) {
console.log(value);
};
//index.js
print(title);
function print(valie) {
console.log('이건 새로운 print 입니다.');
};
const title = '너도 할 수 있어 코딩!';
//출력되는 값
//syntax Error
이런 문제를 방지하기 위해서 각각의 모듈 파일이 다른 파일과는 스코프를 함부로 공유할 수 없도록 모듈 스코프를 가지고 있어야 한다.
HTML파일에서 자바스크립트 파일을 불러올 때 모듈 스코프를 갖게 하려면 script태그에 type속성을 module이라는 값으로 지정해 주어야 한다.
//index.html
<body>
<script type="module" src="index.js"></script>
<script type="module" src="print.js"></script>
</body>
이렇게 하고 실행을 눌러보면 에러가 발생하는데, 브라우저에서 내 컴퓨터 안에 있는 HTML 파일을 불러오게 되면 자바스크립트의 모듈 문법은 사용할 수가 없다.
자바스크립트의 모듈 문법을 만들 때 문법 자체의 보안 요구사항으로 일부러 에러가 발생되도록 설계했기 때문이다.
이 문제를 해결하려면 브라우저에서 직접 파일을 불러오는 방식이 아니라 서버를 통해서 이 HTML 파일을 실행해야 한다. 서버를 통해 HTML을 실행하는 방법은 굉장히 다양하지만 VS Code의 확장기능을 활용해서 간단하게 해결할 수도 있다.
설치를 한 후 go live 버튼을 눌러보면 서버를 통해 실행된 TML 파일이 자동으로 열리게 된다. 서버를 통해 실행된 HTML 파일에서 콘솔을 열어보면 index.js에서 title이 선언되지 않았다라는 오류가 발생하는걸 확인 할 수 있다.
왜냐하면 index.html에서 script를 연결할 때 type 특성에 module이라는 값을 지정해줬기 때문에 이 파일들은 각각 모듈 스코프를 갖게 되서 print.js 파일 내에 선언한 변수나 함수는 다른 외부 파일과 스코프를 공유하지 않게 된 것이다.
기본적으로 모듈 스코프를 가지고 특별한 문법을 통해서 서로 다른 파일들끼리 변수나 함수를 공유하는 것이 모듈 문법의 핵심이다.
모듈 문법
자바스크립트의 모듈 문법은 기본적으로 export(내보내다)와 import(불러오다) 다.
모듈 스코프를 가진 파일에서 외부로 내보내고자 하는 변수나 함수를 export 키워드를 통해 내보내고, 모듈 파일에서 내보낸 변수나 함수들은 다른 파일에서 import 키워드를 통해 가져온다.
// printer.js
export const title = 'CodePrinter';
export function print(value) {
console.log(value);
};
//이제는 이 값들을 다른 파일에서도 사용할 수 있다. 하지만 무조건 제한 없이 사용할 수 있는건 X
//변수나 함수를 사용하고자 하는 파일에서 한번더 불러오는 과정을 거쳐야 사용할 수 있다.
// index.js
import { title, print } from './printer.js';
print(title);
//파일 내에서 사용할 함수나 변수를 쉼표로 구분해서 작성해준 다음 from 키워드와 모듈 파일의 경로를 작성해준다.
이름 바꿔 import 하기
import 키워드를 통해 모듈을 불러올 때 as 키워드를 활용하면 import하는 대상들의 이름을 변경할 수 있다.
import 할 변수나 함수 이름을 조금 더 간결한 이름으로 바꾸거나, 혹은 더 구체적으로 바꾸고 싶을 때 활용할 수 있다.
뿐만 아니라 이름을 바꿔서 import 하면 여러 파일에서 불러오는 대상들의 이름이 중복되는 문제를 해결할 수 있다.
// printer.js
export const title = 'CodePrinter';
export function print(value) {
console.log(value);
};
//members.js
export const title = 'CodeMembers';
export const data = [
'지민', '태연', '유나', '연준', '태형', '정국',
'해린', '민정', '사랑', '승희', '소유', '효리'
];
//index.js
import { title as printerTitle, print, printArr } from './printer.js';
//title이라는 변수나 함수이름을 printerTitle로 바꾼다.
import { title, data as members } from './members.js';
printer(title); //CodeMembers
arrPrinter(members); //(12) ['지민', '태연', ..., '소유', '효리']
한꺼번에 import 하기
모듈 파일을 import 할 때 그 파일에서 export 하는 대상을 모두 가지고 올 필요는 없다. 실제로 파일에서 사용하는 것만 import를 하면 된다. 지금은 코드가 짧아 export 대상이 몇 개 없지만 만약 모듈 파일에서 export 하는 대상이 훨~씬 많고 다른 파일에서 import를 할 때도 그 대상들을 모두 불러와야 한다면 생각보다 작성해야 될 코드도 많아지고, 자연스럽게 가독성도 나빠진다.(여기서 이름까지 바꾼다면 코드는 더 복잡해진다.)
import할 때 와일드카드 문자(*, Wildcard Character)와 as를 활용하면 모듈 파일에서 export하는 모든 대상을 하나의 객체로 불러올 수 있다.
//print.js에서 export하는 모든 대상이 이 이름(printerJS)에 전달이 된다.
import * as printerJS from './printer.js';
console.log(printerJS.title); // CodeitPrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }
이렇게 활용하면 조금 더 간결하게 사용할 수 있고, 값을 가지고 올 때 프로퍼티 형식으로 값을 불러오기 때문에 여러 모듈을 불러 올 때 이름이 중복 될 일이 없다.
하지만 이 파일에서 필요하지 않은 대상들도 모두 불러오는 단점도 있어서 코드가 항상 효율적이라고 할 순 없다. (무분별하게 *를 사용하지 말고 필요에 따라 적절하게 활용하는것이 좋다.)
한꺼번에 export 하기
변수나 함수 앞에 매번 export 키워드를 붙일 수도 있지만, 선언된 변수나 함수를 하나의 객체로 모아 한꺼번에 내보낼 수도 있다.
이때 as 키워드를 활용하면 이름을 변경해서 export할 수도 있다.
const title = 'CodeitPrinter';
function print(value) {
console.log(value);
}
function printArr(arr) {
arr.forEach((el, i) => {
console.log(`${i + 1}. ${el}`);
});
}
// export 키워드 다음에 내보낼 대상을 중괄호로 묶어주면 된다.
export { title as printerTitle, print, printArr };
그러면 선언마다 export를 붙여주지 않아도 되고, 어떤것들이 export 되는지 한눈에 알아볼 수 있다는 장점이 있다.
import를 할 때처럼 as 키워드를 사용할수도 있는데, 모듈 파일에서 미리 이름을 바꿔서 export를 할 수도 있다.
default export
export 키워드를 사용할 때와는 다르게 default 키워드가 붙으면 그 뒤에는 반드시 하나의 대상만 내보낼 수가 있다. 변수나 함수 이름으로 export하는 것 뿐만 아니라 바로 값 하나를 전달할 수도 있다. 한가지 주의해야 할 점은 default 키워드는 모듈 파일내엣 딱 한번만 사용할 수 있다는 점이다.
const title = 'CodePrinter';
function print(value) {
console.log(value);
}
export default print; //변수나 함수 뿐만 아니라
//export default 'CodePrinter'; 이렇게 값을 바로 입력할 수도 있다.
default export는 import할 때도 기본적으로 default 키워드로 구분이 된다. (키워드 자체로는 선언할 수 없고 반드시 as 키워드를 통해서 이름을 붙여주어야한다.)
import { default as printerJS } from './printer.js';
console.log(printerJS.title); // CodePrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }
export default print; //하나의 대상만 export : Default Export
export { title as printerTitle, print, printArr }; //변수나 함수의 이름으로 여러 대상을 export 하는 방식 : Named Export
//조금 특별하게 import 사용하기
import {
default as code,
title as membersTitle,
data as membersData
} from './members.js'
//에서 default as 부분을 생략하고 default 값에 붙여준 이름을 중괄호 밖으로 빼서 사용할 수 있다.
import code, {
title as membersTitle,
data as membersData
} from './members.js'
//import를 할 때 중괄호 여부로 Default Export와 Named Export를 구분할 수 있다.
다음과 같이 축약형 문법으로 import 할 수도 있다.
import * as printerJS from './printer.js'; //를
import printerJS from './printer.js'; //로 축약할 수 있다.
console.log(printerJS.title); // CodePrinter
console.log(printerJS.print); // ƒ print(value) { console.log(value); }
사용할 때 객체 형태로 사용할 수가 있게 되는데 import 부분의 코드를 간결하게 쓸 수 있고 객체 형태로 값을 활용하기 때문에 다른 모듈과 이름 중복을 피할 수 있다는 장점이 있긴 하지만 객체 형식으로 활용하면 매번 점 표기법으로 프로퍼티에 접근해야 하고, 모듈 파일을 불러온 입장에서 각 대상의 이름을 변경할 수 없다는 단점도 있다.
'코린이 개념잡기 > JavaScript' 카테고리의 다른 글
얕고(Shallow), 깊은(Deep) 복사(Copy) (1) | 2024.12.20 |
---|---|
==와 ===의 차이 (0) | 2024.12.20 |
배열 메소드 Ⅵ (map, set) (0) | 2024.12.19 |
배열 메소드 Ⅴ (sort, reverse) (0) | 2024.12.19 |
배열 메소드 Ⅳ (reduce) (0) | 2024.12.19 |