치춘짱베리굿나이스

require, import, export 본문

Javascript + Typescript/이론과 문법

require, import, export

치춘 2022. 7. 25. 13:00

다른 파일에서 함수 가져오기

작업을 하다 보면 한 파일에 함수가 매우매우매우매우… 많아져서 보기 싫어질 때가 많다

파일을 분리하고, 해당 파일에서 함수를 불러오는 식으로 작업하면 한 파일당 한두 개의 함수에 집중할 수 있기 때문에 코드가 깔끔해진다

프론트엔드 컴포넌트를 만들 때도 거의 무조건 (특정 컴포넌트 안에서만 사용되는 한두줄짜리 컴포넌트가 아닌 이상) 한 파일에 컴포넌트 1개 룰을 지키면서 코딩하다 보니 이제 각 파일별로 주제가 명확하지 않으면 코드 읽기가 어려운 지경에 왔다

다른 파일에서 모듈을 불러오는 방법은 크게 두 가지가 있는데, requireimport이다

사실 다른 게시글에서 적었지만 정보가 상당히 빈약해서.. 삭제 후 더 자료검색해서 새로 작성하였다

require

const React = require('react');
const { Component } = React;

node의 모듈 시스템으로 module.exports로 내보낸 모듈을 다른 곳에서 쓰게 하는 문법

구조분해 방식으로 작성된 부분은 (위의 const {component}) 내보낸 대상이 객체거나 배열일 때 사용가능하다

commonJS 문법이라 ES6 코드를 실행시킬 수 없는 환경에서는 require를 쓸 수밖에 없다 ⇒ import로 완벽히 대체는 아직 불가능하다

 

let input = require("fs").readFileSync("/dev/stdin");

직접 내보낸 require문에 대하여 메서드 체이닝으로 바로 다음 메서드를 실행시킬 수 있다

 

module.exports = {
    foo,
    bar,
    dap
};

/* 불러올 때 */
let { foo } = require("./testFunc.js");

/* 다음과 같다 */
let { foo } = {
    foo: () => console.log("foo"), 
    bar: () => console.log("bar"),
    dap: () => console.log("dap")
};

require 함수는 해당 경로에서 module.exports로 내보낸 객체를 바로 반환한다

따라서 module.exports로 내보낸 객체를 그대로 require 함수 위치에 넣은 것과 똑같은 의미를 갖는다

나는 foo만 쓰고 싶은데 foo, bar, dap을 전부 불러온 꼴이 되어버렸다

이처럼 require은 내보내기한 모든 코드들이 불러와지기 때문에 실제로 어느 코드가 사용되는지 명확히 알 수 없다

module.exports

module.exports = testFunc;
module.exports = {
    foo,
    bar,
    dap
};

/* 사용할 때 */
const testFunc = require("./testFunc.js"); // 단일 함수 내보내기
const obj = require("./test.js"); // 여러 객체 내보내기
obj.foo();

/* 구조분해 */
const { foo, bar } = require("./test.js");

module.exports 문을 통해 객체 하나만을 반환하거나, 중괄호 ({}) 로 묶어 여러 객체를 한번에 반환할 수 있다

 

exports.foo = foo;
exports.bar = bar;

/* 사용할 때 */
const obj = require("./test.js");
obj.foo();

/* 구조분해 */
const { foo, bar } = require("./test.js");

exports만 사용해도 같은 방법으로 사용할 수 있다

exportsmodule.exports의 참조값이라 (같은 값을 바라보고 있다는 뜻) 같은 방식으로 사용할 수 있다

exportmodule.exports의 바로가기 (네임스페이스) 라고 생각하면 된다

 

다만 exports = foo 와 같이 바로 할당하는 방식은 동작하지 않고, exports를 객체처럼 사용하여 프롭퍼티를 묶어주는 것만 가능하다

바로 할당하여 사용하면 exportsmodule.exports의 참조관계를 끊어버리고 exports라는 새로운 변수로 바뀌기 때문이라고 한다

import

import React, { Component } from 'react'

위의 require문을 한 줄로 깔끔하게 작성한 것, 파일 최상단에 작성한다

require문은 파일 중간에서도 호출할 수 있지만, import문은 무조건 파일 상단에 위치해야 한다

from 뒤에는 파일의 경로, import 뒤에는 불러올 모듈명을 적어주면 되고, export로 내보낸 모듈을 불러올 수 있다

 

ES6 (es2015) 전용 문법이기 때문에 ES6을 지원하지 않는 브라우저는 해당 구문이 오류가 발생해야 하지만, 바벨이 알아서 번역해서 컴파일링 해 준다 (짱)

단 ES6 모듈 문법이기 때문에 commonJS에서는 동작하지 않고, 따라서 파일 확장자가 .mjs이거나 package.json에 부가 설정을 해 주어야 한다

package.json에 설정하기

{
    "type": "module"
    }

위와 같이 type 속성을 module로 지정해주면 해당 작업 레포지토리의 모든 파일을 모듈로 인식하여 import문을 쓸 수 있다

다르게 말하자면 type: commonJS일 땐 이 구문을 사용할 수 없다

export

/* named export */
export const foo = () => { console.log("foo"); }

/* default export */
const foo = () => { console.log("foo"); }
export default foo;

module.exports 와 비슷한 역할을 하지만, import문에 대응되는 내보내기 키워드이다

export default는 Default Export라 하며, 한 파일에 기본값 하나만 내보내기 할 수 있고, (내보내기 한 값이 객체가 아니라면) 구조분해 할당으로 가져올 수 없다

export const는 Named Export라 하며, 한 파일에 여러 개가 존재할 수 있다

 

import { default as foo } from "./test.js";

export default로 내보내기하는 것은 사실상 default라는 이름으로 Named Export하는 것과 같아서, 위와 같이 가져오기가 가능하다

require vs import

require

  • 동적으로 가져오는 방식이기 때문에, 조건부로 모듈을 가져오는 등이 가능하다
  • 파일의 어디서든 필요한 모듈을 불러올 수 있다
  • 해당 파일의 모든 내보내기를 한번에 가져오기 때문에, 메모리가 더 많이 소모된다

import

  • 정적으로 가져오는 방식이기 때문에, 조건부 호출이 불가능하다
    • async / await을 사용하여 동적으로 가져오는 것이 가능하다
  • 파일 최상단에서만 모듈을 불러오는 것이 가능하다
  • 사용하려는 모듈만 선택적으로 가져오기 때문에, 성능이 더 좋고 메모리가 절약된다 (트리쉐이킹)
    • 이 이점 때문에 정적으로 동작하고, 파일 최상단에서만 불러오도록 고정되어 있다
  • .mjs 확장자 (ES6 module 타입) 또는 package.jsontype: module로 명시되어 있을 경우에만 사용가능하다

참고자료

자바스크립트 ES6 모듈 내보내기/불러오기 (import)

NODE JS - 모듈이란? , module.export 와 exports의 차이

require(), exports, module.exports 공식문서로 이해하기

module.exports와 exports 차이 이해하기

export const vs. export default in ES6

Dynamic import(동적 가져오기)

 

Comments