본문 바로가기
Study/JavaScript

You don't know JS yet(2)

by dailycoding777 2025. 2. 6.

2.1 파일은 프로그램입니다.

JS에서는 파일 각각이 별도의 프로그램이다.

독립적은 .js 파일 여러개를 하나의 프로그램으로서 작동시키는 유일한 방법은

전역스코프를 사용해 파일 간 상태를 공유하고 공통으로 사용하는 기능을 접근할 수 있도록 만드는 방법 뿐이다.

전역 스코프 (Global Scope)

  • 코드 어디서든 접근 가능한 변수 및 함수가 정의되는 범위.
  • var는 window(브라우저)나 global(Node.js)에 등록되지만, let과 const는 해당 블록 스코프

JS는 각 모듈을 별도로 처리한다.

파일 하나를 제3의 작고 독립적은 프로그램(파일)과 협력해 전체를 작동시키는 고유한 작은 프로그램이다.


2.2 값

JS에서 값은 크게 원시타입, 객체타입으로 분류 된다.

  1. 원시 타입 (Primitive Type)
    • 값 자체를 저장하며, 변경 불가능 (Immutable).
    • Number, String, Boolean, Null, Undefined, Symbol, BigInt 포함.
  2. 객체 타입 (Object Type)
    • 값이 참조로 저장되며, 변경 가능 (Mutable).
    • Object, Array, Function, Date, RegExp 등 포함.

문자열 보간(String Interpolation)

문자열 안에 변수를 삽입 하는 방식

let firstName = "카일";
console.log(`제 이름은 ${firstName}입니다.`);

?? 이거 JS에선 템플릿 리터럴 아닌가?

차이점은 이렇다.

문자열 보간(String Interpolation) 👉 이론적인 개념

템플릿 리터럴(Template Literal) 👉 구현 방법 (JavaScript 문법)

보간이 필요 없는 경우 따옴표,큰따옴표 한종류만 쓰는 게 좋다.

리터럴이란? (프로그래밍에서 직접 값 자체를 표현해놓은 것)

  • 변수를 쓰지 않고 값 자체를 직접 적은 것
  • 변수 없이 그냥 적혀있는 숫자,문자열,불리언 값 전부 리터럴
  • "hello", 42, true, [1,2,3], { name: "카일" } → 전부 리터럴!

2.2.1 배열과 객체

JS에는 원시 타입 말고 객체 타입도 존재한다.

배열은 객체 내 데이터에 숫자 인덱스가 매겨진다.

var names = ["카일","보라","지수","현"]

names.length; // 4

names[0] // 카일

names[1] // 보라

배열

  • 배열에는 원시타입, 객체타입 상관 없이 모든 타입의 값이 들어갈 수 있다.
  • 배열안에 배열넣기 가능
  • 함수= 값 ⇒ 배열이나 객체의 값에 함수 역시 할당 가능

객체

  • 배열보다 좀 더 일반적인 데이터 타입
  • 정렬되지 않은 키-값 쌍을 모아놓은 컬렉션
  • key(property)를 사용해 인덱스에 접근
var me = {
first : "카일",
last : "심슨" ,
age : 39,
specialties : ["JS", "탁구"]
}

console.log(`제 이름은 ${me.first}입니다.`)

2.2.2 값의 타입

typeof 연산자를 사용해 원시타입 값과 객체타입 값을 구분한다.

console.log(typeof "Hello");     // "string"
console.log(typeof 42);          // "number"
console.log(typeof true);        // "boolean"
console.log(typeof undefined);   // "undefined"
console.log(typeof null);        // ❗"object" (JS의 오래된 버그)
console.log(typeof Symbol("id"));// "symbol"
  • type of null은 null이 아닌 object를 반환한다.
  • 함수는 function을 반환
  • array는 object를 반환

강제 타입 변환

  • 의미 : 값의 타입을 변경하는 것
  • 예시 : 문자열→ 숫자

