모듈이란

FAST FOX·2025년 8월 11일

About. JavaScript

목록 보기
9/10
post-thumbnail

1. 모듈이란?

모듈이란 기능 단위로 코드를 분리하고, 재사용과 유지보수를 쉽게 하기 위한 독립 단위이라고 한다.
사실 이런 설명은 함수와 같은 다른 코드들과 차이점이 무엇인지 궁금증이 생긴다.
좀 더 자세하게 설명하자면 전역 변수, 함수, 클래스 등 관련된 코드들을 하나의 파일(또는 독립된 단위)에 묶어
하나의 공통된 기능 또는 역할을 수행하도록 구성한 코드 단위를 모듈이라고 한다.

기존에 한 프로그램 코드 속에 모든 변수와 함수들을 무분별하게 작성해서 가독성과 유지보수가 불편하던 것을 보완하기 위해서 하나의 파일 또는 독립된 단위에 묶어서 관리하고 ES6 이후로는 import/export 키워드를 사용해서 간편하게 재사용할 수 있다는 장점이 있다.

2. 모듈화 방법

2-1. ES6 이전

객체 리터럴

var student = {
  records: [
    { id: 14, name: "카일", grade: 86 },
    { id: 73, name: "보라", grade: 87 },
  ],
  getName(studentId) {
    var student = this.records.find(
      student => student.id === studentId
    )
    return student.name;
  }
};

그냥 객체 하나를 전역에 선언한 상태로 records가 public → student.records로 외부에서 직접 접근/수정 가능하다.

student.records.push({ id: 99, name: "해킹", grade: 0 }); // 아무나 수정 가능

결론적으로 네임스페이스 역할은 하지만 데이터 은닉(캡슐화)은 불가능하기 때문에 “모듈화”라고 부르기엔 부족하다.

📌 네임스페이스
네임스페이스는 코드를 묶고 이름 충돌을 방지하는 구조이지, 데이터나 구현 세부사항을 숨기는 개념이 아니기 때문에 모듈의 캡슐화와는 차이가 있으면 ES6 이후 현재는 모듈이 네임스페이스의 역할까지 겸하고 있다.

IIFE 모듈 패턴(싱글톤)

ES6 모듈 문법이 없던 시절에 IIFE로 감싸서 private 스코프 생성하는 방법으로 많이 사용된 모듈 패턴이다.

var Student = (function defineStudent () {
  var records = [
    { id: 14, name: "카일", grade: 86 },
    { id: 73, name: "보라", grade: 87 },
  ]
  var publicAPI = { getName };
  return publicAPI;

  function getName(studentId) {
    var student = records.find(
      student => student.id === studentId
    )
    return student.name;
  }
})();

publicAPI를 반환해 외부에 노출할 것만 공개하기 때문에 records가 getName()으로만 간접 접근 가능하고 외부에서 직접 접근 불가능하다.
즉, 데이터 은닉(캡슐화)이 적용된 모듈화가 된다.

console.log(Student.records); // undefined (직접 접근 불가)

모듈 팩토리 함수

함수를 이용한 모듈화라는건 IIFE 모듈 패턴과 비슷하지만 모듈 팩토리 함수의 경우는 모듈 인스턴스를 호출할 때마다 새로운 모듈 인스턴스를 만들 수 있다는 특징이 있다.

function createCounterModule() {
  let count = 0;

  function increment() {
    count++;
    console.log(count);
  }

  function reset() {
    count = 0;
  }

  return {
    increment,
    reset
  };
}

const counter1 = createCounterModule();
const counter2 = createCounterModule();

counter1.increment(); // 1
counter1.increment(); // 2

counter2.increment(); // 1

IIFE와는 다르게 선언과 동시에 모듈 인스턴스가 생성되지는 않지만 호출을 할 때마다 각각의 인스턴스를 생성할 수 있게된다.

CommonJS

CommonJS는 Node.js에서 사용하는 모듈화 방식이다.
다른 모듈화와의 차이점은 파일 기반이기 때문에 모듈을 만들 때 별도의 파일을 정의해야 한다는 차이가 있다.

// math.js
module.exports.add = add;
function add(a, b) { return a + b; }

// app.js
const { add } = require('./math.js');
console.log(add(2, 3));

코드에서 보이는 것처럼 module.exports를 사용해서 공개하고자 하는 변수/함수를 정의하고 require()을 사용해서 참조하고자 하는 변수/함수를 정의한다.

만약 여러개를 동시에 내보내고 싶을 때 module.exports = {...내보낼 것}과 같이 정의하는 경우가 있지만 이는 모듈이 순환적으로 종속되는 경우 오류가 발생하는 등의 예기치 못한 상황이 발생하므로 Object.assign(module.exports, {...내보낼 것})와 같이 작성하는 것이 좋다.

2-2. ES6 이후

ES 모듈

현대 JS에서 표준으로 사용되는 ES Module(ESM)은 CommonJS와 비슷한 구성을 가지고 있다.
다른 점은 module.exports 대신에 export를, require() 대신에 import를 사용한다는 것이다.

// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }
export default function multiply(a, b) { return a * b; }

// app.js
import multiply, { PI, add } from './math.js';
console.log(PI, add(2, 3), multiply(2, 3));

export를 사용해서 모듈에서 외부에 공개할 변수, 함수, 클래스 지정하고 import는 다른 모듈에서 공개한 걸 가져온다.

이렇게 파일 하나가 곧 모듈이 되고 각 모듈(파일)은 자체적인 스코프를 가지기 때문에 변수 충돌 오류를 방지하고 데이터와 함수의 공개 범위를 지정할 수 있게 된다.

3. 네임스페이스, 캡슐화, 모듈

공부하다보니 네임스페이스, 캡슐화, 모듈이라는 개념이 나오는데 세가지 모두 "묶는다"라는 공통점만 알겠고 정확한 차이는 모르겠어서 좀 더 알아봤다.

3-1. 네임스페이스

네임스페이스의 목적은 단순히 전역 변수 충돌을 막기 위해 코드(변수, 함수 등)를 한 곳에 묶는 것이다.
때문에 데이터 은닉/접근 제한 기능은 없다.
사용 방법은 주로 아래 예시와 같이 객체 리터럴로 표현한다.

var MyApp = {
  foo() { ... },
  bar() { ... }
};

3-2. 캡슐화

캡슐화의 주요 목적은 은닉화와 접근 제한에 있다.
데이터(상태)를 숨기고, 외부에는 제한된 API만 공개해서 안전하게 조작하게 하는 것이 목적이다.
JS의 주요 개념인 클로저 역시 캡슐화를 위한 하나의 수단이고, 위에서 알아봤던 모듈 역시 캡슐화를 포함한 개념이다.
이외에도 클래스 private 필드 등으로 구현이 가능하다.

function createCounter() {
  let count = 0;  // 은닉된 상태 => createCounter.increment를 사용해서만 count를 조작할 수 있고, getCount()를 사용해서만 접근할 수 있다.
  return {
    increment() { count++; },
    getCount() { return count; }
  };
}

3-3. 모듈

모듈에 대한 기본 정의는 위에서 봤기 때문에 생략하고 네임스페이스, 캡슐화와의 차이점만 알아보자면 네임스페이스 + 캡슐화 = 모듈 이라고 볼 수 있다.
모듈은 단순히 기능 단위로 묶는 것 뿐만 아니라 캡슐화를 통해서 데이터와 함수의 은닉화가 가능하다는 점이다.

profile
준비하는 개발자

0개의 댓글