frontend/javascript

🟨 2-20. 자바스크립트 클래스(Class) 내부 구조 — 문법적 설탕 이상의 진짜 의미

mirabo01 2025. 11. 7. 08:54

1. 클래스란 무엇인가

클래스(Class)는 객체를 만들기 위한 청사진(템플릿) 이다.
다른 언어(Java, C++)에서는 원래부터 존재하던 개념이지만,
자바스크립트는 2015년(ES6) 이후에 문법적으로 추가되었다.

class User {
  constructor(name) {
    this.name = name;
  }

  sayHi() {
    console.log(`안녕, 나는 ${this.name}`);
  }
}

const kb = new User("기범");
kb.sayHi(); // 안녕, 나는 기범

✅ constructor는 객체가 생성될 때 자동 실행되는 생성자 함수
✅ 클래스 내부 메서드는 User.prototype에 저장된다
✅ 즉, 클래스는 “보기 좋은 프로토타입 문법”이다


2. 클래스와 프로토타입의 관계

위 코드는 내부적으로 다음과 동일하게 동작한다 👇

function User(name) {
  this.name = name;
}
User.prototype.sayHi = function () {
  console.log(`안녕, 나는 ${this.name}`);
};

즉, 클래스는 사실상 다음 구조를 자동으로 만들어준다.

구성요소 실제 역할

constructor 생성자 함수
메서드 prototype에 저장됨
new 호출 인스턴스 생성 + 프로토타입 연결

이 때문에 클래스 문법을 이해하려면 프로토타입 체인을 먼저 알아야 하는 것이다.


3. 클래스의 주요 특징

✅ 클래스는 호이스팅되지 않는다

const user = new User(); // ❌ ReferenceError
class User {}

클래스 선언은 TDZ(Temporal Dead Zone) 내에 있기 때문에
함수처럼 위에서 호출할 수 없다.


✅ strict mode가 자동 적용

클래스 내부의 모든 코드는 자동으로 'use strict' 모드에서 실행된다.
즉, this가 undefined로 처리되는 등 보다 안전한 실행 환경을 가진다.


4. 클래스의 상속 (extends)

클래스의 진정한 힘은 상속 구조에 있다.

class Animal {
  constructor(name) {
    this.name = name;
  }
  move() {
    console.log(`${this.name}이(가) 움직인다`);
  }
}

class Dog extends Animal {
  bark() {
    console.log(`${this.name}이(가) 멍멍 짖는다`);
  }
}

const choco = new Dog("초코");
choco.move(); // 초코이(가) 움직인다
choco.bark(); // 초코이(가) 멍멍 짖는다

✅ extends는 내부적으로 Dog.prototype.__proto__ = Animal.prototype
✅ 즉, 프로토타입 상속을 자동으로 구성해준다


5. super() — 부모 클래스 생성자 호출

super()는 상속 관계에서 부모 클래스의 생성자(constructor) 를 호출한다.

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

class Dog extends Animal {
  constructor(name, breed) {
    super(name); // 부모 생성자 호출
    this.breed = breed;
  }
}

const max = new Dog("맥스", "리트리버");
console.log(max.name, max.breed); // 맥스 리트리버

✅ super()는 this보다 먼저 호출해야 한다
✅ 부모 클래스의 속성을 자식이 안전하게 상속받는 구조


6. 메서드 오버라이딩

자식 클래스에서 부모의 메서드를 재정의할 수 있다.

class Animal {
  speak() {
    console.log("소리를 낸다");
  }
}

class Cat extends Animal {
  speak() {
    super.speak();
    console.log("야옹!");
  }
}

const kitty = new Cat();
kitty.speak();
// 소리를 낸다
// 야옹!

✅ super.speak()로 부모 메서드 실행 후 확장 가능
✅ “확장 가능한 상속 구조” 구현 가능


7. 정적 메서드 (static)

클래스의 인스턴스가 아닌 클래스 자체에 직접 연결되는 메서드를 만들 수 있다.

class MathUtil {
  static add(a, b) {
    return a + b;
  }
}
console.log(MathUtil.add(5, 3)); // 8

✅ 인스턴스 없이 호출 가능
✅ 보통 유틸리티, 헬퍼, 팩토리 함수 등에 사용