2.3 변수 선언과 사용

변수 : 값을 담는 상자

변수를 사용하려면 변수 선언(생성)이 선언 되어야 한다.

키워드 재할당 가능? 재선언 가능? 스코프 호이스팅

var ✅ 가능 ✅ 가능 함수 스코프 호이스팅되지만 초기값 undefined
let ✅ 가능 ❌ 불가능 블록 스코프 호이스팅되지만 초기화 안 됨 (TDZ)
const ❌ 불가능 ❌ 불가능 블록 스코프 호이스팅되지만 초기화 안 됨 (TDZ)

🔥 결론 (언제 뭐 써야 함?)

  • var → ❌ (let, const가 더 안전함) 함수 스코프 + window (브라우저) or global (Node.js) 등록됨

let과 const → 블록 스코프(Block Scope)임. window 또는 global에 등록되지 않음

  • let → ✅ 값이 변경될 수 있으면 사용 (ex. 반복문, 상태 관리)
  • const → ✅ 항상 쓰기! (변경할 필요 없는 값은 const 사용)

var는 선언한 변수가 접근 범위가 함수 스코프다.

let 선언한 변수는 변수 접근범위가 블록이다.

그럼 JS는 이 변수를 3번 읽는 것인가?

→ No. 실제론 2번만 읽는다.

  1. 실행 컨텍스트 생성 → 변수 & 함수 등록(호이스팅)
  2. 코드 실행 → 초기화 및 값 할당
  • 3단계(선언 → 초기화 → 할당)로 보이는 이유
    • let과 const는 선언만 먼저 되고, TDZ를 거친 후 나중에 초기화 & 할당되기 때문!
  • TDZ(Temporal Dead Zone)은 "일시적 사각지대" let과 const로 선언된 변수가 호이스팅되긴 하지만, 선언 전에 접근할 수 없는 상태를 의미

2.4 함수

프로시저 : 한번 이상 호출할 수 있고 입력값이 있을 수 있으며 하나 이상의 출력값을 반환하는 구문의 모음

1. 함수 선언문 (Function Declaration)

📌 특징:

  • function 키워드로 독립적으로 선언하는 방식.
  • 호이스팅(hoisting) 됨 → 선언 전에 호출해도 실행됨.
hello();  // ✅ 가능 (호이스팅됨)

function hello() {
  console.log("Hello, 싸장님!");
}

hello();  // "Hello, 싸장님!"

✔️ 정리:

  • 함수 선언이 코드 실행 전에 등록되기 때문에, 선언 이전에도 호출 가능!
  • 스코프 최상단으로 끌어올려짐(호이스팅).

✅ 2. 함수 표현식 (Function Expression)

📌 특징:

  • 함수를 변수에 할당하는 방식.
  • 호이스팅 안 됨 → 선언 전에 호출 ❌
greet(); // ❌ TypeError: greet is not a function

const greet = function() {
  console.log("안녕하세요!");
};

greet(); // "안녕하세요!"

✔️ 정리:

  • 변수에 할당되므로 선언 전에 호출 불가.
  • greet는 함수 선언이 아니라 변수에 저장된 값이므로, 초기화 전엔 undefined 상태.
  • 즉, 호이스팅되지 않음!

✅ 3. 두 개의 차이점 정리

구분 함수 선언문 함수 표현식

호이스팅 ✅ O (선언 전에 호출 가능) ❌ X (선언 전에 호출하면 에러)
선언 방식 독립적으로 선언 변수에 할당
코드 가독성 함수 이름이 바로 보임 변수명으로 접근
사용 예시 일반적인 함수 정의 콜백 함수, 클로저

함수 표현식으로 선언한 함수가 함수 선언으로 선언한 함수와 다른 점은 함수와 함수 식별자가 코드가 실행되기 전까지는 관계를 맺지 않는다는 점

