모듈(export & import) 한 번에 끝내기!!


안녕하세요 이녀석입니다.😎

 

자바스크립트의 공부방법으로 공식문서인 MDN이나 W3Schools, 도서, 강의가 있을 겁니다.

저도 마찬가지였고 지금도 현재 진행형이죠.

 

공부하면서 느낀건데 MDN을 보면 번역본이다 보니 이해하기 조금 난해한 부분이 분명 있었고,

저뿐만 아닌 다른 분들도 그런 부분이 있으셨을 거예요.

 

그래서 제가 이해한 것들을 쉽게 기록을 해보려 합니다.

쉽게 하려다 보니 공식 문서나 여러 강의와 다른 부분이 있을 테지만 제 글을 읽고

분명 도움이 되리라 하는 마음에 포스팅을 작성해 나가도록 하겠습니다.

혹여 부족한 부분과 다른 좋은 내용을 언제든 알려주시면 감사히 배울 테니 언제든 알려주세요.

 

감사합니다.😎


 

개발하는 애플리케이션의 크기가 커지면 언젠간 js파일을 여러 개로 분리해야 하는 시점이 옵니다. 이때 분리된 파일 각각을 '모듈(module)'이라고 부르는데, 다만 일반적인 파일과는 다른 여러가지 차이점을 갖고 있습니다.

 

  • <script>에 type="module"을 추가해 export 혹은 import 구문을 사용할 수 있다.
  • 별다른 처리를 해주지 않아도 엄격 모드(strict mode)로 동작한다.
  • 모듈의 가장 바깥쪽에서 선언된 이름은 전역 스코프가 아니라 모듈 스코프에서 선언된다.

 

1. <script>에 type="module"을 추가해 export 혹은 import 구문을 사용할 수 있다.

 

  • 기본 내보내기(export default)
    • export default 변수(식별자) || 표현식 || 값;
    • 파일당 한 번만 사용할 수 있다
  • 기본 내보내기를 가져올 때
    • import 임의의 변수 from '경로/xx.js';

 

<!-- index.html -->
<script type="module" src="main.js"></script>

 

// 기본 내보내기
// common.js(index.html에 연결되지 않음)
const obj = {
	name: 'Lee',
	age: 32
};
export default obj;

// 기본 내보내기 가져올 때
// main.js(index.html에 연결되어 있음)
import hello from './common.js';

console.log(hello); // {name: 'Lee', age: 32}

 

기본 내보내기(export default)는 표현식(값) 자체를 내보내는 것이기 때문에 import하는 곳에서는 내보내기 할 때의 변수를 그대로 사용하지 않아도 됩니다.(내보내기와 변수명이 달라도 됨)

 


 

  • 이름 내보내기(export)
    • export { 변수1, 변수2, ..., 변수n };
    • 선언과 동시에 내보내기 ex) export const a = 20;
    • 다른 이름으로 내보내기 ex) export { 변수 as 새로운변수 };
    • 여러 번 사용할 수 있다
  • 이름 내보내기를 가져올 때
    • import { 변수1, 변수2, ..., 변수n } from '경로/xx.js'; 
    • 다른 이름으로 가져오기 ex) import { export할 때의 변수 as 새로운 변수 } from '경로/xx.js';

 

<!-- index.html -->
<script type="module" src="main.js"></script>

 

// 이름 내보내기
// common.js(index.html에 연결되지 않음)
const obj = {
	name: 'Lee',
	age: 32
};

const arr = [1,2,3,4,5];

export function sayHello() { // 선언과 동시에 내보내기
	return 'HELLO~';
}
export { obj, arr };

// 이름 내보내기 가져올 때
// main.js(index.html에 연결되어 있음)
import { sayHello, obj, arr } from './common.js';

console.log(sayHello()); // 'HELLO~'
console.log(obj);        // {name: 'Lee', age: 32}
console.log(arr);        // [1,2,3,4,5]

 

이름 내보내기는 여러 번 사용할 수 있고, 값이 할당된 변수(식별자) 자체를 내보내는 것이기 때문에 import하는 곳에서도 동일한 변수를 사용해야 합니다.

 

// 이름 내보내기
// common.js(index.html에 연결되지 않음)
const obj = {
	name: 'Lee',
	age: 32
};

export { obj };

// 이름 내보내기 가져올 때 - 다른 이름으로 가져오기
// main.js(index.html에 연결되어 있음)
import { obj as newObj } from './common.js';

