Programming/Client

[Javascript] 파일 및 모듈 내보내기 (script / Common JS / ES Modules 그리고 Bundler)

Jiwoo 2023. 9. 30. 18:12

1️⃣ script

<script src="./module1.js"></script>
<script src="./module2.js"></script>
<script src="./module3.js"></script>

초기에는 프로그램의 규모가 작았기 때문에 약간의 상호작용만 제공하면 됐기에 다른 파일을 불러올 때 script를 사용하여 전체 다 가져옴


문제점
자바스크립트는 파일마다 독립적인 스코프를 가지지 않고 전체 파일이 하나의 전역 객체를 공유하여 사용
⇒ 전역변수가 중복되는 문제가 발생할 수 있음 (위 같이 여러 개의 파일로 분리해도 1, 2, 3이 같은 스코프를 공유하기 때문에 모듈화가 아님)


문제 해결

  • 모듈: 재사용 가능한 코드 조각으로 애플리케이션을 구성하는 개별 요소

파일을 모듈로 나누어 내보내고 불러옴 ⇒ Node.js는 Common JS를 채택

2️⃣ Common JS

module.exports = { ... }        // 모듈 내보낼 때

const utils = require('utils'); // 모듈 가져올 때

module.exports로 모듈을 내보내고 require()로 접근


Bundler 사용 이유

requier()은 Node.js에만 있고 브라우저에서 지원하지 않기에 브라우저가 이해하게 하려면 번들러가 필요함

번들러는 빌드하면서 require()를 지우고 모듈을 통채로 가져와서 적용해줌

⇒ 이 때문에 Browerify / webpack 같은 툴이 탄생하고 발전함

// a.js
module.exports = "Hi"

// b.js
var a = require("./a")
console.log(a) // = hi

// a.js와 b.js를 bundle하면
var b = "Hi"
console.log(b);

문제점

번들러가 모듈 내의 메소드 전체를 가져오다보니 크기가 커짐
⇒ 일부만 가져오며 정적으로 분석할 수 있게끔 하는 방식 사용 : ES Module

// user.js
module.exports = {
  name: 'Eunjae',
  address: '...',
  phone: '..'
}

// app.js
var user = require('./user') 
console.log(user. name)

// 번들러로 가져올 때
var user = { // name만 사용하지만 전체 다 가져옴
  name: 'Eunjae',
  address: '...',
  phone: '..'
}
console.log(user. name)

3️⃣ ES Modules

export.default =()=> { ... }; // 모듈 내보낼 때

import utils from 'utils';    // 모듈 가져올 때

export로 내보내고 import로 접근


번들 과정

요즘 모던 브라우저는 거의 import를 지원하지만 그렇지 않은 브라우저가 간혹 있으니 번들러를 사용해야함

CJS에 비해 가져올 모듈의 일부를 명시적으로 지정할 수 있기에 효율적임

// user.js
export const name = 'Eunjae'

export const address = '..'

export const phone = '..'

// app.js
import { name } from './user'
console.log(name)

// 번들 하면
// index.js
const name = 'Eunjae' // 이렇게 모듈 중 일부만 가져옴
console.log(name)

번들러는 require / import 상관없이 같은 방식으로 번들함
이렇게 만든 파일은 전통적인 방식으로 HTML에 포함되어 배포됨

<script src="./index.dif98w.js"></script>

지원되는 브라우저는...

<script> 태그에 type="module"을 넣으면 안에서 import 구문을 사용할 수 있음

<script type="module">
  import { name } from './user.js'
  console.log(name)
</script>

📌 번들러를 버릴 수 없는 이유

require / import를 브라우저에서 사용하기 위해 탄생했지만 이 밖의 기능도 많이 해서 의존도가 높아짐 (sass 파일을 css로 변환)

현재는 자바스크립트 & 브라우저 API & webpack or Next.js의 설정 등의 경계가 흐려지고 섞인 상황

이를 명확하게 이해하고 정리하기는 어렵고 대강 타임라인만 이해하면 될 듯!



참고

모듈과 CommonJS vs ES modules
Common JS, ES Modules, Bundler, ...