본문 바로가기
JavaScript & TypeScript

require와 import의 차이점: JavaScript 모듈 시스템 비교

by 대박플머 2024. 11. 5.

requireimport는 JavaScript와 TypeScript에서 모듈을 가져오는 데 사용되는 두 가지 주요 방법입니다. 이 두 방법은 모듈 시스템과 환경에 따라 동작 방식과 특징이 달라지며, 각각의 장단점과 사용 사례가 있습니다. 이번 글에서는 requireimport의 차이를 상세히 비교하고 어떤 상황에서 어떤 방식을 사용해야 하는지 알아보겠습니다.

1. 기본적인 개념

require

  • require는 CommonJS 모듈 시스템의 일부이며, Node.js 환경에서 주로 사용됩니다.
  • 동기적으로 모듈을 로드합니다. 이는 코드가 require 문을 만나면 해당 모듈의 로딩이 완료될 때까지 다음 코드의 실행을 멈추고 기다린다는 의미입니다. 예를 들어:
    1. require 문을 만납니다.
    2. 요청된 모듈을 찾고 로드합니다.
    3. 모듈의 코드를 실행합니다.
    4. 모듈의 exports 객체를 반환합니다.
    5. 이후의 코드를 계속 실행합니다.
      이러한 동기적 로딩 방식은 코드의 실행 순서를 예측하기 쉽게 만들지만, 대규모 애플리케이션에서는 성능 저하를 일으킬 수 있습니다.
  • 예시:
const fs = require("fs"); 
 const data = fs.readFileSync("/path/to/file"); 
 console.log(data.toString());

import

  • import는 ES6(ECMAScript 2015) 모듈 시스템의 일부이며, 브라우저 환경과 최신 JavaScript 및 TypeScript에서 주로 사용됩니다.
  • 비동기적으로 모듈을 로드합니다. 이는 모듈의 로딩이 백그라운드에서 이루어지며, 다른 코드의 실행을 차단하지 않는다는 의미입니다.
  • 호이스팅(hoisting)됩니다. 이는 import 문이 파일의 최상단으로 끌어올려져 실행된다는 뜻입니다. 따라서 import 문은 항상 모듈의 시작 부분에 위치하게 됩니다.
  • 정적 분석이 가능합니다. 컴파일 시점에 모듈 의존성을 파악할 수 있어, 코드 최적화와 에러 검출에 유리합니다.
  • 모듈 로딩 순서가 보장됩니다. 순환 의존성 문제를 더 잘 처리할 수 있습니다.
  • 트리 쉐이킹(Tree Shaking)을 지원합니다. 사용하지 않는 코드를 제거하여 번들 크기를 줄일 수 있습니다.
  • 예시:
    import fs from "fs"; 
    const data = fs.readFileSync("/path/to/file"); 
    console.log(data.toString());

2. 주요 차이점

특징 require import
모듈 시스템 CommonJS ES Module
사용 환경 Node.js (주로) 최신 브라우저, Node.js (ECMAscript 모듈 지원)
로드 방식 동기적 (Synchronous) 비동기적 (Asynchronous)
모듈 호출 시점 런타임(Run-time) 컴파일 타임(Compile-time)
호이스팅 지원 지원하지 않음 지원 (항상 스코프의 최상단에 위치)
전체 모듈 가져오기 require(module) 전체 import * as module from 형식 가능
부분 가져오기 const {method} = require(module) import { method } from module

3. 동작 방식의 차이

동기 vs 비동기

  • require는 동기적으로 모듈을 로드합니다. 즉, require 호출이 끝날 때까지 다음 코드가 실행되지 않습니다. 이는 I/O를 포함하는 경우 속도가 느려질 수 있다는 단점이 있습니다.
  • import는 비동기적으로 모듈을 로드하여 성능 최적화에 유리합니다. 브라우저나 번들러가 모듈을 미리 분석하기 때문에 코드 실행을 더 빠르게 할 수 있습니다.

런타임 vs 컴파일 타임

  • require는 런타임에 평가되므로, 조건부로 모듈을 가져올 수 있습니다.
    if (condition) { 
    	const module = require("some-module"); 
    }
  • import는 컴파일 타임에 정적으로 분석됩니다. 따라서 조건문 내에서 사용할 수 없습니다.

4. 브라우저 환경과 Node.js 환경에서의 차이

  • 브라우저에서는 ES 모듈을 지원하기 시작했으며, import가 기본적으로 사용됩니다. 반면 require는 Node.js의 CommonJS 모듈 시스템에 의존하기 때문에 브라우저에서는 직접 사용할 수 없습니다.
  • Node.js에서는 require를 전통적으로 사용하지만, ES 모듈을 지원하는 최신 버전에서는 import도 사용할 수 있습니다. 이때 package.json 파일에 "type": "module"을 설정해야 합니다.

