[Javascript] 모듈 import / export 하는법 및 쉬운 개념 정리 (commonJS의 require, ES6부터의 import 차이 한 번에 이해하기)
require? ipmort?
require
는 commonJS의 키워드이자 nodejs에서 외부 모듈을 가져와 사용할 때 쓰는 함수다. 쉽게 말해서 다른 파일의 코드를 불러오는데 사용된다. 근데 문제는 ES6부터는 require
가 아닌 import
를 통해 외부 모듈을 가져오게 되는데, 용도는 거의 비슷해보이지만 사용 방법이 달라서 처음 nodejs를 접했을 때 매우 혼란스러웠다.
필자는 ES6부터 자바스크립트를 접했기 때문에, import
키워드를 사용해 외부 모듈을 불러오는게 익숙한데, 웹 상에 존재하는 여러 예제들을 보면 아직 require
키워드를 사용하는 경우가 많았기 때문이다.
var msg = require("messageObject.js")
무튼 정리해보면 require
이나 import
둘 다 외부 모듈/라이브러리를 불러오는데 사용되는 키워드이고, 아직까지 nodejs에서는 commonJS
를 기본으로 사용하고 있기 때문에 어쩔 수 없이 require
키워드를 사용해야 하는 경우가 많아 require
키워드의 용법에 대한 공부가 필요하다고 생각했다.
따라서 본 글에서는
1) commonJS의 require
키워드와 export방법들에 대해 알아보고,
2) ES6의 import
키워드와 named export
, default export
에 대해 알아본 뒤,
3) commonJS방식의 모듈들을 import
키워드로 import해보고,
4) Node.js 프로젝트에서 import / export 사용하는 세팅 방법에 대해 알아보겠다.
기본적인 예제 코드는 아레 repository에 업로드 해둘테니 참고해서 한번 직접 실행해보거나, 아니면 직접 자기만의 방식으로 코드를 짜서 이해해보는 것을 추천한다.
https://github.com/getveryrichet/nodejs-study/tree/main/modules_tutorial
commonJS : require 알아보기
모듈 불러오기
commonJS의 방식에서 require
를 사용해 모듈을 불러오는 기본적인 사용 방법은 아래와 같다.
require('{module-name}/{.js file-path}')
//예: require('파일 경로.js') or require('Sequelize')
모듈 내보내기
commonJS 방식으로 모듈을 내보내기 위해서는 exports
변수와 module.exports
변수를 사용해야 한다.
exports
변수는 복수 객체를 내보내고 싶은 경우, exports
변수의 속성으로 객체 할당해 사용하고,
module.exports
변수는 딱 하나의 객체를 내보내고 싶은 경우, module.exports
변수 자체에 할당한다.
이렇게 글로쓰면 잘 이해가 안될 수 있기 때문에, 바로 예시로 살펴보자
module.exports
사용
exports는 특별한 객체로, 모든 것을 assign해서 모듈로 사용할 수 있다. 즉 exports라는 객체에 원하는 것들을 assign 후 외부에서 사용할 수 잇게 되는 것이다.
export literal
exports는 객체이기 때문에 literal도 assign할 수 있다.
// commonJS/module_exports/message_literal.js
module.exports = 'Hello world';
export as object
exports는 객체이기 때문에 method나 property들을 붙일 수 있다.
// commonJS/module_exports/message_object.js
module.exports.message = 'Hello';
module.exports.printMessage = async function (msg) {
console.log(msg)
}
// or
// const obj = {};
// obj.message = 'Hello'
// obj.printMessage = async function (msg) {
// console.log(msg)
// }
// module.exports = obj
위와 같이 파일 두 개를 만들고 아래 예제를 실행해보면
// commonJS/module_exports/index.js
var o = require("./message_object.js")
console.log(o.message)
o.printMessage("haha")
var msg = require("./message_literal.js")
console.log(msg)
// 출력
// Hello
// haha
// Hello world
exports
사용
exports를 사용하면 여러 개의 객체를 내보낼 수 있다. 예시는 아래와 같다
export literal
exports를 사용할 때는 literal을 assign해서 내보낼 수 없다. 일단 확인하기 위해 아래처럼 써보자
// commonJS/exports/message_literal.js
exports = 'haha' // this won't work as intended
export as object
// commonJS/exports/message_object.js
exports.msg = "haha"
exports.func = function(haha){
console.log(haha + "inserted")
}
위와 같이 두 개의 파일을 만들고 아래 예제를 실행해보면
// commonJS/exports/index.js
var msg = require("./message_literal.js")
console.log(msg)
var msg2 = require("./message_object.js")
console.log("obj", msg2)
msg2.func("imported")
//출력
// {} <- 빈 obj출력되는 것 확인할 수 있음
// obj { msg: 'haha', func: [Function (anonymous)] }
// importedinserted
modules.exports, exports차이
위와 같이 modules.exports
를 사용하면 exports객체에 property를 붙여서 사용할 수 있는 exports
와는 다르게 바로 literal을 적용해서 사용할 수 있다.
예를 들어 아래와 같이 함수 정의한 후에
// commonJS/module_exports/direct_literal.js
module.exports = (haha) => {
return haha + " returned"
}
아래와 같이 바로 require로 불러오자마자 바로 함수로 사용할 수도 있다.
// commonJS/module_exports/index.js
console.log(require("./direct_literal.js")("df"))
// 출력
// df returned
자 이제 require
를 어떻게 활용할 수 있는지, 기본적인 사용법을 알아봣으니 이제 nodejs에서 import와 export를 사용하는 법에 대해 알아보자
ES6의 import export
es6 이전에는 javascript에 모듈에 대한 표준이 없었다. 하지만 es6부터는 모듈에 대한 표준이 도입됐고 import
, from
, export
, default
와 같이 모듈 관리 전용 키워드를 사용하면서 가독성이 매우 좋아졌다. 이전의 require
를 사용하는 commonJs 모듈 방식은 모듈 전체를 불러왔지만 ES6부터는 모듈내 정의된 것들중에 필요한 객체들만 불러올 수 있기 때문에 성능과 메모리 측면에서도 매우 좋아졌다.
ES6에서는 모듈을 export하는 방식을 크게 아래 두가지로 생각해볼 수 있다.
1) default export : default 키워드를 사용해 단일 객체 내보내기
2) named export : export 키워드만 사용해 명시적으로 선언하여 여러 객체 내보내기
default export : default
키워드 사용해서 export하기
모듈 당 하나의 객체를 내보내고 싶을 때 사용하는 키워드로, 하나의 객체만 내보낼 수 있기 때문에 한 모듈에 하나의 default export만 존재 할 수 있다.
바로 예제를 통해 알아보자
아래처럼 바로 literal을 assign할 수도 있고
// message_literal.js
export default "haha"
아래처럼 바로 객체를 assign할 수도 있고
// message_object.js
export default {
"name" : "message object"
}
객체를 만들어서 export default로 해당 객체를 expose할 수도 있다.
// message_object_ver_2.js
const obj = {};
obj.name = "hah";
obj.func = function(msg) {
console.log("printing in module", msg)
};
export default obj;
default export : import 하기
위와 같이 default키워드를 사용한 모듈들을 import할 때는 아무 이름이나 원하는 이름을 주고 해당 객체에 접근할 수 있다.
아래와 같이 실행해보면 정상적으로 불러올 수 있다.
import rrr from "./message_literal.js"
console.log(rrr)
import haha from "./message_object.js"
console.log(haha)
import ddd from "./message_object_ver_2.js"
console.log(ddd)
ddd.func("haha")
// 출력
// haha
// { name: 'message object' }
// { name: 'hah', func: [Function (anonymous)] }
// printing in module haha
named export : export
키워드로 export하기
export키워드를 사용해서 모듈을 내보내게되면, 여러 객체를 내보내고 불러올 수 있다. 위의 default 키워드를 사용할 때와는 다르게 변수나 함수의 이름이 import할 때 사용되는 이름이기 때문에 named export
라고 부른다.
바로 예를 들어 보겠다.
먼저 literal만 export하는 경우를 알아보자
// ES6/named-export/message_literal.js
export const msg = "haha";
위와 같이 정의해두고 아래와 같이 두 가지 방식으로 불러올 수 있다.
1) import { [export된 변수명] } from “{js-file-path}”
- export된 변수명으로 import 가능
2) import * as {원하는 이름} from “js-file-path}”
- {원하는 이름}이 하나의 객체가 되고, 이 객체의 속성으로 export된 변수들이 붙음
한번 아래처럼 불러와서 실행해보자
// ES6/named-export/index.js
// 방법 1)
import {msg} from "./message_literal.js"
console.log(msg)
// 방법 2)
import * as msg2 from "./message_literal.js"
console.log(msg2)
console.log(msg2.msg)
// 출력
// haha
// [Module: null prototype] { msg: 'haha' }
// haha
다음으로는 함수와 여러 객체들을 export해보겠다.
아래와 같이 다양한 방식으로 객체들을 export할 수 있다.
// ES6/named-export/message_object.js
export function printMessage(haha) {
console.log(haha);
}
export const message = "haha";
const me = "it's me";
export { me };
그러고 위에 export된 변수명 혹은 함수명으로 아래와 같이 import할 수 있다.
// ES6/named-export/index.js
import {printMessage, message, me} from "./message_object.js"
printMessage("haha")
console.log(message)
console.log(me)
//출력
// haha
// haha
// it's me
commonJS방식으로 구현된 모듈 ES6방식으로 import하기
ES6로 코딩을 하는데, import해야 되는 모듈은 commonJS방식으로 export돼있을때는 어떻게 해야 할까?
필자는 이 부분에서 굉장히 헷갈렸다. 하지만 위에서 commonJS방식을 설명할 때 아래 두가지 방식으로 정리했다.
1) module.exports
사용방식 : 단일 객체를 내보냄
2) exports
사용 방식 : 여러 속성으로 객체를 넣어서 내보냄
그럼 얼추 ES6 방식에서의 default export와 named export와 어느 정도 유사하다는 느낌이 온다. 사용방식은 다음과 같다.
1) commonJS의 module.exports
방식을 ES6의 default export
방식으로 받아들여, 내가 원하는 이름으로 해당 객체를 import해 사용하고,
2) commonJS의 exports
방식을 ES6의 named export
로 받아들이되, export의 속성 이름을 import할 수 있다.
바로 예를 들어 설명해보겠다.
위 예시에서 commonJS의 module.exports
방식을 사용하는 message_litreal.js
와 message_object.js
를 ES6방식으로 아래와 같이 사용할 수 있다.
// ES6/import-from-commonjs/index.js
import moduleExportLiteral from "../../commonJS/module_exports/message_literal.js"
console.log(moduleExportLiteral)
import moduleExportsObject from "../../commonJS/module_exports/message_object.js"
console.log(moduleExportsObject)
moduleExportsObject.printMessage("test")
// 출력
// Hello world
// { message: 'Hello', printMessage: [AsyncFunction (anonymous)] }
// test
또, commonJS의 exports
방식을 사용하는 message_literal.js
와 message_object.js
를 ES6의 named export
처럼 사용해보자
// ES6/import-from-commonjs/index.js
import exportLiteral from "../../commonJS/exports/message_literal.js"
console.log(exportLiteral)
import exportObject from "../../commonJS/exports/message_object.js"
console.log(exportObject)
import {msg} from "../../commonJS/exports/message_object.js"
console.log(msg)
//출력
// {}
// { msg: 'haha', func: [Function (anonymous)] }
// haha
위에서 정의한 모듈들의 구현체를 보면서 출력물과 비교하면서 이해해보도록 하자.
Node.js 프로젝트에서 import / export 사용하기
Node.js에서 ES6 방식의 import/export를 사용하는 방법은 간단하다.
프로젝트 디렉토리 내의 package.json을 열고, 다음과 같이 type을 module로 설정해준다.
// package.json
{
...(생략)
"type": "module"
...(생략)
}
참고자료
https://webisfree.com/2017-05-05/commonjs란-무엇이고-활용하는-방법
https://velog.io/@sms8377/Javascript-require-간단-동작-원리-및-module.export-와-export의-차이
https://www.daleseo.com/js-module-import/
https://www.tutorialsteacher.com/nodejs/nodejs-module-exports