8. 필드(Field) 선언과 초기화

ES2022 이후로, 클래스 내부에서 바로 속성을 정의할 수 있다.

class Counter {
  count = 0; // 클래스 필드
  increase() {
    this.count++;
    console.log(this.count);
  }
}

const c = new Counter();
c.increase(); // 1

✅ 생성자 없이도 필드 정의 가능
✅ React, Vue 등에서 클래스형 컴포넌트의 기본 구조로 자주 쓰임


9. private 필드 (#)

ES2022부터 클래스에는 진짜 비공개 변수(private field) 를 지원한다.

class BankAccount {
  #balance = 0;

  deposit(amount) {
    this.#balance += amount;
    console.log(`입금 완료: 현재 잔액은 ${this.#balance}원`);
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount();
account.deposit(10000);
console.log(account.getBalance()); // 10000
console.log(account.#balance); // ❌ SyntaxError

✅ #으로 선언된 변수는 외부 접근 불가
✅ 진정한 의미의 캡슐화(Encapsulation) 구현 가능


10. Getter / Setter

Getter와 Setter를 사용하면
속성 접근처럼 보이지만, 실제로는 함수 로직을 실행시킬 수 있다.

class Product {
  constructor(name, price) {
    this.name = name;
    this._price = price;
  }

  get price() {
    return this._price + "원";
  }

  set price(value) {
    if (value < 0) throw new Error("가격은 0 이상이어야 합니다.");
    this._price = value;
  }
}

const item = new Product("커피", 3000);
console.log(item.price); // 3000원
item.price = 5000;
console.log(item.price); // 5000원

✅ 내부 로직을 속성 접근처럼 처리할 수 있어 직관적
✅ 데이터 유효성 검사 및 포맷팅에 자주 사용


11. 클래스와 화살표 함수의 조합

클래스 내부에서 화살표 함수를 쓰면
자동으로 this가 인스턴스에 바인딩된다.

class Button {
  constructor(label) {
    this.label = label;
  }

  handleClick = () => {
    console.log(`${this.label} 버튼 클릭됨`);
  };
}

const btn = new Button("확인");
document.body.addEventListener("click", btn.handleClick);

✅ bind() 없이도 this가 유지
✅ React 클래스형 컴포넌트에서 매우 자주 사용되는 패턴


12. 클래스 vs 함수 생성자 비교

구분 클래스 생성자 함수

문법 class 키워드 function 키워드
상속 extends, super() Object.create(), call()
캡슐화 private field 지원 불가능
this 바인딩 명확함 함수 컨텍스트에 따라 다름
호이스팅 불가능 가능
코드 가독성 높음 낮음

13. 실무 예제 — 사용자 인증 시스템

class User {
  constructor(name, role = "guest") {
    this.name = name;
    this.role = role;
  }

  login() {
    console.log(`${this.name}님이 로그인했습니다.`);
  }
}

class Admin extends User {
  constructor(name) {
    super(name, "admin");
  }

  deleteUser(user) {
    console.log(`${user.name}님이 삭제되었습니다.`);
  }
}

const kb = new Admin("기범");
const user1 = new User("민수");

kb.login(); // 기범님이 로그인했습니다.
kb.deleteUser(user1); // 민수님이 삭제되었습니다.

✅ 클래스 상속을 이용해 역할(Role)별 로직을 구조화
✅ 실제 백엔드 사용자 모델에서도 거의 동일한 방식으로 사용


14. 정리

개념 설명

클래스(Class) 객체 생성 템플릿, 프로토타입 기반 문법
constructor 인스턴스 초기화 메서드
extends / super 상속 및 부모 호출
static 클래스 단위 메서드
private field 정보 은닉 (#field)
getter/setter 속성 접근 인터페이스
this 자동 바인딩(화살표 함수와 조합 시 유용)

15. 마무리

클래스는 단순한 문법 편의가 아니라,
자바스크립트를 진짜 객체지향 언어로 확장시킨 도구다.
프로토타입의 복잡한 구조를 단순화하면서,
캡슐화·상속·다형성의 3대 원리를 완벽히 구현할 수 있게 되었지.

“프로토타입은 자바스크립트의 유전자이고,
클래스는 그 유전자가 만들어낸 생명체다.”