[Javascript]

[Javascript] 모듈 import / export 하는법 및 쉬운 개념 정리 (commonJS의 require, ES6부터의 import 차이 한 번에 이해하기)

quokkalover 2022. 4. 16. 22:29

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

 

GitHub - getveryrichet/nodejs-study: nodejs 개발 관련해 참고 필요한 코드 모음

nodejs 개발 관련해 참고 필요한 코드 모음. Contribute to getveryrichet/nodejs-study development by creating an account on GitHub.

github.com

 

 

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.jsmessage_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.jsmessage_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

https://stackoverflow.com/questions/29596714/new-es6-syntax-for-importing-commonjs-amd-modules-i-e-import-foo-require

https://2dubbing.tistory.com/83

https://www.daleseo.com/js-module-import/