⇒ 함수표현식으로 선언된 함수는 코드실행 전까지 변수와 연결 안된다.

함수는 오로지 1개의 값만 반환할 수 있다.

여러 개를 반환하고 싶다면 객체 or 배열로 감싸 반환하면 된다.


2.5 비교

2.5.1 같음에 고찰 (같다는 건 무엇인가?)

JS에서 ‘서로 다르지 않음’ 뿐만 아니라 ‘아주 유사’ , ‘교환 가능’한지와 같이 좀 더 넓은 관점에서 비교하는 때도 있다.

JS에서 비교가 일어날 때는 일치 연산자에서처럼 타입도 고려된다.

다른 비교 연산에서는 타입 강제 변환을 허용한다. (42 == ‘42’ true)

✅ 1. ===(일치 비교)는 원시 타입일 때 "값"을 비교

console.log(42 === 42);      // ✅ true (숫자 값이 동일)
console.log("hello" === "hello");  // ✅ true (문자열 값이 동일)
  • 원시 타입(숫자, 문자열, 불리언 등)은 값을 직접 비교해서 같으면 true.

✅ 2. 객체 비교에서는 "참조(Reference)"를 비교

console.log([1, 2, 3] === [1, 2, 3]);  // ❌ false
console.log({ a: 42 } === { a: 42 });  // ❌ false
  • 객체는 메모리 주소(참조 값)를 비교하므로, 구조나 내용이 같아도 다르면 false
  • 새로운 { a: 42 } 객체는 매번 새로운 메모리 주소를 가지므로 비교 결과가 false.
  • ++ 독자성 일치라는 건 - 구조가 아나라 참조(메모리 주소)를 비교한다는 말임.

✅ 3. 같은 참조를 가진 객체끼리는 true

var x = [1, 2, 3];
var y = x;  // x를 y에 참조 복사

console.log(y === x);  // ✅ true (같은 메모리 주소)
console.log(x === [1, 2, 3]);  // ❌ false (새로운 배열)
  • y = x; → 배열 참조를 복사해서 y와 x는 같은 주소를 가짐.
  • 그러나 [1, 2, 3] 같은 배열을 새로 만들면, 새로운 메모리 주소를 가지므로 false.

✅ 4. JS는 객체의 "구조적 일치(structural equality)"를 비교하지 않음

  • JS는 기본적으로 객체의 구조가 같은지 직접 비교하는 기능이 없음.
  • "이 객체와 저 객체가 같은지" → 참조가 같은지(reference identity)만 확인 가능.
  • 객체의 내용을 비교하려면 직접 코드를 짜야 함.

🔹 예제: 객체 구조가 같은지 비교하려면?

function isEqual(obj1, obj2) {
  return JSON.stringify(obj1) === JSON.stringify(obj2);
}

console.log(isEqual({ a: 42 }, { a: 42 }));  // ✅ true
console.log(isEqual({ a: 42 }, { a: 43 }));  // ❌ false

❗ 하지만 이 방법도 순서가 다를 경우 오작동할 수 있음.


✅ 5. 함수끼리 비교는 더 어렵다

const f1 = function() { return 42; };
const f2 = function() { return 42; };

console.log(f1 === f2);  // ❌ false (각각 다른 참조)
  • 함수도 객체라서 새로운 함수는 새로운 참조를 가짐.
  • 심지어 같은 코드라도 다른 메모리 주소를 가지므로 비교하면 false.

===는 객체의 "내용"이 아닌 "참조 값"을 비교한다

  1. 원시 타입(number, string, boolean)은 값 자체를 비교.
  2. 객체({}, [], function)는 메모리 주소(참조 값)를 비교.
  3. 객체 구조가 같은지 비교하는 기능은 JS에 기본 제공되지 않음.
  4. 객체 내용 비교는 JSON 변환 같은 추가 코드가 필요함.
  5. 함수 비교는 더 어려움 → 같은 코드라도 참조가 다르면 false.

