JS - ES modules

sarang_daddy·2023년 4월 8일
0

Javascript

목록 보기
18/26
post-thumbnail

🤔 모듈화란 무엇이고 왜 필요할까?

일반적인 웹사이트 개발에서 많은 JavaScript파일을 관리하기란 코드 간의 의존성 문제로 어렵다.
JS파일들의 의존성 관리를 도와주면서 여러 장점을 제공해 주는 ES Modules에 대해 학습하고 사용해보자.


✅ 모듈화 프로그래밍(Modular Programming)

모듈화 프로그래밍은 프로그램을 작은 독립적인 단위로 분할하여 개발하는 방법론을 말한다.

  • 프로그램을 작은 단위인 모듈(Module)로 나누고
  • 각 모듈이 독립적으로 작동하도록 설계하고
  • 각 모듈은 하나 이상의 관련된 함수, 변수, 클래스 등을 포함하며
  • 이러한 모듈들이 상호작용하여 프로그램을 완성하도록 한다.

모듈화 프로그래밍의 장점

  1. 코드의 재사용성 : 각 모듈은 독립적으로 작동하기에 다른 프로그래에서도 사용이 재사용이 가능하다.
  2. 유지보수성 향상 : 각 모듈은 독립적으로 작동하기에 다른 모듈에 영향을 미치지 않고 수정할 수 있다.
  3. 코드의 가독성 향상 : 각 모듈은 자체적으로 기능을 수행하므로, 코드의 구조가 더욱 명확하다.

이러한 모듈화 프로그래밍 방식을 JS에서 지원해주는 모듈 시스템이 ES modules 이다.


✅ ES modules

ES modules은 ECMAScript 6(ES6)에서 추가된 JavaScript의 모듈 시스템이다.
이전에는 JavaScript에서 모듈을 구현하는 여러가지 방법이 있었지만,
ES modules은 표준화되어 있으며 다른 모듈 시스템과 호환성도 높다.

ES modules에서는 export와 import 구문을 사용하여
모듈 내에서 정의한 변수, 함수, 클래스 등을 외부로 보내거나 가져올 수 있다.

<head>
	<script> type="module" src="./app.js"></script>
</head>

<!--
HTML에는 한개의 JS 파일을 모듈로 추가한다.
이렇게 추가하는 시작점의 JS를 entry point라고 한다.

script 태그에 type="module"로 해야함을 기억하자.
 -->
// app.js

import { hello, sayHello } from './greeting.js';

console.log(hello); // "Hello, world"
greet("kim"); // "Hello, world My name is kim."

// import 키워드를 사용하여 greeting.js 모듈에서 변수와 함수를 가져온다.
// app.js는 greeting.js를 의존하고 있다.
// greeting.js

export const hello = "Hello, world";
export function sayHello(name) {
	console.log(`${hello} My name is ${name}.`);
}

// export 키워드를 사용하여 hello 변수와 sayHello 함수를 외부로 보낸다.
// 다양한 import 방식

// export된 객체 중 foo만 사용한다(destructuring)
import { foo } from "./foo.js";

// bar라는 객체를 활용할 수 있다.
// 같은 도메인이라면 URL방식도 사용가능하다. 다른 도메인은 CORS때문에 지원 불가능
import  * as bar from "http://127.0.0.1:8080/bar.js";

const pobiResult= bar.pobi()
  • 모듈은 파일 단위로 분리되며, 자체적인 스코프를 가지므로 다른 모듈과 충돌을 방지한다.
  • 모듈은 단 한번만 로드되며, 메모리를 효율적으로 사용한다.
  • ES modules은 JS에서 모듈화 프로그래밍을 구현하는 가장 표준화된 방법이다.

Understanding ES6 Modules
ES modules까지의 흐름

📎 Entry Point

entry point란 프로그램이 실행되기 시작하는 지점을 정의하는 파일을 가리킨다.

  • entry point 파일에서는 필요한 다른 모듈을 불러오거나 초기화 작업을 수행한다.
  • WebPack과 같은 모듈 번들러에서도 사용된다.
  • 모듈 번들러란 모든 모듈을 하나의 파일로 번들링하는 작업을 말한다.
  • 모듈 번들러는 Entry Point를 시작으로 모든 종속성을 탐색하고 하나의 번들 파일로 생성한다.

📎 CORS (Cross-Origin Resource Sharing)

CORS란 웹에서 실행되는 스크립트가 다른 도메인에 있는 리소스에 접근하는 것을 제안하는 보안 기능이다.
ES modules에서는 다른 도메인에 있는 모듈을 가져오려면 해당 도메인에서 CORS 정책을 설정해줘야 한다.

📎 SPA (Single Page Application)

하나의 HTML 페이지를 기반으로 하여 동적으로 데이터를 로드하고 변경하는 웹 어플리케이션을 말한다.
새로운 패이지를 로드하는 대신 필요한 데이터만 서버로부터 받아와서 화면에 동적으로 보여준다.

  • React, Vue 같은 프레임워크를 사용하여 개발되며 이러한 프레임워크들은 컴포넌트 기반으로 UI를 구성한다.
  • 필요한 데이터를 API를 통해 서버로부터 받아와서 렌더링 한다.
  • 클라이언트(사용자) 사이드 라우팅을 사용하여 URL의 경로에 따라 필요한 데이터를 로드하고 화면을 변경한다.
  • 사용자 경험을 개선하고 서버 부하를 줄이는 현대적인 웹 어플의 형태 중 하나다.

