NodeJs require(),exports,module.exports (Part.1)

신세원·2020년 10월 11일
0

NodeJs란 무엇인가?

NodeJs는 구글 크롬의 자바스크립트 엔진(V8 Engine)으로 만든 이벤트 기반 JS 런타임이다.
(런타임:컴퓨터가 실행되는 동안 프로세스나 프래그램을 위한 소프트웨어 서비스를 제공하는 가상 머신의 상태)

2009년에 Ryan Dahl(라이언 달)에 의해 개발되었다.

많은 입문자들이 NodeJs는 웹서버라고 오해한다. Node 자체로는 아무것도 하지 않는다.

아파치 웹서버처럼 HTML 파일 경로를 지정해주고 서버를 열고 그런 설정이 없다.

단, HTTP 서버를 직접 작성해야 한다. (일부 라이브러리의 도움을 받으면서)

NodeJs 는 그저 코드를 실행할 수 있는 하나의 방법에 불과한 그저 JavasScript 런타임이다.

NodeJs에서 많이 쓰이는 코드 require(),exports,module.exports를 공식 문서를 통해 이해해보도록 하자.

require()함수는 모듈을 가지고 온다.

설명전에 '모듈'에 대해 간단하게 이야기 하자면, 모듈은 두 가지의 의미를 가지고 있다.

  1. NodeJs가 갖고있는 수많은 기능들을 비슷한 것들끼리 그룹핑 한 것을 모듈이라 한다.

  2. 자바스크립트를 사람들이 쓸수 있는 코드로 만들면 그것이 모듈이다.
    (이 모듈을 exports하면 그것이 모듈화이다.)

NodeJs에서는 모듈을 불러오기 위해 require() 함수를 쓴다.

간단하게 예시를 들어보도록 하자.

두 가지 파일이 있다. 'foo.js' 와 'bar.js'이다.

첫번째로 foo.js 파일이고, 변수 a에 데이터 10이 담겨 있다.


//foo.js 

const a = 10

두번째로 bar.js 파일이고, 변수 a를 콘솔에 찍는 파일이다.


//bar.js 

console.log(a) //ReferenceError: a is not defined

이 상황에서 bar.js를 실행시키면, 에러가 뜬다. bar.js 스코프에 변수 a가 undefined이기 때문이다.

그렇다면 bar.js에서 a를 출력하려면 어떻게 해야 할까?

이때 bar.js에 foo.js 모듈을 불러와야한다. 그리고 이때 require()함수가 쓰인다.

그럼 require()함수로 foo.js를 bar.js로 가지고 오도록 하자.

//bar.js

const foo=require('./foo.js') //foo.js, bar.js는 같은 디렉토리
console.log(foo.a)

하지만 여기서도 undefined 문제가 생긴다.

//node bar.js

console.log(foo.a) //ReferenceError: a is not defined

무엇이 문제 일까? 문제를 찾아보니 foo.js에 exports가 없어서 그렇다.

여기서 exports의 필요성을 알 수 있다.

그럼 제대로 수정해서 고쳐보자.

//foo.js

const a = 10
exports.a = a;

//node bar.js
10

foo.js를 수정하고 실행해보니 원하는 값이 나오게 되었다.

여기까지 봤을때도 require()exports가 무슨 역할을 하는지 감이 안잡힐수 있다.

더 자세히 require()exports 더 나아가 exportsmodule.export는 무슨 관계인지 알아보자.

require()함수는 module.exports를 리턴한다.

require()함수의 소스코드는 복잡하다. 하지만 요약하면 다음과 같은 모양새로 구성되어 있고 하나하나

해석 해보도록 하자. (필자 요약이 아닌 문서에서 제시한 요약이다.)

var require = function(src){                 //line 1
    var fileAsStr = readFile(src)            //line 2
    var module.exports = {}                  //line 3
    eval(fileAsStr)                          //line 4
    return module.exports                    //line 5
}
  • line 1에서 'src'의 인자를 받아온다. 즉,
