안녕하세요. 이번 시간에는 모듈 시스템에 대해 알아보겠습니다. ES2015에서 바뀐 것들 중에서 가장 획기적이라고 생각하는 변화입니다.
자바스크립트는 예전부터 리소스 관리가 어려워서 말이 많았습니다. 리소스는 웹 페이지를 구성하는 자원들입니다. 현재 웹에서는 해당 페이지에 필요한 모든 파일을 미리 불러와야 하고, 그 파일들이 사용하는 변수가 겹치지 않나 잘 살펴봐야 합니다.
변수가 겹치는 문제가 골칫거리였기 때문에 jQuery와 underscore는 jQuery.noConflict
, _.noConflict
라는 전역 변수를 변경하는 메소드를 제공하기도 합니다.
또한 스크립트를 로딩하는 순서도 중요합니다. 필요한 파일을 먼저 로딩하지 않으면 에러가 나죠. 많은 분들이 자바스크립트와 제이쿼리를 처음 배웠을 때 $ is not defined라는 에러를 한 번 쯤은 겪었을 겁니다. 바로 제이쿼리 로딩보다 제이쿼리 코드를 더 먼저 넣었을 경우죠.
자바스크립트는 패키지와 파일끼리 서로 의존하고 있는 경우가 많습니다. 파일 A가 jQuery와 font-awesome을 사용하고, 파일 B에서는 facebookSDK와 kakaoAPI를 사용한다면, 파일 A는 jQuery와 font-awesome 패키지에 의존하고 있는 거고, 파일 B는 facebook과 kakao에 의존하고 있는 겁니다.
대부분의 프로그래밍 언어에서는 원칙적으로는 파일 A에서는 kakao에 접근할 수 없어야하고, 파일 B에서는 jQuery에 접근할 수 없어야 합니다. 하지만 웹에서는 처음 시작할 때 모든 스크립트를 로딩하기 때문에 다른 파일도 모든 패키지에 접근할 수 있었습니다.
A.js
$ = null;
B.js
$('#button').on('click', function() {
// 코드들
});
간단한 예시로 A.js와 B.js를 순차적으로 로딩했을 시 B.js에서는 에러가 발생합니다. A.js에서 제이쿼리 전역변수인 $를 고의적으로 바꾸었기 때문이죠. 이렇게 누군가가 악의적으로 웹에 스크립트를 삽입하여 장난을 칠 수도 있습니다.
위와 같은 문제를 해결하기 위해서 사람들은 commonJS나 requireJS같은 자체적인 시스템을 만들었습니다. 의존성 관리를 하기 위함인데요. 파일 첫 부분에 이 파일은 어떤 패키지를 필요로 한다고 선언하는 겁니다. 그러고나서 선언한 패키지만 그 파일에서 사용합니다. 이것이 ES2015에서는 모듈 시스템으로 도입되었습니다.
ES2015에서는 파일 최상단에 명시적으로 필요한 파일들을 선언해야 합니다. Node.js에서 require을 하듯요. Node.js는 아직 ES2015 모듈 시스템을 지원하지 않습니다. Node.js의 모듈 시스템을 알아보려면 이 강좌를 참고하세요.
import React from 'react';
import { render } from 'react-dom';
ReactJS를 사용하고 싶을 때 파일 최상단에 import라고 적어줍니다. 이제부터 이 파일은 React와 render라는 변수를 사용할 수 있습니다. React 변수는 react 패키지로부터 불러왔고, render 변수는 'react-dom'이라는 패키지로부터 불러왔습니다.
첫 번째는 그냥 React인데, 두 번째는 { render }처럼 괄호 안에 감싸져 있네요. 간단한 차이가 있습니다. export 방식의 차이입니다. 모듈을 불러올 때 import라고 써주는 것처럼, 모듈을 다른 파일로 보내려면 export라고 명시적으로 써줘야 합니다. Example.js 파일을 보죠.
Example.js
const a = 1;
const b = 2;
export { a };
export const c = 3;
export default b;
Example2.js
import d, { a, c as e } from './Example';
console.log(a, d, e); // 1, 2, 3
Example.js에서 세 가지 방식으로 export를 했습니다. 변수 a는 객체에 담아서 export하고, c는 선언 및 초기화와 동시에 바로 export 했습니다. 변수 b는 독특하게 앞에 default라는 키워드를 붙인 채 export했는데요. Example2.js에서 Example 파일을 불러올 때(js는 생략해도 됩니다) 뜬금없이 처음보는 d는 괄호 없이 불러오고, 나머지 a, c는 괄호 안에서 불러옵니다. 그런데 c는 뒤에 as e라고 되어있네요.
default가 바로 기본이라는 뜻입니다. default로 export한 b는 괄호를 사용하지 않아도 import할 수 있습니다. 그리고 변수명도 마음대로 지을 수 있습니다. 변수 d가 바로 변수 b를 import한 겁니다. 나머지 a와 c는 export할 때 이름 그대로 import하고, 괄호 안에서 불어와야 합니다.
하지만 괄호 안의 변수도 이름을 바꾸고 싶다면, as 라는 키워드를 사용해 바꿀 수 있습니다. c as e라고 한 게 보이죠? 변수 c의 값을 변수 e로 넘겨주는 겁니다. 이렇게 하면 변수 e를 c 대신 사용할 수 있습니다. 이것은 두 개 이상의 패키지에서 같은 변수 이름을 사용해 변수가 겹칠 때, 이름을 다르게 바꿀 수 있어 유용합니다.
참고로 어떤 값이든 다 export, import 가능합니다. 위에서는 간단히 숫자로만 예를 들었지만, 문자열, 불린, 객체, 배열, 함수 등 전달할 수 있는 값이면 다 가능합니다. 위에 import { render } from 'react-dom';
에서 render은 함수입니다.
Example3.js
import * as namespace from './Example';
console.log(namespace); // { a: 1, c: 3, default: 2 }
* 이란 것도 있습니다. export한 모든 것을 다 모아서 import 해주는 것입니다. default도 객체의 멤버로 들어갑니다.
참고로 ES2015 module system에서의 모듈은 기본적으로 strict 모드(use strict)로 동작합니다.
모듈은 아직 대다수의 브라우저에서 지원하지 않기 때문에 BabelJS 같은 컴파일러를 사용해서 ES5 스타일로 바꿔줘야 합니다. 언젠가는 대부분의 브라우저가 모듈을 지원하여 효율적으로 의존성 관리를 할 수 있는 날이 오겠죠?
내친김에 다음 강좌로는 BabelJS에 대해 알아보겠습니다.