|
대개는 두 번째 방식으로 모듈을 만드는 걸 선호하기 때문에 함수, 클래스, 변수 등의 개체는 전용 모듈 안에 구현됩니다.
그런데 이렇게 모듈을 만들다 보면 자연스레 파일 개수가 많아질 수밖에 없습니다. 그렇더라도 모듈 이름을 잘 지어주고, 폴더에 파일을 잘 나눠 프로젝트를 구성하면 코드 탐색이 어렵지 않으므로 이는 전혀 문제가 되지 않습니다.
모듈은 export default라는 특별한 문법을 지원합니다. export default를 사용하면 '해당 모듈엔 개체가 하나만 있다’는 사실을 명확히 나타낼 수 있습니다.
내보내고자 하는 개체 앞에 export default를 붙여봅시다.
// 📁 user.js
export default class User { // export 옆에 'default'를 추가해보았습니다.
constructor(name) {
this.name = name;
}
}
파일 하나엔 대개 export default가 하나만 있습니다.
이렇게 default를 붙여서 모듈을 내보내면 중괄호 {} 없이 모듈을 가져올 수 있습니다.
// 📁 main.js
import User from './user.js'; // {User}가 아닌 User로 클래스를 가져왔습니다.
new User('John');
중괄호 없이 클래스를 가져오니 더 깔끔해 보이네요. 모듈을 막 배우기 시작한 사람은 중괄호를 빼먹는 실수를 자주 합니다. named export 한 모듈을 가져오려면 중괄호가 필요하고, default export 한 모듈을 가져오려면 중괄호가 필요하지 않다는 걸 기억해 실수를 방지합시다.
사실 named export와 default export를 같은 모듈에서 동시에 사용해도 문제는 없습니다. 그런데 실무에선 이렇게 섞어 쓰는 사례가 흔치 않습니다. 한 파일엔 named export나 default export 둘 중 하나만 사용합니다.
파일당 최대 하나의 default export가 있을 수 있으므로 내보낼 개체엔 이름이 없어도 괜찮습니다.
아래 예시의 개체엔 이름이 없지만 모두 에러 없이 잘 동작합니다.
export default class { // 클래스 이름이 없음
constructor() { ... }
}
export default function(user) { // 함수 이름이 없음
alert(`Hello, ${user}!`);
}
// 이름 없이 배열 형태의 값을 내보냄
export default ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
export default는 파일당 하나만 있으므로 이 개체를 가져오기 하려는 모듈에선 중괄호 없이도 어떤 개체를 가지고 올지 정확히 알 수 있으므로 이름이 없어도 괜찮습니다.
default를 붙이지 않았다면 개체에 이름이 없는 경우 에러가 발생합니다.
export class { // 에러! (default export가 아닌 경우엔 이름이 꼭 필요합니다.)
constructor() {}
}
* 'default' name
default 키워드는 기본 내보내기를 참조하는 용도로 종종 사용됩니다.
함수를 내보낼 때 아래와 같이 함수 선언부와 떨어진 곳에서 default 키워드를 사용하면, 해당 함수를 기본 내보내기 할 수 있습니다.
function sayHi(user) {
alert(`Hello, ${user}!`);
}
// 함수 선언부 앞에 'export default'를 붙여준 것과 동일합니다.
export {sayHi as default};
흔치 않지만 user.js라는 모듈에 ‘default’ export 하나와 다수의 named export가 있다고 해봅시다.
// 📁 user.js
export default class User {
constructor(name) {
this.name = name;
}
}
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
아래와 같은 방식을 사용하면 default export와 named export를 동시에 가져올 수 있습니다.
// 📁 main.js
import {default as User, sayHi} from './user.js';
new User('John');
* 를 사용해 모든 것을 객체 형태로 가져오는 방법도 있는데, 이 경우엔 default 프로퍼티는 정확히 default export를 가리킵니다.
// 📁 main.js
import * as user from './user.js';
let User = user.default; // default export
new User('John');
* default export의 이름에 관한 규칙
named export는 내보냈을 때 사용한 이름 그대로 가져오므로 관련 정보를 파악하기 쉽습니다.
그런데 아래와 같이 내보내기 할 때 쓴 이름과 가져오기 할 때 쓸 이름이 동일해야 한다는 제약이 있죠.
import {User} from './user.js';
// import {MyUser}은 사용할 수 없습니다. 반드시 {User}이어야 합니다.
named export와는 다르게 default export는 가져오기 할 때 개발자가 원하는 대로 이름을 지정해 줄 수 있습니다.
import User from './user.js'; // 동작
import MyUser from './user.js'; // 동작
// 어떤 이름이든 에러 없이 동작합니다.
그런데 이렇게 자유롭게 이름을 짓다 보면 같은 걸 가져오는데도 이름이 달라 혼란의 여지가 생길 수 있습니다.
이런 문제를 예방하고 코드의 일관성을 유지하기 위해 default export 한 것을 가져올 땐 아래와 같이 파일 이름과 동일한 이름을 사용하도록 팀원끼리 내부 규칙을 정할 수 있습니다.
import User from './user.js';
import LoginForm from './loginForm.js';
import func from '/path/to/func.js';
...
그런데 규칙이 있어도 이를 지키지 않는 사람이 있을 수 있기 때문에 어떤 팀은 named export만 사용할 것을 강제하는 경우도 있습니다. 모듈 하나에서 단 하나의 개체만 내보내는 경우에도 default 없이 이름을 붙여 내보내면 혼란을 방지할 수 있기 때문이죠.
이런 규칙은 아래에서 배울 모듈 다시 내보내기를 쉽게 해준다는 장점도 있습니다.
* 모듈 다시 내보내기
export ... from ... 문법을 사용하면 가져온 개체를 즉시 ‘다시 내보내기(re-export)’ 할 수 있습니다. 이름을 바꿔서 다시 내보낼 수 있는 것이죠. 예시를 살펴봅시다.
export {sayHi} from './say.js'; // sayHi를 다시 내보내기 함
export {default as User} from './user.js'; // default export를 다시 내보내기 함
다시 내보내기가 왜 필요한건지 의문이 드실 겁니다. 유스 케이스를 통해 다시 내보내기가 실무에서 언제 사용되는지 알아봅시다.
NPM을 통해 외부에 공개할 '패키지(package)'를 만들고 있다고 가정합시다. 이 패키지는 수많은 모듈로 구성되어있는데, 몇몇 모듈은 외부에 공개할 기능을, 몇몇 모듈은 이러한 모듈을 도와주는 ‘헬퍼’ 역할을 담당하고 있다고 합시다.
패키지 구조는 아래와 같습니다.
auth/
index.js
user.js
helpers.js
tests/
login.js
providers/
github.js
facebook.js
...
진입점 역할을 하는 '주요 파일’인 auth/index.js을 통해 기능을 외부에 노출시키면 이 패키지를 사용하는 개발자들은 아래와 같은 코드로 해당 기능을 사용할 겁니다.
import {login, logout} from 'auth/index.js'
이때 우리가 만든 패키지를 사용하는 외부 개발자가 패키지 안의 파일들을 뒤져 내부 구조를 건드리게 하면 안 됩니다. 그러려면 공개할 것만 auth/index.js에 넣어 내보내기 하고 나머는 숨기는 게 좋겠죠.
이때 내보낼 기능을 패키지 전반에 분산하여 구현한 후, auth/index.js에서 이 기능들을 가져오고 이를 다시 내보내면 원하는 바를 어느 정도 달성할 수 있습니다.
// 📁 auth/index.js
// login과 logout을 가지고 온 후 바로 내보냅니다.
import {login, logout} from './helpers.js';
export {login, logout};
// User를 가져온 후 바로 내보냅니다.
import User from './user.js';
export {User};
...
이제 외부 개발자들은 import {login} from "auth/index.js"로 우리가 만든 패키지를 이용할 수 있습니다.
export ... from ...는 위와 같이 개체를 가지고 온 후 바로 내보낼 때 쓸 수 있는 문법입니다. 아래 예시는 위 예시와 동일하게 동작합니다.
// 📁 auth/index.js
// login과 logout을 가지고 온 후 바로 내보냅니다.
export {login, logout} from './helpers.js';
// User 가져온 후 바로 내보냅니다.
export {default as User} from './user.js';
...
* default export 다시 내보내기
기본 내보내기를 다시 내보낼 때는 주의해야 할 점들이 있습니다.
user.js 내의 클래스 User를 다시 내보내기 한다고 가정해 봅시다.
// 📁 user.js
export default class User {
// ...
}
User를 export User from './user.js'로 다시 내보내기 할 때 문법 에러가 발생합니다. 어디가 잘못된 걸까요?
default export를 다시 내보내려면 위 예시처럼 export {default as User}를 사용해야 합니다.
export * from './user.js'를 사용해 모든 걸 한 번에 다시 내보내면 default export는 무시되고, named export만 다시 내보내집니다.
두 가지를 동시에 다시 내보내고 싶다면 두 문을 동시에 사용해야 합니다.
export * from './user.js'; // named export를 다시 내보내기
export {default} from './user.js'; // default export를 다시 내보내기
default export를 다시 내보낼 땐 이런 특이한 상황도 인지하고 있다가 처리해줘야 하므로 몇몇 개발자들은 default export를 다시 내보내는것을 선호하지 않습니다.
(원문) https://ko.javascript.info/import-export
|