모듈 패턴을 이용한 캡슐화

모듈 패턴은 즉시 실행 함수(IIFE)를 사용하여 구현할 수 있으며, 이를 통해 프라이빗 멤버와 퍼블릭 멤버를 분리할 수 있다.

counter 변수가 계속 유지될 수 있는 이유는 자바스크립트의 클로저(closure) 때문임
클로저는 함수가 선언될 때의 렉시컬 환경을 기억하는 기능하다.

이를 통해 함수가 선언된 환경 외부에서 호출되어도, 그 함수가 선언될 때의 환경에 접근할 수 있음

var CounterModule = (function () {
    var counter = 0;

    //퍼블랙 api 반환
    return {
        incrementCounter: function () {
            return ++counter;
        },

        resetCounter: function () {
            console.log("리셋");
            counter = 0;
            return counter;
        },

        getCounter: function () {
            return counter;
        },
    };
})(); //즉시 실행 함수

//클로저를 이용해서 counter 값을 유지하면서 증가시킬 수 있음
console.log(CounterModule.incrementCounter());  //1
console.log(CounterModule.incrementCounter());  //2

console.log(CounterModule.resetCounter());      //0
console.log(CounterModule.getCounter());        //0

객체 리터럴을 이용한 캡슐화

객체 리터럴을 사용하여 메소드와 프라퍼티를 캡슐화할 수 있음

이 방법은 모듈 패턴보다 간단하지만, 모든 멤버가 공개되어 있어 프라이빗 멤버를 만들 수 없다.

var Person = {
    name: "kiki",
    age: 10,
    greet: function () {
        console.log("hello~~, my name is", this.name, "my age", this.age);
    },
};

Person.greet();     //hello~~, my name is kiki my age 10

Revealing Module Pattern

모듈 패턴의 변형으로, 함수와 변수를 프라이빗으로 유지하면서 공개하고자 하는 것만 반환

모듈 내부에서 사용하는 변수와 함수 중에서 외부에 공개하고 싶은 것들만을 선택적으로 공개하는 방식

var CounterModule = (function () {
    //프라이빗한 멤버 변수
    var data = {
        counter: 0,
    };

    function add(n) {
        data.counter += n;
    }

    function reset() {
        data.counter = 0;
    }

    function getValue() {
        return data.counter;
    }

    //공개 api
    return {
        add: add,
        reset: reset,
        getValue,
    };
})();

CounterModule.add(5);
CounterModule.add(3);
console.log(CounterModule.getValue()); //8

CounterModule.reset();
console.log(CounterModule.getValue()); //0

모듈 패턴 vs. Revealing Module Pattern

모듈 패턴에서는 객체를 반환할 때 객체 리터럴을 직접 정의하여 반환

이 객체 리터럴에는 모듈의 공개 API를 구성하는 메소드와 변수가 포함

공개 API 외에 모듈 내부에서만 사용하는 프라이빗 변수와 함수는 이 객체 리터럴에 포함되지 않아 외부에서 접근할 수 없다.

var myModule = (function() {
  var privateVar = 'I am private';
  function privateFunction() {
    console.log(privateVar);
  }

  return {
    publicMethod: function() {
      privateFunction();
    }
  };
})();

Revealing Module Pattern에서는 모듈 내부의 모든 변수와 함수를 처음부터 정의하고, 그 중에서 공개하고 싶은 것들만을 객체 리터럴을 통해 반환하여 공개

var myRevealingModule = (function() {
  var privateVar = 'I am private';
  function privateFunction() {
    console.log(privateVar);
  }

  function publicMethod() {
    privateFunction();
  }

  // 공개 API
  return {
    revealMethod: publicMethod
  };
})();

Revealing Module Pattern 예시 1

var ToDoList = (function () {
  // 프라이빗 멤버: 할 일 목록
  let tasks = [];

  // 할 일 추가
  function addTask(task) {
    tasks.push(task);
    console.log(`Task "${task}" added.`);
  }

  // 할 일 삭제
  function removeTask(task) {
    tasks = tasks.filter(t => t !== task);
    console.log(`Task "${task}" removed.`);
  }

  // 할 일 목록 조회
  function listTasks() {
    console.log("Tasks:");
    tasks.forEach(task => console.log(`- ${task}`));
  }

  // 공개 메소드
  return {
    add: addTask,
    remove: removeTask,
    list: listTasks
  };
})();

// 사용 예
ToDoList.add("Learn JavaScript");
ToDoList.add("Write a blog post");
ToDoList.list(); // Lists all tasks
ToDoList.remove("Learn JavaScript");
ToDoList.list(); // Lists remaining tasks

Revealing Module Pattern 예시 2

var ConfigManager = (function () {
  // 프라이빗 멤버: 설정 값
  let settings = {
    theme: "dark",
    language: "en"
  };

  // 설정 값 가져오기
  function getSetting(key) {
    return settings[key];
  }

  // 설정 값 업데이트
  function setSetting(key, value) {
    if (settings.hasOwnProperty(key)) {
      settings[key] = value;
      console.log(`Setting "${key}" updated to "${value}".`);
    } else {
      console.log(`Setting "${key}" not found.`);
    }
  }

  // 공개 메소드
  return {
    get: getSetting,
    set: setSetting
  };
})();

// 사용 예
console.log(ConfigManager.get("theme")); // "dark"
ConfigManager.set("theme", "light");
console.log(ConfigManager.get("theme")); // "light"

Revealing Module Pattern 예시 3

var UserManager = (function () {
    let users = [
        { id: 1, name: "John Doe", email: "john@example.com" },
        { id: 2, name: "Jane Doe", email: "jane@example.com" },
    ];

    // 사용자 조회
    function findUserId(id, callback) {
        setTimeout(() => {
            const user = users.find((user) => user.id === id);
            if (user) {
                callback(null, user);
            } else {
                callback(new Error("유저 없음"));
            }
        }, 1000);
    }

    //사용자 업데이트
    function updateUser(id, updateData, callback) {
        setTimeout(() => {
            let userIndex = users.findIndex((user) => user.id === id);
            if (userIndex !== -1) {
                users[userIndex] = { ...users[userIndex], ...updateData };
                callback(null, users[userIndex]);
            } else {
                callback(new Error("유저 없음"));
            }
        });
    }

    return {
        findUserById: findUserId,
        updateUser: updateUser,
    };
})();

UserManager.findUserById(1, (err, user) => {
    if (err) {
        console.log(err.message);
    } else {
        console.log("유저 찾음:", user);

        //사용자 업데이트
        UserManager.updateUser(
            user.id,
            { email: "test123@naver.com" },
            (err, updatedUser) => {
                if (err) {
                    console.log(err.message);
                } else {
                    console.log("유저 업데이트", updatedUser);
                }
            }
        );
    }
});
자바스크립트 캡슐화 및 모듈 패턴 예시