[Javascript] 13강 자바스크립트 모듈

img1.daumcdn.jpg

 

Javascript 프로그램은 꽤 작게 시작했습니다. 초기에 사용 된 대부분 스크립트는 독립적 작업을 수행하고, 필요한 경우 웹 페이지에 약간의 상호 작용을 제공해 일반적으로 큰 스크립트를 필요하지 않았습니다. 시간이 흐르고 많은 웹 브라우저에 실행되고 있는 완전한 애플리케이션을 실행할 수 있음과 동시 다른 컨텍스트(Node.js)에서 사용되게 되었습니다.

 

브라우저 호환성

 

js-examples/module-examples/basic-modules at main · mdn/js-examples

Code examples that accompany the MDN JavaScript/ECMAScript documentation - mdn/js-examples

github.com

 

자바스크립트 샘플의 첫 번째 예제(basic-modules를 보면 다음과 같은 파일 구조를 가지고 있습니다.

- index.html
- main.js
- modules/
    canvas.js
    square.js

 

modules 디렉토리의 두 모듈은 다음과 같습니다.

  • canvas.js - 캔버스 설정과 관련된 기능을 포함
    • create() - 지정한 ID를 가진 래퍼 <div> 태그 안에, 지정한 width 와 height를 가진 캔버스를 생성, 지장한 ID(첫 번쨰 인자)는 지정한 부모 요소(두 번째 인자)안에 추가, 캔버스의 2D 컨텍스트와 래퍼(wrapper div)의 ID가 들어있는 객체를 반환
    • createReportList() - 데이터를 출력하는데 사용할 수 있는, 지정한 래퍼 요소(div) 안에 추가 된 정렬되지 않은 리스트(ul)를 생성, 리스트의 ID 반환
  • square.js - 다음을 포함
    • name -- 문자열 'square'를 담고있는 상수
    • draw() -- 지정된 크기, 위치, 색상을 사용해 지정된 캔버스에 사각형을 그림. 사각형의 크기, 위치, 색상을 포함하는 객체를 반환
    • reportArea() -- 길이가 주어지면 사각형의 넓이를 지정한 보고서 리스트에 작성
    • reportPerimeter() -- 길이가 주어지면 사각형의 둘레를 지정한 보고서 리스트에 작성

1.Exporting module features

모듈 기능을 사용하려면 먼저 함수를 export 해야 합니다. 이 작업에는 export 문 (statement)을 사용해 수행합니다. 이를 사용하는 가장 쉬운 방법으로 모듈 밖에 내보내려는 항목 앞에 (export)배치하는 것입니다.

export const name = "square";

export function draw(ctx, length, x, y, color) {
  ctx.fillStyle = color;
  ctx.fillRect(x, y, length, length);

  return {
    length: length,
    x: x,
    y: y,
    color: color,
  };
}

 

function, var, let, const, class를 내보낼 수 있으나, 최상위 항목이어야 합니다, 예를 들어서 함수 안에 export를 사용할 수 없습니다. 여러 항목을 내보내는 더 편리한 방법으로 모듈 파일 끝에 하나의 export 문을 사용하는 것입니다.

export { name, draw, reportArea, reportPerimeter };

2.Importing features into your script

모듈에서 일부 기능을 내보낸 후, 이를 사용하도록 스크립트로 가져와야 합니다. 이때 import를 사용합니다. 

import {
  name,
  draw,
  reportArea,
  reportPerimeter,
} from "./14-1.Exporting-module";

 

import 문(statement)을 사용해 가져올 목록을 쉼표로 구분하고 나열한 뒤 괄호로 묶습니다. 그 뒤에 from을 사용해 모듈 경로의 파일 경로를 작성해줍니다. main.js를 참고하세요. 그러나 우리는 경로를 조금 다르게 작성했는데, "현재 위치"를 의미하는 점(.)을 구문으로 사용하고 있으며, 그 다음에 찾고자 하는 파일 경로를 뒤에 써줍니다.

 

이것은 상대적으로 전체 상대 경로를 작성하는 것보다 빠른 방법으로, URL이 더 짧아져 사이트 계층 구조의 다른 위치로 이동하더라도 작동하게 됩니다.

/js-examples/modules/basic-modules/modules/square.js

 

이러한 방식에서

./modules/square.js

 

이렇게 줄일 수 있습니다. 스크립트에 기능을 가져오면 동일한 파일 내 정의한 것처럼 기능을 적용할 수 있습니다. 


3.Applying the module to your HMTL

이제 main.js 모듈을 HTML 페이지에 적용하면 됩니다. HTML 페이지의 일반 스크립트를 적용한 것과 매우 유사하며 스크립트로 선언 시 <script> 태그에 type="module"을 포함시켜야 합니다.

<script type="module" src="main.js"></script>

 

기본적으로 모듈 기능을 가져오는 스크립트는 최상위 모듈로 작동하며, 이를 생략시 에러를 발생시킵니다. import와 export 문(statement)은 모듈 내에서만 사용할 수 있고 정규 스크립트는 아닙니다.


Default exports versus named exports

지금까지 우리가 export한 기능은 named exports로 구성되 있습니다. 각 항목(function, const 등)은 export 할 때 이름으로 참조되었으며, import 할 때 이 이름을 참조해 사용합니다. 그 외에 default export 라고 부르는 export도 있습니다. 이것은 모듈이 제공하는 기본 기능을 쉽게 만들 수 있도록 설계되어있습니다.

 

또한 Javascript 모듈은 기존의 CommonJS와 AMD 모듈 시스템과 함께 사용(interpolate)하는 데 도움이 됩니다. (Jason Orendoff에 의해 작성된 ES6 In Depth:Modules에 설명되 있습니다.

 

예제 중 basic-modules 프로젝트의 square.js 파일에서 임의된 색상, 크기, 위치로 갖는 사각형을 만드는 randomSquare()이라는 함수를 찾을 수 있습니다. 이것을 기본값으로 export 하려고 아래와 같이 사용합니다.

export default randomSquare;

 

중괄호는 없으며 대신 함수 앞에 export default를 추가해 다음과 같이 익명함수로 선언할 수 있습니다.

export default function(ctx) {
  
}

 

main.js 파일에 선언된 코드처럼 사용 시 default function이 import 됩니다.

import randomSquare from "./js/square.js";

4.Avoiding naming conflicts 

지금까지 캔버스 도형 그리기 모듈은 제대로 작동하는 것을 확인했습니다. 원이나 삼각형처럼 다른 도형을 그리는 모듈을 추가하려면 도형(shape)에는 draw(), reportArea() 등과 같은 관련 함수가 있을 것입니다. 동일한 이름의 여러 함수를 동일한 최상위 모듈로 가져오려면 충돌과 에러가 발생합니다.


5.Renaming imports and exports

import 와 export 문(statement)의 중괄호 안에 as 키워드를 사용하 새 함수의 이름으로 함께 사용하여, 최상위 모듈 내부의 함수들을 식별 가능한 이름으로 변경할 수 있습니다.

// inside module.js
export { function1 as newFunctionName, function2 as anotherNewFunctionName };

// inside main.js
import { newFunctionName, anotherNewFunctionName } from "./modules/module.js";
// inside module.js
export { function1, function2 };

// inside main.js
import {
  function1 as newFunctionName,
  function2 as anotherNewFunctionName,
} from "./modules/module.js";

 

이 모듈듈 각각에 내부적으로 동일한 이름의 기능이 있습니다. 따라서 각각 하단에 동일한 export 문(statement)가 있습니다.

export { name, draw, reportArea, reportPerimeter };

 

이것들을 main.js에 가져올 때 우리는 다음과 같이 시도할 수 있습니다.

import { name, draw, reportArea, reportPerimeter } from "./modules/square.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/circle.js";
import { name, draw, reportArea, reportPerimeter } from "./modules/triangle.js";

 

위와 같이 적으면 브라우저에 오류가 발생하므로 import가 고유하도록 이름을 변경해줘야 합니다.

import { create, createReportList } from "./modules/canvas.js";

import {
  name as squareName,
  draw as drawSquare,
  reportArea as reportSquareArea,
  reportPerimeter as reportSquarePerimeter,
} from "./modules/square.js";

import {
  name as circleName,
  draw as drawCircle,
  reportArea as reportCircleArea,
  reportPerimeter as reportCirclePerimeter,
} from "./modules/triangle.js";

import {
  name as triangleName,
  draw as drawTriangle,
  reportArea as reportTriangleArea,
  reportPerimeter as reportTrianglePerimeter,
} from "./modules/circle.js";

 

다음과 같이 import하는 파일 대신 모듈 파일에서 문제를 해결할 수 있습니다.

// in square.js
export {
  name as squareName,
  draw as drawSquare,
  reportArea as reportSquareArea,
  reportPerimeter as reportSquarePerimeter,
};
// in main.js
import {
  squareName,
  drawSquare,
  reportSquareArea,
  reportSquarePerimeter,
} from "./modules/square.js";

6. Creating a module object 

위 방법을 정상적으로 작동하지만, 다소 지저분하고 길어질 수 있어, 보다 나은 해결책은 모듈의 기능을 모듈 객체 내부로 가져오는 것입니다.

import * as Canvas from "./modules/canvas.js";
import * as Square from "./modules/square.js";
import * as Circle from "./modules/triangle.js";
import * as Triangle from "./modules/circle.js";

 

각각의 경우, 지정 객체 이름 아래에 있는 모듈의 import에 접근할 수 있습니다.

let triangle1 = drawTriangle(myCanvas.ctx, 100, 75, 190, "yellow");
Triangle.reportArea(triangle1.length, reportList);
Triangle.reportPerimeter(triangle1.length, reportList);

 


7.Modules and classes

이전에 암시 했듯 class를 export하거나 import 할 수 있습니다. 코드의 충돌을 피하기 위한 또 다른 옵션으로, 모듈 코드가 이미 객체 지향 스타일로 작성된 경우에 특히 유용합니다.

class Square {
  constructor(ctx, listId, length, x, y, color) {
    this.ctx = ctx;
    this.listId = listId;
    this.length = length;
    this.x = x;
    this.y = y;
    this.color = color;
    this.name = "square";
  }

  draw() {
    this.ctx.fillStyle = this.color;
    this.ctx.fillRect(this.x, this.y, this.length, this.length);
  }

  reportArea() {
    let listItem = document.createElement("li");
    listItem.textContent = `${this.name} area is ${
      this.length * this.length
    }px squared.`;

    let list = document.getElementById(this.listId);
    list.appendChild(listItem);
  }

  reportPerimeter() {
    let listItem = document.createElement("li");
    listItem.textContent = `${this.name} perimeter is ${this.length * 4}px.`;

    let list = document.getElementById(this.listId);
    list.appendChild(listItem);
  }
}

export { Square };

참고 자료

 

JavaScript modules - JavaScript | MDN

이 가이드는 JavaScript 모듈 구문을 시작하는데 필요한 모든 것을 제공합니다.

developer.mozilla.org

 

 

GitHub - javascript-only/javascript-bloging

Contribute to javascript-only/javascript-bloging development by creating an account on GitHub.

github.com

 

 

LIST