const foo = require('foo')

와 같은 경우 'foo'를 위에서 src 인자로 받아오는 식이다.

  • line 2에서는 소스 파일을 읽어서 fileAsStr에 저장한다.

  • line 3에서는 module.exports라는 빈 객체를 만든다.

  • line 4에서는 fileAsStr을 eval 한다.(eval()은 문자로 표현된 JavaScript 코드를 실행하는 함수이다.)

여기서 line 4를 자세히 알아보기위해 예를 들어보자.


//foo.js
const a = 10
exports.a = a;

const foo = require('./foo.js')

위 코드를 보면 require()의 src 인자로 './foo.js'를 넣는다는 것이고, line 4는 해석하자면

 eval(fileAsStr)                          //line 4

다음의 코드로 변경되는 것과 마찬가지이다.


var require = function(src){                 //line 1
    var fileAsStr = readFile(src)            //line 2
    var module.exports = {}                  //line 3
    const a = 10
    exports.a = a;  
    return module.exports                    //line 5
}

결론적으로 exports의 a의 프로퍼티의 key 값에 10이 들어간 셈이다.

  • line 5 마지막에선 require()exports의 값을 리턴(반환)한다.

좀 더 쉽게 이해하자면 다음과 같이 이해하면 될 것이다.

foo.js 와 bar.js가 같은 디렉토리 안에 있는 서로 다른 파일이고, bar.js가 다음과 같이 생겼지만

//bar.js
const foo = require('./foo.js') //foo.js, bar.js는 같은 디렉토리.
console.log(foo.a)

런타임 환경에서는 이런 모습이다.

//bar.js
const foo = { a : 10 }
console.log(foo.a)

foo에 들어간 exports에 들어간 key,value들이 require()함수의 아웃풋으로 나오게 되는 것이다.

exports와 module.exports의 차이는?

위에 코드에서 살짝 아리송한 부분이 있을것이다.

line 5를 참고해보자.


    return module.exports                    //line 5

분명 코드에서는 module.exports를 리턴이라고 적혀 있는데, 위에선 "require()exports의 값을 리턴(반환)한다."라고 적혀있다.

그렇다면 이 둘의 차이는 무엇일까?

exports는 단순히 module.exports를 참조한다. 짧은 alias일 뿐이다.

공식문서가 그게 다란다.

module.exports와 exports는 같은 객체를 바라보고 있고, exports는 module.exports의 shortcut이다.

이것도 이해하기 쉽게 코드를 보면서 이해하도록 하자.

아래 코드는 위에서 언급한 require()함수 코드이다.

var require = function(src){                 //line 1
    var fileAsStr = readFile(src)            //line 2
    var module.exports = {}                  //line 3
    eval(fileAsStr)                          //line 4
    return module.exports                    //line 5
}

line 4에서 코드가 복사 붙여넣기 되는것을 설명했고, 다시 한번 정리하자면


var require = function(src){                 //line 1
    var fileAsStr = readFile(src)            //line 2
    var module.exports = {}                  //line 3
    const a = 10			     //line 4.1
    exports.a = a;  			     //line 4.2
    return module.exports                    //line 5
}

이렇게 쓰인다고 이야기했고, exportsmodule.exports는 같은 대상을 지칭하고 있는 서로 다른 두 명칭이라는 점도 이야기했다.

따라서 다시 해석 하자면,

  • line 3에서 module.exports라는 이름으로 빈 객체를 만들었고
  • line 4.2에서 exports에 <key:value>로서 <a:10>을 넣은것이며 이는 곧,
  • module.exports에 <key:value> 값이 <a:10>을 넣은 것과 같다.

(exports에는 개발자의 귀차니즘 정신이 제대로 박혀있다고 생각하면 마음이 편할거 같다.)

profile
생각하는대로 살지 않으면, 사는대로 생각하게 된다.

0개의 댓글