Skip to content

Commit

Permalink
nextjs에 CommonJS vs ES Module 에 대한 내용 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
mainmethod0126 committed Jan 21, 2025
1 parent 3263b3a commit c974c52
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 1 deletion.
4 changes: 3 additions & 1 deletion pages/msa/_meta.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{
"service-discovery": "서비스 디스커버리란?",
"add-jwt-verification-proxy-to-istio": "istio에 jwt 검증 proxy 추가하기"
"add-jwt-verification-proxy-to-istio": "istio에 jwt 검증 proxy 추가하기",
"istio-proxy-ssl": "istio에서 ssl/tls 통신하기 위한 방법",
"istio-grpc-path-based-jwt-authentication-or-authentication-exception-handling-method": "istio에서 grpc 사용 시 path 기반 jwt 인증 또는 인증 예외 처리 방법"
}
3 changes: 3 additions & 0 deletions pages/nextjs/_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"commonjs-vs-esmodule": "CommonJs vs ES Module"
}
317 changes: 317 additions & 0 deletions pages/nextjs/commonjs-vs-esmodule.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
# CommonJS vs ES Module: 자바스크립트 모듈 시스템 완벽 가이드

## 모듈 시스템이란?

모듈 시스템은 자바스크립트 코드를 재사용 가능한 단위로 분리하고 관리하는 방법을 제공합니다. 코드를 모듈화함으로써 다음과 같은 이점을 얻을 수 있습니다:

- 코드의 재사용성 향상
- 의존성 관리의 용이성
- 네임스페이스 충돌 방지
- 코드의 캡슐화와 은닉화
- 더 나은 코드 구조화와 유지보수성

## CommonJS

CommonJS는 Node.js에서 채택한 모듈 시스템으로, 서버 사이드 자바스크립트를 위해 설계되었습니다.

### 주요 특징

```javascript
// 모듈 내보내기
module.exports = {
someFunction: function() {},
someValue: 42
};

// 또는
exports.someFunction = function() {};
exports.someValue = 42;

// 모듈 가져오기
const myModule = require('./myModule');
```

### 동작 방식
1. 동기적 로딩: 모듈이 순차적으로 로드됨
2. 모듈 캐싱: 한 번 로드된 모듈은 메모리에 캐시됨
3. 값의 복사: require로 가져온 객체는 복사본임

## ES Module

ES Module은 ECMAScript 2015(ES6)에서 도입된 표준 모듈 시스템으로, 브라우저 환경을 고려하여 설계되었습니다.

### 주요 특징

```javascript
// named export
export const someFunction = () => {};
export const someValue = 42;

// default export
export default class MyClass {};

// 모듈 가져오기
import { someFunction, someValue } from './myModule';
import MyClass from './myModule';
import * as myModule from './myModule';
```

### 동작 방식
1. 정적 분석: 임포트/익스포트가 파일의 최상위 레벨에서 정적으로 결정됨
2. 비동기 로딩: 필요한 모듈을 병렬로 로드 가능
3. Live binding: 익스포트된 값의 실제 바인딩을 참조

## 주요 차이점

1. **문법적 차이**
- CommonJS: require()와 module.exports 사용
- ES Module: import와 export 키워드 사용

2. **로딩 방식**
- CommonJS: 동기적 로딩
- ES Module: 비동기적 로딩 (더 나은 성능)

3. **정적 vs 동적**
- CommonJS: 런타임에 동적으로 모듈 로딩 가능
- ES Module: 정적 분석으로 빌드 타임 최적화 가능

4. **사용 환경**
- CommonJS: 주로 Node.js 환경
- ES Module: 브라우저와 Node.js 모두 지원


## 모듈 시스템 도입 전후 비교

### 도입 이전의 문제점

1. **전역 스코프 오염**
```javascript
// math.js
function add(a, b) {
return a + b;
}

// utils.js
function add(str1, str2) { // 이름 충돌! 전역 스코프의 add 함수를 덮어씀
return str1 + str2;
}
```

2. **스크립트 의존성 관리**
```html
<!-- 순서가 매우 중요했음 -->
<script src="jquery.js"></script>
<script src="bootstrap.js"></script> <!-- jquery 의존성 필요 -->
<script src="app.js"></script> <!-- 위 라이브러리들 의존성 필요 -->
```

3. **네임스페이스 패턴 사용**
```javascript
// 전역 오염을 피하기 위한 네임스페이스 패턴
var MyApp = {
math: {
add: function(a, b) {
return a + b;
}
},
utils: {
add: function(str1, str2) {
return str1 + str2;
}
}
};
```

### 도입 이후의 개선점

1. **모듈 스코프**
```javascript
// math.js
export function add(a, b) {
return a + b;
}

// utils.js
export function add(str1, str2) { // 다른 모듈이라 이름 충돌 없음
return str1 + str2;
}

// app.js
import { add as mathAdd } from './math.js';
import { add as stringAdd } from './utils.js';
```