📎 ES modules + SPA

하나의 HTML 페이지를 기반으로 동적인 동작을 하는 SPA는
필요한 파일만 로드하고 사용해야 하기에 파일들을 모듈화 해주는게 중요하다.

따라서, SPA에서는 ES modules를 사용함으로써 모듈간 의존성을 관리하고 자원을 효율적으로 사용 할 수 있다.


⭐️ 그래서 모듈화란 무엇이고 왜 필요하다고?

  • 모듈화란 코드를 작성할 때, 비슷한 기능을 하는 함수나 변수들을 묶어서 하나의 모듈로 만들어 관리하는 것
  • 모듈화를 하지 않으면, 코드의 양이 많아질수록 복잡도가 증가하여 유지보수가 어려워진다.
  • 모듈화를 하면 코드를 작은 단위로 분할하여 각각의 모듈에서 독립적으로 작업할 수 있기 때문에
    코드의 복잡도를 낮출 수 있다.
  • 모듈화를 하면 코드를 재사용할 수 있기 때문에 코드의 생산성을 높일 수 있다.

🧐 함께 고민해보기

import를 여러군데서 한다면?

  • 하나의 모듈을 여러 모듈에서 import한다는 것은 해당 모듈의 인스턴스를 공유하는 것을 의미한다.
  • 즉, import 한 모듈의 인스턴스는 메모리에서 단 한 번만 로드되고 다른 모듈에서도 이 인스턴스를 참조하여 사용하는 것이다. (ES 모듈의 내부 캐시 기능)
  • 이것은 모듈간의 의존성을 관리하는 데 매우 유용한데 여러 모듈에서 import하더라도 메모리에 한 번만 로드되기에 코드의 중복을 줄이고 메모리 사용량도 줄일 수 있다.
  • 그리고 해당 모듈의 상태가 다른 모듈에서 변경되면 다른 모듈에서도 해당 변경 사항이 반영된다. (상태관리)
  • 단, 여러 모듈에서 동시에 해당 모듈의 인스턴스를 수정할 경우 문제가 발생할 수 있기에 해당 모듈에서 읽기 전용으로 사용되는 객체나 함수를 내보내거나, 객체를 복사하여 사용하는 방법 등으로 문제를 방지할 수 있게 설계를 해야한다.

캐시(cache)

  • ES모듈은 내부적으로 캐시를 사용하여 import한 모듈의 코드를 로드하고 실행한다.
  • 모듈을 로드할 때 이 캐시를 검색하고 이전에 로드된 모듈이 있다면 캐시에서 해당 인스턴스를 반환한다.
  • 따라서 동일한 모듈을 여러 번 import하는 경우에도 인스턴스는 한번만 생성되고 참조된다.

상태관리란?

  • 데이터를 공유하고 다루는 방법을 정의하는 소프트웨어 아키텍처릐 한 부분이다.
  • ESM에서 상태를 공유하는 것은 상태를 중앙 집중적으로 관리하고 모듈 간의 의존성을 줄이는데 유용하다.
  • 단, 상태 공유가 많아지면 그만큼 복잡성도 증가하기에 적절한 상태 관리 전략이 필요하다.
  • 상태를 변경하는 방법, 공유되는 데이터의 범위, 데이터 일관성 등을 고려해야 한다.

다이나믹 임포트(Dynamic import)

  • ES모듈에서 동적으로 모듈을 로드하는 방법이다.
  • 코드 실행 시점에 필요한 모율만 로드할 수 있으므로 애플리케이션의 초기 로딩 속도를 개선할 수 있다.
  • 또한 모듈이 필요한 시점에만 로드하기 때문에 메모리 사용량을 줄일 수 있다.

다이나믹 임포트는 import 키워드 대신에 import() 함수를 사용하여 모듈을 로드한다.

async function loadModule(modulePath) {
  const module = await import(modulePath);
  return module;
}

const myModule = await loadModule('./myModule.js');
  • loadModule 함수는 모듈 경로를 인수로 받아 해당 모듈을 비동기적으로 로드한다.
  • 이후에 로드된 모듈을 myModule 상수에 할당하여 사용한다.

다이나믹 임포트는 Promise를 반환하기 때문에, Promise에 대한 처리가 가능하다.

async function loadModules(modulePaths) {
  const promises = modulePaths.map(modulePath => import(modulePath));
  const modules = await Promise.all(promises);
  return modules;
}

const [myModule1, myModule2] = await loadModules(['./myModule1.js', './myModule2.js']);
  • loadModules 함수는 모듈 경로 배열을 받아 해당 모듈들을 비동기적으로 로드한다.
  • Promise.all을 사용하여 로드된 모듈들을 한 번에 처리할 수 있다.
  • 이후에 로드된 모듈을 각각 myMoudle1, myMoulde2 상수에 할당하여 사용할 수 있다.
profile
한 발자국, 한 걸음 느리더라도 하루하루 발전하는 삶을 살자.

0개의 댓글