5. ES 모듈의 특징과 CommonJS 모듈과의 차이

ES 모듈

  • 정적 구조: 모듈의 구조가 정적이기 때문에, 코드가 로드되기 전에 전체 모듈의 구조를 알 수 있습니다. 이를 통해 최적화가 가능하며, dead code 제거(tree-shaking)에 유리합니다.

Dead code(데드 코드)란 프로그램에서 실행되지 않거나 사용되지 않는 코드를 의미합니다. 이는 불필요한 메모리를 차지하고 프로그램의 크기를 증가시키며, 성능에 부정적인 영향을 줄 수 있습니다. Dead code의 예시로는 다음과 같은 것들이 있습니다:

  • 호출되지 않는 함수
  • 사용되지 않는 변수
  • 도달할 수 없는 코드 (예: return 문 이후의 코드)
  • 조건이 항상 false인 if 문 내부의 코드

Tree-shaking은 ES 모듈의 정적 구조를 활용하여 사용되지 않는 코드를 제거하는 최적화 기술입니다. 이를 통해 번들 크기를 줄이고 애플리케이션의 로딩 속도를 향상시킬 수 있습니다. Tree-shaking은 주로 Webpack, Rollup과 같은 모던 번들러에서 지원됩니다.

  • 엄격한 모드(strict mode): 모든 ES 모듈은 기본적으로 엄격 모드로 실행됩니다.
  • import는 블록 스코프를 따릅니다.

CommonJS 모듈

  • 동적 구조: 런타임에 모듈을 가져올 수 있습니다. 이는 동적으로 모듈을 가져오는 상황에서 유리합니다. 예를 들어:
    • 조건부 모듈 로딩: 특정 조건에 따라 다른 모듈을 로드해야 할 때 유용합니다.
    • 플러그인 시스템: 사용자가 선택한 플러그인을 동적으로 로드할 수 있습니다.
    • 성능 최적화: 필요한 시점에 모듈을 로드하여 초기 로딩 시간을 줄일 수 있습니다.
    • 에러 처리: 모듈 로딩 실패 시 대체 모듈을 로드하거나 에러를 처리할 수 있습니다.
    • 국제화: 사용자의 언어 설정에 따라 다른 언어 모듈을 동적으로 로드할 수 있습니다.
      이러한 유연성은 복잡한 애플리케이션에서 특히 유용할 수 있습니다.
  • CommonJS 모듈은 strict mode가 기본 적용되지 않습니다.

6. require와 import의 상호 호환성

Node.js에서 requireimport를 함께 사용하려면 몇 가지 사항을 고려해야 합니다.

  • require에서 import 모듈을 사용하려면 ES 모듈이 default export를 갖고 있어야 합니다.
    // someModule.js 
    export default function () { 
    console.log("Hello from ES module"); 
    } // CommonJS 환경 
    const someModule = require("./someModule").default; 
    someModule();
  • 반대로, import에서 require를 사용할 때는 import가 CommonJS 모듈을 기본적으로 default import로 처리하기 때문에 호환성을 위한 추가 조치가 필요할 수 있습니다.

7. 실전 활용 예시

1) TypeScript에서의 사용

TypeScript는 기본적으로 ES 모듈을 따르며, import를 권장합니다. 하지만 Node.js 환경에서 CommonJS 모듈을 가져와야 하는 경우, esModuleInteroptrue로 설정하여 더 편리하게 사용할 수 있습니다.

  • tsconfig.json
{   "compilerOptions": { 
          "esModuleInterop": true 
    } 
}

2) 번들러 환경에서의 사용

Webpack, Rollup, Parcel 등의 번들러는 ES 모듈을 권장하며, import를 통해 모듈을 가져올 때 더 나은 최적화 효과를 얻을 수 있습니다.

8. 어떤 것을 사용해야 할까?

  • Node.js 프로젝트: 모듈 시스템의 종류에 따라 결정됩니다. 대부분의 경우 최신 프로젝트에서는 import를 사용하는 것이 좋지만, 기존 프로젝트나 CommonJS 모듈과의 호환성이 필요한 경우 require를 사용합니다.
  • 브라우저 환경: 반드시 import를 사용해야 합니다. 최신 브라우저는 ES 모듈을 네이티브로 지원합니다.

결론

  • require는 Node.js의 CommonJS 모듈 시스템에서, import는 ES6의 모듈 시스템에서 사용되는 방법입니다.
  • require는 동기적이고 런타임에 모듈을 로드하는 반면, import는 비동기적이며 컴파일 타임에 로드됩니다.
  • import는 최신 JavaScript 프로젝트와 브라우저 환경에서 권장되며, require는 주로 Node.js 환경에서 사용됩니다.

두 방법 모두 각자의 장단점이 있으므로, 프로젝트의 환경과 요구 사항에 따라 적절하게 선택해야 합니다.