2.5.2 강제 변환

타입의 값이 다른 타입의 값으로 변하는 걸 의미한다.

피연산자가 타입이 같으면 완전히 동일하게 작동

1 === 1 
1 == 1

그런데 == 연산자는 비교 이전에 강제로 타입을 맞추는 작업을 수행한다는 점에서 ===와 차이가 있다.

  • 공통점 : 두 연산자 모두 같은 값 비교
  • 다른점 : ==연산자는 타입 강제변환 먼저 실행

그래서 == 연산자는 느슨한 동등 비교연산자가 아닌 강제 변환 동등 비교 연산자라고 설명하는게 적합하다.

42 == "42"; // true
1 == true; // true

다른 연산자는? (<,>,≤,≥)

  • 타입이 같은 경우 : ==와 마찬가지로 피연산자들의 타입이 같으면 === 처럼 작동.
  • 타입이 다른 경우 : 타입 강제 변환이 먼저 일어남(대개 숫자형으로 변환)

1. 타입이 같은 경우 (==처럼 작동)

→ 타입이 같으면 ===처럼 값 비교만 수행!

console.log(10 > 5);   // ✅ true
console.log(3 <= 3);   // ✅ true
console.log("apple" < "banana");  // ✅ true (사전순 비교)
console.log(true > false);  // ✅ true (true는 1, false는 0으로 변환)
  • 숫자끼리는 그대로 크기 비교.
  • 문자열끼리는 알파벳 순서(유니코드 순서) 로 비교됨.
    • "apple" vs "banana" → "a"가 "b"보다 앞에 있어서 true.
  • 불리언 값도 숫자로 변환 (true → 1, false → 0).

2. 타입이 다른 경우 → "숫자형으로 변환" 후 비교

→ == 처럼 타입 강제 변환이 먼저 일어나고, 숫자로 변환한 뒤 비교!

console.log("10" > 2);    // ✅ true ("10" → 숫자 10)
console.log("5" < "12");  // ❌ false (문자열 비교 → "5"가 "1"보다 뒤에 있음)
console.log("5" < 12);    // ✅ true ("5" → 숫자 5)
console.log(true > 0);    // ✅ true (true → 1, false → 0)
console.log(false >= 0);  // ✅ true (false → 0)
console.log(null >= 0);   // ✅ true (null → 0 변환)
console.log(null > 0);    // ❌ false (null → 0 변환 → 0은 0보다 크지 않음)
console.log(undefined > 0); // ❌ false (undefined는 변환 시 NaN → 비교 불가)
console.log(undefined < 0); // ❌ false (undefined → NaN, 비교 불가능)
console.log(undefined == null); // ✅ true (둘 다 "없음"을 의미)

📌 null과 undefined의 비교 주의점!

  • null → 숫자 비교(>=, <=)에서는 0으로 변환되지만, == 비교에서는 undefined와 같음.
  • undefined → 숫자로 변환 시 NaN이 되므로 모든 크기 비교(>, <, >=, <=)에서 false.

연산자 유형 동작 방식

같은 타입 ===처럼 값만 비교
다른 타입 숫자로 변환 후 비교 ("10" > 2 → "10"이 숫자 10으로 변환)
문자열 비교 알파벳 순서(유니코드 순)로 비교 ("5" < "12" → false)
불리언 값 true → 1, false → 0 변환 (true > false → 1 > 0)
null vs 숫자 null → 0 변환 (null >= 0 → true, null > 0 → false)
undefined vs 숫자 undefined는 NaN이 되어 모든 비교에서 false

비교 연산자(>, <, >=, <=)는 ==처럼 타입 강제 변환을 수행하며, 대부분 숫자로 변환한 후 비교한다!


2.7 코드 구조화 패턴

많은 프로그램이 클래스, 모듈 두가지 패턴을 모두 사용한다.

한 패턴만 고수하는 프로그램도 있고 두패턴 모두 사용하지 않는 프로그램도 있다.