console.log(newObj); // {name: 'Lee', age: 32}
console.log(obj);    // Uncaught ReferenceError: obj is not defined

 


 

  • 기본 내보내기(export default), 이름 내보내기(export) 동시 사용

 

<!-- index.html -->
<script type="module" src="main.js"></script>

 

// 내보내기
// common.js(index.html에 연결되지 않음)
export default 32 // 기본 내보내기
export const myName = 'Lee'; // 이름 내보내기

// 가져오기
// main.js(index.html에 연결되어 있음)
import age, { myName } from './common.js'; // 각각 가져오기
import * as all from './common.js'; // 전부 가져오기

console.log(age);    // 32
console.log(myName); // 'Lee'
console.log(all);    // {default: 32, myName: 'Lee'}

 

 


 

  • 동적으로 가져오기( import() )
    • import('경로/xx.js').then(all => { ... }); (Promise를 이용)
    • import()는 <script>에 type="module"이 없어도 된다.
    • import()는 함수 호출과 문법이 유사해 보이긴 하지만 함수 호출은 아니다. super()처럼 괄호를 쓰는 특별한 문법 중 하나이다. 따라서 import를 변수에 복사한다거나 call/apply를 사용하는 것이 불가능하다.

 

// 내보내기
// common.js(index.html에 연결되지 않음)
export default 32;
export let myName = 'Lee';

 

<!-- index.html -->
<script>
    // type="module"이 없어도 import()는 동작한다
    
    // 1초 후에 동적으로 export된 모든 것을 가져오기
    setTimeout(()=>{
        import('./module.js')
            .then((all)=>{
                console.log(all); // {default: 32, myName: "Lee"}
            })
    }, 1000);
    
    // async-await으로 동적으로 export된 모든 것을 가져오기
    (async function(){
        const all = await import('./module.js');
        
        console.log(all); // {default: 32, myName: "Lee"}
    })();
    
</script>

 


 

  • 다시 내보내기(re-export)
    • export { 변수 } from '경로/xx.js';
    • 경로는 생략 가능

 

<!-- index.html -->
<script type="module" src="main.js"></script>

 

// 기본 내보내기
// a.js(index.html에 연결되지 않음)
export default 'a.js입니다';

// 기본 내보내기, 이름 내보내기
// b.js(index.html에 연결되지 않음)
export default 'b.js입니다';
export const myName = 'Lee';

// 기본 내보내기, 이름 내보내기
// c.js(index.html에 연결되지 않음)
export default 'c.js입니다';
export const myAge = 32;

// 기본 내보내기, 이름 내보내기
// d.js(index.html에 연결되지 않음)
export default 'd.js입니다';
export const fruits = 'apple';

// 기본 내보내기, 이름 내보내기
// e.js(index.html에 연결되지 않음)
export default 'e.js입니다';
export const color = 'orange';

// 다시 내보내기(re-export)
// common.js(index.html에 연결되지 않음)
export { default } from './a.js';
export { default, myName } from './b.js';
export { default as txtC, myAge } from './c.js';
export * from './d.js';
export * as allE from './e.js';

// 가져오기
// main.js(index.html에 연결되어 있음)
import txtA from './a.js'; // 기본 내보내기를 가져오기
import txtB, { myName } from './b.js'; // 각각 가져오기
import { txtC, myAge } from './c.js'; // 각각 가져오기
import * as allD from './d.js'; // 전부 가져오기
import { allE } from './e.js'; // 전부 가져오기

console.log(txtA); // 32
console.log(txtB, myName); // 'b.js입니다' 'Lee'
console.log(txtC, myAge); // 'c.js입니다' 32
console.log(allD); // {default: 'd.js입니다', fruits: 'apple'}
console.log(allE); // {default: 'e.js입니다', color: 'orange'}

 

2. 별다른 처리를 해주지 않아도 엄격 모드(strict mode)로 동작한다.

<!-- index.html -->
<script>
	console.log(this); // window
</script>

<script type="module">
	console.log(this); // undefined
</script>

 

3. 모듈의 가장 바깥쪽에서 선언된 이름은 전역 스코프가 아니라 모듈 스코프에서 선언된다.

<!-- index.html -->
<script type="module">
    const sayHello = function() {
        console.log('Hello');
    }
</script>

<script>
	sayHello(); // Uncaught ReferenceError: sayHello is not defined
</script>