2. **의존성 자동 관리**
```javascript
// 의존성이 자동으로 관리됨
import { Component } from 'react';
import { render } from 'react-dom';
import { MyComponent } from './components/MyComponent';
```

3. **캡슐화**
```javascript
// module.js
const privateFunction = () => { // 모듈 내부에서만 접근 가능
return 'private';
};

export const publicFunction = () => { // 외부로 공개할 기능만 export
return privateFunction();
};
```

## Named Export와 Default Export의 차이

### Named Export

1. **여러 개의 값을 내보낼 수 있음**
```javascript
// utils.js
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
export const multiply = (a, b) => a * b;

// app.js
import { add, subtract } from './utils'; // 필요한 것만 가져올 수 있음
import * as utils from './utils'; // 전체를 가져올 수도 있음
```

2. **이름이 고정됨**
```javascript
// 반드시 동일한 이름을 사용해야 함 (as로 별칭은 가능)
import { add as sum, subtract as minus } from './utils';
```

3. **정적 분석이 용이**
```javascript
// 빌드 타임에 사용되는 export를 파악할 수 있음
export const config = {
api: 'https://api.example.com',
timeout: 5000
};
```

### Default Export

1. **모듈당 하나의 default export만 가능**
```javascript
// MyComponent.js
const MyComponent = () => {
return <div>Hello</div>;
};

export default MyComponent;

// app.js
import MyComponent from './MyComponent'; // 중괄호 없이 임포트
import CustomName from './MyComponent'; // 원하는 이름으로 임포트 가능
```

2. **이름 변경이 자유로움**
```javascript
// math.js
export default function add(a, b) {
return a + b;
}

// 다른 파일에서
import sum from './math'; // 원하는 이름으로 임포트
import addition from './math'; // 이름이 달라도 동작
```

### 사용 가이드라인

1. **Named Export 선호하는 경우**
- 라이브러리나 유틸리티 함수들
- 여러 개의 독립적인 기능을 제공할 때
- 정적 분석과 트리 쉐이킹이 중요할 때

```javascript
// utils.js
export const formatDate = (date) => { /* ... */ };
export const formatCurrency = (amount) => { /* ... */ };
export const formatNumber = (num) => { /* ... */ };
```

2. **Default Export 선호하는 경우**
- React 컴포넌트
- 클래스
- 모듈의 주요 기능이 하나일 때

```javascript
// UserProfile.js
class UserProfile extends Component {
/* ... */
}

export default UserProfile;
```

3. **혼합 사용**
```javascript
// api.js
export const BASE_URL = 'https://api.example.com';
export const getHeaders = () => { /* ... */ };

export default class ApiClient {
/* ... */
}
```


## 충돌 문제

### 1. 혼용 시 발생하는 문제

```javascript
// 🚫 잘못된 사용
const myModule = require('./esModule'); // ES 모듈을 CommonJS로 임포트
import { something } from './commonjsModule'; // CommonJS를 ES 모듈로 임포트
```

### 2. 해결 방법

1. **package.json 설정**
```json
{
"type": "module" // ES Module 사용
// 또는
"type": "commonjs" // CommonJS 사용
}
```

2. **확장자 구분**
- `.mjs`: ES Module
- `.cjs`: CommonJS

3. **interop 헬퍼 사용**
```javascript
// ES Module에서 CommonJS 모듈 사용
import cjsModule from 'cjs-module';
const { createRequire } = require('module');
const require = createRequire(import.meta.url);
```
## 결론
### ES Module 선택을 추천하는 이유
1. **표준화**: ECMAScript 표준의 일부로 장기적인 지원 보장
2. **성능**: 정적 분석과 트리 쉐이킹 가능
3. **브라우저 호환성**: 브라우저에서 네이티브 지원
4. **더 나은 개발 경험**: 더 명확한 문법과 IDE 지원
### 단, 다음 경우 CommonJS 고려
1. 레거시 Node.js 프로젝트 유지보수
2. 동적 모듈 로딩이 필수적인 경우
3. 즉시 실행이 필요한 서버 사이드 스크립트
### 미래 전망
- ES Module이 산업 표준으로 자리잡는 추세
- 대부분의 새로운 프로젝트는 ES Module 채택
- CommonJS는 레거시 지원을 위해 당분간 공존할 것으로 예상
이러한 이해를 바탕으로, 새로운 프로젝트를 시작할 때는 ES Module을 사용하는 것이 미래 지향적인 선택이 될 것입니다.
3 changes: 3 additions & 0 deletions theme.config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ const config: DocsThemeConfig = {
if (title === "security") {
return <>📒 {title}</>;
}
if (title === "nextjs") {
return <>📒 {title}</>;
}
if (title === "vue") {
return <>📒 {title}</>;
}
Expand Down

0 comments on commit c974c52

Please sign in to comment.