상호 배타적인 패턴이 아니라는 것도 알자.

2.6.1 Class

데이터 + 데이터를 조작하는 함수(메서드)를 묶어서 하나의 객체를 만드는 설계도

Class = 템플릿(객체 + 객체의함수)

// 1. class 이름 { 생성자,함수}
// 2. 변수선언 = new 생성사
// 3. 콘솔로그 변수.함수
class Counter {
  constructor() {
    this.count = 0;
  }
  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

const calc = new Calculator(); 
console.log(calc.add(5, 3)); // ✅ 8
console.log(calc.multiply(5, 3)); // ✅ 15

생성자(constructor)가 없어도 되고, 함수가 없어도 된다.

생성자가 없는 클래스 (constructor 없음)

💡 constructor를 정의하지 않으면, 자동으로 빈 생성자가 추가됨!

class Person {
  // 생성자 없음
}

const p = new Person();  // ✅ 정상 동작
console.log(p);  // ✅ Person {} (빈 객체)

✔ 생성자가 없어도 기본적으로 객체는 생성됨.

✔ 다만, 속성을 초기화할 방법이 없음!

✔ new Person()을 실행하면, 빈 객체가 만들어짐.

📌 결론:

  • 생성자가 없으면 자동으로 빈 constructor()가 추가됨.
  • 속성을 초기화할 필요가 없거나, 후에 추가할 경우라면 생략 가능!

메서드가 없는 클래스 (함수 없음)

class Animal {
  constructor(type) {
    this.type = type;
  }
}

const a = new Animal("Dog");
console.log(a.type);  // ✅ "Dog"

✔ constructor는 있지만 메서드가 없음.

✔ 객체를 만들고 속성만 저장할 용도라면 메서드 없이도 가능.

✔ 하지만 동작(메서드)을 추가하지 않으면 단순한 데이터 저장용 클래스가 됨.

📌 결론:

  • 메서드 없이도 동작하지만, 클래스를 사용하는 의미가 줄어듦.
  • 보통 데이터 저장용(모델 클래스) 으로 사용할 때 메서드 없이 만들기도 함.

생성자도 없고 메서드도 없는 클래스

class EmptyClass {}

const obj = new EmptyClass();
console.log(obj);  // ✅ EmptyClass {}

완전 빈 클래스도 가능.

✔ 하지만 이렇게 만들면 객체를 생성할 의미가 거의 없음.

속성도 없고, 동작(메서드)도 없으므로 사실상 "의미 없는 클래스"가 됨.

클래스 상태 가능 여부 설명

✅ 생성자 없음 가능 자동으로 빈 constructor()가 추가됨
✅ 메서드 없음 가능 속성만 저장하는 용도로 사용 가능
✅ 생성자+메서드 없음 가능 하지만 사실상 의미 없음

확장

클래스 확장은 기존 클래스를 재사용 하면서 새로운 기능을 추가하는 방법이다.

1. 기본

class Animal {
  constructor(name) {
    this.name = name;
  }
  makeSound() {
    console.log("...");
  }
}

// Animal 클래스를 상속받아서 Dog 클래스를 만듦.
class Dog extends Animal { 
  bark() {
    console.log("멍멍!");
  }
}
const myDog = new Dog("멍멍이");
console.log(myDog.name); // 멍멍이

myDog.makeSound(); // Animal의 메서드
myDog.bark(); // 멍멍! // Dog에서 추가한 메서드

2. super 키워드로 부모 클래스 생성자 호출

super란?

  • 부모 클래스의 constructor(생성자) 호출
  • 부모 클래스의 메서드 실행
  • ⇒ extends 없이 super를 쓰면 에러가 난다.
class Animal {
  constructor(name) {
    this.name = name;
  }
}

class Cat extends Animal {
  constructor(name, color) {
    super(name);  // ✅ 부모 클래스(Animal)의 생성자 호출
    this.color = color;
  }

