
프론트엔드 면접 질문에서 지긋지긋하게 듣는 질문 중 하나다.
온전한 내 생각을 나열해보겠다.
- 모듈 시스템에 대한 이해도 평가 -> 코드 구조화와 관리 능력을 가늠하는 지표
- 프로젝트 설정 및 빌드 도구 경험 -> import와 require는 각각 다른 환경과 도구설정이 요구된다. 해당 예시는 프로젝트에 대한 설정 경험에 대한 지표.
- 성능 최적화 인식 -> 트리 쉐이킹 가능 여부 등 성능 최적화에 대한 지원자의 인식을 평가할 수 있다.
- 비동기 프로그래밍 이해 -> import 문의 동적 임포트 기능은 비동기 프로그래밍 개념과 연관됨.
- 생태계 파악에 얼마나 민감한지 확인 가능
위의 흐름대로 보면 어떤 꼬리 질문이 나오는지 어느정도 가늠이 된다.
require와 import는 모두 JavaScript에서 모듈을 불러오는 데 사용되는 방식이지만, 몇 가지 중요한 차이점이 있다.
내가 알고 싶은 것만 조금 길게 쓰고 그 외의 것들은 요약해서 정리해봤으니 도움이 된다면 좋겠다.
CommonJS 모듈 시스템에서 사용된다.
함수 호출 형태로 사용된다: const module = require('module-name');
파일의 어느 위치에서든 사용할 수 있다.
import:
ES6(ES2015) 모듈 시스템에서 사용된다.
선언적 형태로 사용된다: import module from 'module-name';
파일의 최상단에서만 사용할 수 있다.
동적으로 모듈을 불러올 수 있다. 즉, 조건문 내에서 사용하거나 변수를 통해 모듈 이름을 지정할 수 있다.
if (condition) {
const module = require('module-name');
}
정적으로 모듈을 불러올 수 있다.(핵심) 컴파일 시점에 모듈 의존성이 결정된다.
동적 임포트를 위해서는 import() 함수를 사용해야 한다. -> 비동기 패턴
<script async type="module">
import {counter} from './test.js';
counter.count();
</script>
런타임에 동기적으로 실행된다.
모듈이 필요한 시점에 로드되고 실행된다.
파일의 시작 부분에서 실행되며, 모든 import문이 실행된 후에 모듈 본문이 실행된다.
이를 통해 더 효율적인 정적 분석과 트리 쉐이킹(tree shaking)이 가능하다.
모듈 전체를 로드
필요한 부분만 선택적으로 로드
import { specificFunction } from 'module-name';
모듈을 처음 불러올 때 캐시하고, 이후 호출에서는 캐시된 버전을 반환
모듈은 한 번만 평가되며, 여러 번 import해도 같은 인스턴스 공유
기본적으로 동기적. 비동기 로딩을 위해서는 별도의 방법을 사용해야함.
import() 함수를 사용하여 동적으로 모듈을 비동기적으로 로드할 수 있음.
브라우저에서 직접 지원되지 않는다. 그래서 Browserify나 Webpack이 필요한거다.
최신 브라우저에서 네이티브로 지원함 (type="module" 스크립트 태그 사용).
순환 의존성을 허용하지만, 부분적으로 초기화되지 않은 객체를 반환할 수 있다.
순환 의존성을 더 잘 처리한다. 모듈이 완전히 평가되기 전에 바인딩 제공.
개인적으로 조금 까다롭다. TypeScript에서 사용할 수 있지만, 타입 정보를 자동으로 가져오지 않기 때문이다.
TypeScript와 더 잘 통합되며, 모듈의 타입 정보를 자동으로 가져온다.
위 차이점을 천천히 읽다 보면 어떠한 키포인트를 얻어갈 수 있을 것이다.
미리 파악했으면 이 포스팅을 더이상 읽을 필요가 없다. 뒤로가기 눌러라.
바로
두괄식으로 풀자면 import가 require보다 메모리를 덜 사용할 수 있다는게 결론이다.
이유는 아래와 같다. 매우 많지만 핵심 몇가지만 나열한다.
1. 정적분석이 가능하면 코드가 실행되기 전에 그 모듈의 어떤 부분이 사용될 지 미리 알 수 있다. 위에 핵심이라고 마킹한 문장이 핵심이다.
-> 즉, 이를 통해 불필요한 코드를 제거하는 트리쉐이킹이 가능해져, 결과적으로 번들 크기가 줄어들고 메모리 사용량이 감소한다.
2. import로 가져온 모듈은 한 번만 평가된다. 여러 곳에서 같은 모듈을 import해도 단일 인스턴스만 생성된다.
-> 중복된 모듈 인스턴스로 인한 메모리 낭비를 방지한다.
3. import()를 사용한 동적 import는 필요한 시점에 모듈을 비동기적으로 로드할 수 있게 해준다
-> 초기 로딩 시 메모리 사용량을 줄이고, 필요할 때만 메모리를 사용하게 해준다.
CommonJs 방식으로 모듈을 내보낼때는 ES6처럼 명시적으로 선언하는 것이 아니라 특정 변수나 그 변수의 속성으로 내보낼 객체를 세팅해줘야 한다.특히, 제일 햇갈리는 부분이 바로 유사해 보이는 export 변수와 module.exports 변수를 상황에 맞게 잘 사용해야 한다는 점이다
2가지 규칙만 기억하면 된다.
1. 여러개의 객체를 내보낼 경우 → export.변수 의 개별 속성으로 할당
2. 딱 하나의 객체를 내보낼 경우 → module.exports = 객체 자체에 할당
import와 require는 개발 환경에 따라 사용 방식과 지원 범위에 차이가 있다.
Node.js 13.2.0 버전부터 실험적으로 지원되기 시작했다.
.mjs 확장자를 사용하거나 package.json에 "type": "module"을 설정해야함.
Node.js의 기본 모듈 시스템으로, 모든 버전에서 지원된다.
기본적으로 지원되며, 타입 정보도 함께 가져올 수 있다.
지원되지만, 타입 정보를 자동으로 가져오지 않는다.
ES6+ 코드를 변환할 때 자동으로 지원함
CommonJS 모듈 시스템을 사용하는 코드로 변환할 수 있다.
import와 require 모두 지원. 트리 쉐이킹은 import 문을 사용할 때만 가능.
import와 require 모두 사용 가능. 최신 버전에서는 import 사용을 권장.
import를 사용. 파일 확장자를 명시적으로 지정해야 함.
import와 require 모두 사용 가능하지만, 설정에 따라 다를 수 있다. 일관성을 위해 하나의 방식을 선택하는 것이 일반적.
대부분 Node.js 기반이므로 require를 주로 사용. 최신 환경에서는 import도 사용 가능.