  meow() {
    console.log("Meow! 🐱");
  }
}

const myCat = new Cat("Whiskers", "Gray");
console.log(myCat.name);  // ✅ "Whiskers" (Animal에서 상속됨)
console.log(myCat.color); // ✅ "Gray" (Cat에서 추가된 속성)
myCat.meow();  // ✅ "Meow! 🐱"

3. 부모 매서드 오버라이딩(재정의)

부모 클래스의 매서드를 자식 클래스에서 다른 동작으로 변경 가능하다.

class Animal {
  makeSound() {
    console.log("Some generic animal sound...");
  }
}

class Dog extends Animal {
  makeSound() {  // ✅ 부모 메서드 오버라이딩(재정의)
    console.log("Bark! 🐶");
  }
}

const myDog = new Dog();
myDog.makeSound();  // ✅ "Bark! 🐶" (부모 메서드가 덮어씌워짐)

4. 부모 메서드 + 새로운 기능 추가 (super.method)

class Animal {
  makeSound() {
    console.log("Some generic animal sound...");
  }
}

class Bird extends Animal {
  makeSound() {
    super.makeSound();  // ✅ 부모의 메서드 먼저 실행
    console.log("Tweet! 🐦");
  }
}

const myBird = new Bird();
myBird.makeSound();
/*
✅ 출력:
Some generic animal sound...
Tweet! 🐦
*/

2.6.2 모듈

코드의 일부를 독립적인 파일로 분리하여 관리하는 개념이다.

ES6에서 추가 (import , export)

모듈의 장점

  • 코드의 재사용 가능 → 한번 만든 모듈을 여러 곳에서 가져다 쓸 수 있음.
  • 코드 유지보수 용이 → 기능별로 파일을 분리하면 관리가 쉬워짐
  • 네임 스페이스 관리 → 전역 변수를 피하고 모듈 내에서만 유지 가능

모듈을 export로 내보내기

export function add(a, b) {
  return a + b;
}

export const PI = 3.14159;

모듈 import 가져오기

// main.js
import { add, PI } from './math.js';

console.log(add(2, 3));  // ✅ 5
console.log(PI);         // ✅ 3.14159

클래스 모듈이란?

ES6 이전에 사용되던 모듈 시스템을 의미한다.

JS 자체적으로 모듈 시스템이 없었기 때문에 , 함수나 즉시 실행 함수(IIFE)를 활용해서 모듈을 구현 했다.

var MathModule = {
  add: function(a,b) {
    return a + b;
  },
  PI: 3.14159
};

console.log(MathModule.add(2, 3));  // ✅ 5
console.log(MathModule.PI);         // ✅ 3.14159

객체(MathModule)를 만들어서 전역에서 공유하는 방식.

단점: 모든 모듈이 전역에 노출됨 → 이름 충돌 가능성 있음!

즉시 실행 함수(IIFE, Immediately Invoked Function Expression)

var MathModule = (function() {
  var PI = 3.14159; // private 변수 (외부에서 접근 불가)

  function add(a, b) {
    return a + b;
  }

  return {
    add: add,
    getPI: function() { return PI; } // 접근 가능하게 메서드 추가
  };
})();

console.log(MathModule.add(2, 3));  // ✅ 5
console.log(MathModule.getPI());    // ✅ 3.14159
console.log(MathModule.PI);         // ❌ undefined (private 변수)

즉시 실행 함수(IIFE)를 사용하여 변수 보호 가능!

✔ PI는 외부에서 직접 접근할 수 없음 → 캡슐화 가능! → ?? var인데 왜 그러지? ⇒

 

  1. 자체적 특성 var는 함수 스코프를 존중하기 때문에 함수 안에서 정의하면 외부에서 그 var 변수를 참조할 수 없다.
  2. 즉시 실행함수(IIFE) 내부의 var는 클로저로 보호 됨 var가 선언 된 곳은 함수 스코프가 만들어져서 다른 곳에선 참조할 수 없다.

모듈 팩토리

1. 기본적인 모듈 팩토리 패턴

✅ 함수가 새로운 객체(모듈)를 반환하는 구조

function createUser(name, age) {
  return {
    name: name,
    age: age,
    greet: function() {
      console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
    }
  };
}

const user1 = createUser("Minho", 28);
const user2 = createUser("Alice", 25);

user1.greet(); // ✅ "Hello, my name is Minho and I am 28 years old."
user2.greet(); // ✅ "Hello, my name is Alice and I am 25 years old."

✔ createUser() 함수가 객체를 반환하는 모듈 팩토리 역할을 함.

✔ 함수 호출할 때마다 새로운 객체가 생성됨.


2. 모듈 팩토리를 활용한 클로저(정보 은닉)

function createCounter() {
  let count = 0;  // private 변수

  return {
    increment: function() {
      count++;
      console.log(`Count: ${count}`);
    },
    decrement: function() {
      count--;
      console.log(`Count: ${count}`);
    },
    getCount: function() {
      return count;
    }
  };
}

const counter1 = createCounter();
const counter2 = createCounter();

counter1.increment(); // ✅ Count: 1
counter1.increment(); // ✅ Count: 2
console.log(counter1.getCount()); // ✅ 2

counter2.increment(); // ✅ Count: 1 (새로운 모듈이므로 값이 독립적)
console.log(counter2.getCount()); // ✅ 1

✔ count는 외부에서 직접 접근할 수 없는 private 변수!

✔ createCounter()를 호출할 때마다 독립적인 상태를 가진 새로운 객체가 생성됨.

✔ 즉, 각 counter는 서로 다른 count 값을 유지할 수 있음.


3. 즉시 실행 함수(IIFE)와 함께 사용하기

const MathModule = (function() {
  function createMathModule() {
    return {
      add: (a, b) => a + b,
      multiply: (a, b) => a * b
    };
  }

  return createMathModule();
})();

console.log(MathModule.add(3, 5));  // ✅ 8
console.log(MathModule.multiply(3, 5));  // ✅ 15

✔ createMathModule() 함수가 모듈을 생성하는 역할을 함.

✔ 즉시 실행 함수(IIFE)를 사용하면 단 한 번만 실행되어 고정된 모듈을 제공할 수 있음.


4. 모듈 팩토리를 활용한 require() 방식 (CommonJS)

Node.js의 CommonJS 모듈 시스템에서도 모듈 팩토리를 사용할 수 있음.

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

// main.js
const createMathModule = require('./math');
const MathModule = createMathModule();

console.log(MathModule.add(4, 6));  // ✅ 10
console.log(MathModule.multiply(4, 6));  // ✅ 24

✔ require()를 호출할 때마다 새로운 모듈이 생성됨.

✔ 즉, 각 호출마다 새로운 상태를 가지는 모듈을 만들 수 있음.


🚀 모듈 팩토리 정리

개념 설명 예제

모듈 팩토리 함수를 사용하여 동적으로 모듈을 생성 function createUser() { return { ... }; }
클로저 활용 내부 상태(변수)를 보호하면서 객체 생성 function createCounter() { let count = 0; return { increment() { count++; } }; }
즉시 실행 함수(IIFE)와 결합 한 번만 실행되도록 설정 const Module = (function() { return { ... }; })();
Node.js CommonJS require()를 호출할 때마다 새로운 모듈 생성 module.exports = function() { return { ... }; };

📌 한 줄 요약:

"모듈 팩토리는 함수를 사용하여 새로운 모듈(객체)을 동적으로 생성하는 패턴이다!"

'Study > JavaScript' 카테고리의 다른 글

You don't know JS yet(1.5~1.8)  (0) 2025.01.23
You don't know JS (1.1~1.4)  (0) 2025.01.16