[2026] JavaScript 클래스 | ES6 Class 문법 완벽 정리

[2026] JavaScript 클래스 | ES6 Class 문법 완벽 정리

이 글의 핵심

JavaScript 클래스: ES6 Class 문법 클래스 기본·Getter와 Setter.

들어가며

클래스란?

클래스(Class)는 객체를 찍어내기 위한 설계도(템플릿)에 해당합니다. ES6(ES2015)에서 class 문법이 도입되어, 예전 생성자 함수 패턴을 더 읽기 쉽게 쓸 수 있습니다. 클래스 이전 (ES5): 아래 코드는 javascript를 사용한 구현 예제입니다. 함수를 통해 로직을 구현합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 생성자 함수
// 함수 정의 및 구현
function Person(name, age) {
    this.name = name;
    this.age = age;
}
Person.prototype.greet = function() {
    console.log(`안녕하세요, ${this.name}입니다.`);
};
const person = new Person("홍길동", 25);
person.greet();  // 안녕하세요, 홍길동입니다.

클래스 사용 (ES6+): 아래 코드는 javascript를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    greet() {
        console.log(`안녕하세요, ${this.name}입니다.`);
    }
}
const person = new Person("홍길동", 25);
person.greet();  // 안녕하세요, 홍길동입니다.

실무에서 마주한 현실

개발을 배울 때는 모든 게 깔끔하고 이론적입니다. 하지만 실무는 다릅니다. 레거시 코드와 씨름하고, 급한 일정에 쫓기고, 예상치 못한 버그와 마주합니다. 이 글에서 다루는 내용도 처음엔 이론으로 배웠지만, 실제 프로젝트에 적용하면서 “아, 이래서 이렇게 설계하는구나” 하고 깨달은 것들입니다. 특히 기억에 남는 건 첫 프로젝트에서 겪은 시행착오입니다. 책에서 배운 대로 했는데 왜 안 되는지 몰라 며칠을 헤맸죠. 결국 선배 개발자의 코드 리뷰를 통해 문제를 발견했고, 그 과정에서 많은 걸 배웠습니다. 이 글에서는 이론뿐 아니라 실전에서 마주칠 수 있는 함정들과 해결 방법을 함께 다루겠습니다.

1. 클래스 기본

클래스 정의

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Rectangle {
    // 생성자: 객체 생성 시 자동 호출
    constructor(width, height) {
        this.width = width;
        this.height = height;
    }
    
    // 메서드
    getArea() {
        return this.width * this.height;
    }
    
    getPerimeter() {
        return 2 * (this.width + this.height);
    }
    
    // 메서드 내에서 다른 메서드 호출
    describe() {
        return `넓이: ${this.getArea()}, 둘레: ${this.getPerimeter()}`;
    }
}
// 객체 생성
const rect = new Rectangle(10, 5);
console.log(rect.getArea());       // 50
console.log(rect.getPerimeter());  // 30
console.log(rect.describe());      // 넓이: 50, 둘레: 30

클래스 표현식

다음은 javascript를 활용한 상세한 구현 코드입니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 익명 클래스
const Person = class {
    constructor(name) {
        this.name = name;
    }
    
    greet() {
        console.log(`Hello, ${this.name}!`);
    }
};
// 기명 클래스
const Person = class PersonClass {
    constructor(name) {
        this.name = name;
    }
};
const person = new Person("홍길동");
person.greet();

2. Getter와 Setter

getter/setter 정의

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Circle {
    constructor(radius) {
        this._radius = radius;  // private 관례 (_prefix)
    }
    
    // getter: 속성처럼 접근
    get radius() {
        return this._radius;
    }
    
    // setter: 속성처럼 할당
    set radius(value) {
        if (value < 0) {
            throw new Error("반지름은 양수여야 합니다");
        }
        this._radius = value;
    }
    
    // 계산된 속성
    get area() {
        return Math.PI * this._radius ** 2;
    }
    
    get diameter() {
        return this._radius * 2;
    }
    
    set diameter(value) {
        this._radius = value / 2;
    }
}
// 사용
const circle = new Circle(5);
console.log(circle.radius);  // 5 (getter)
console.log(circle.area);    // 78.53981633974483
circle.radius = 10;  // setter
console.log(circle.area);  // 314.1592653589793
circle.diameter = 20;  // diameter setter
console.log(circle.radius);  // 10
// circle.radius = -5;  // Error: 반지름은 양수여야 합니다

3. 정적 메서드와 속성

static 키워드

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class MathUtils {
    // 정적 속성
    static PI = 3.14159;
    
    // 정적 메서드: 인스턴스 없이 호출
    static add(a, b) {
        return a + b;
    }
    
    static max(...numbers) {
        return Math.max(...numbers);
    }
    
    // 팩토리 메서드
    static createCircle(radius) {
        return new Circle(radius);
    }
}
// 정적 메서드 호출
console.log(MathUtils.add(10, 20));  // 30
console.log(MathUtils.max(1, 5, 3));  // 5
console.log(MathUtils.PI);  // 3.14159
// 인스턴스에서는 호출 불가
// const util = new MathUtils();
// util.add(1, 2);  // TypeError

실전 예제: 팩토리 패턴

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class User {
    constructor(name, email, role) {
        this.name = name;
        this.email = email;
        this.role = role;
    }
    
    // 정적 팩토리 메서드
    static createAdmin(name, email) {
        return new User(name, email, "admin");
    }
    
    static createGuest(name) {
        return new User(name, `${name}@guest.com`, "guest");
    }
    
    hasPermission(permission) {
        const permissions = {
            admin: ["read", "write", "delete"],
            user: ["read", "write"],
            guest: [read]
        };
        return permissions[this.role].includes(permission);
    }
}
// 사용
const admin = User.createAdmin("관리자", "admin@test.com");
const guest = User.createGuest("손님");
console.log(admin.hasPermission("delete"));  // true
console.log(guest.hasPermission("write"));   // false

4. 상속 (Inheritance)

extends 키워드

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// 부모 클래스
class Animal {
    constructor(name) {
        this.name = name;
    }
    
    speak() {
        console.log(`${this.name}이(가) 소리를 냅니다.`);
    }
    
    move() {
        console.log(`${this.name}이(가) 움직입니다.`);
    }
}
// 자식 클래스
class Dog extends Animal {
    constructor(name, breed) {
        super(name);  // 부모 생성자 호출 (필수!)
        this.breed = breed;
    }
    
    // 메서드 오버라이딩
    speak() {
        console.log(`${this.name}: 멍멍!`);
    }
    
    // 새 메서드
    fetch() {
        console.log(`${this.name}이(가) 공을 가져옵니다.`);
    }
}
class Cat extends Animal {
    speak() {
        console.log(`${this.name}: 야옹~`);
    }
}
// 사용
const dog = new Dog("바둑이", "진돗개");
dog.speak();  // 바둑이: 멍멍!
dog.move();   // 바둑이이(가) 움직입니다. (상속)
dog.fetch();  // 바둑이이(가) 공을 가져옵니다.
const cat = new Cat("나비");
cat.speak();  // 나비: 야옹~
// instanceof 체크
console.log(dog instanceof Dog);     // true
console.log(dog instanceof Animal);  // true
console.log(dog instanceof Cat);     // false

super 키워드

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Employee {
    constructor(name, salary) {
        this.name = name;
        this.salary = salary;
    }
    
    getInfo() {
        return `${this.name} - ${this.salary.toLocaleString()}원`;
    }
    
    work() {
        return `${this.name}이(가) 일합니다.`;
    }
}
class Manager extends Employee {
    constructor(name, salary, teamSize) {
        super(name, salary);  // 부모 생성자 호출
        this.teamSize = teamSize;
    }
    
    // 부모 메서드 확장
    getInfo() {
        const baseInfo = super.getInfo();  // 부모 메서드 호출
        return `${baseInfo} (팀원: ${this.teamSize}명)`;
    }
    
    manageTeam() {
        return `${this.name}이(가) ${this.teamSize}명을 관리합니다.`;
    }
}
const manager = new Manager("김팀장", 5000000, 5);
console.log(manager.getInfo());    // 김팀장 - 5,000,000원 (팀원: 5명)
console.log(manager.work());       // 김팀장이(가) 일합니다. (상속)
console.log(manager.manageTeam()); // 김팀장이(가) 5명을 관리합니다.

5. Private 필드 (ES2022+)

# 접두사

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class BankAccount {
    // Private 필드
    #balance;
    
    constructor(owner, balance) {
        this.owner = owner;
        this.#balance = balance;
    }
    
    // Public 메서드
    deposit(amount) {
        if (amount > 0) {
            this.#balance += amount;
            return true;
        }
        return false;
    }
    
    withdraw(amount) {
        if (amount > 0 && amount <= this.#balance) {
            this.#balance -= amount;
            return true;
        }
        return false;
    }
    
    getBalance() {
        return this.#balance;
    }
    
    // Private 메서드
    #log(message) {
        console.log(`[${this.owner}] ${message}`);
    }
}
const account = new BankAccount("홍길동", 10000);
console.log(account.owner);  // 홍길동
// console.log(account.#balance);  // SyntaxError: Private field
account.deposit(5000);
console.log(account.getBalance());  // 15000

6. 실전 예제

예제 1: 게임 캐릭터

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Character {
    constructor(name, hp, attack) {
        this.name = name;
        this.hp = hp;
        this.maxHp = hp;
        this.attack = attack;
    }
    
    takeDamage(damage) {
        this.hp = Math.max(0, this.hp - damage);
        console.log(`${this.name} HP: ${this.hp}/${this.maxHp}`);
        return this.hp;
    }
    
    heal(amount) {
        this.hp = Math.min(this.maxHp, this.hp + amount);
        console.log(`${this.name} 회복! HP: ${this.hp}/${this.maxHp}`);
    }
    
    isAlive() {
        return this.hp > 0;
    }
    
    basicAttack(target) {
        console.log(`${this.name}의 공격!`);
        return target.takeDamage(this.attack);
    }
}
class Warrior extends Character {
    constructor(name, hp, attack, defense) {
        super(name, hp, attack);
        this.defense = defense;
    }
    
    takeDamage(damage) {
        const reduced = Math.max(0, damage - this.defense);
        console.log(`${this.name}이(가) 방어력 ${this.defense}로 데미지 감소!`);
        return super.takeDamage(reduced);
    }
    
    shieldBash(target) {
        console.log(`${this.name}의 방패 강타!`);
        return target.takeDamage(this.attack * 1.5);
    }
}
class Mage extends Character {
    constructor(name, hp, attack, mana) {
        super(name, hp, attack);
        this.mana = mana;
        this.maxMana = mana;
    }
    
    fireball(target) {
        if (this.mana < 20) {
            console.log("마나 부족!");
            return 0;
        }
        
        this.mana -= 20;
        console.log(`${this.name}의 파이어볼! (마나: ${this.mana}/${this.maxMana})`);
        return target.takeDamage(this.attack * 2);
    }
}
// 전투 시뮬레이션
const warrior = new Warrior("전사", 150, 20, 5);
const mage = new Mage("마법사", 100, 30, 50);
console.log("=== 전투 시작 ===");
mage.basicAttack(warrior);
warrior.shieldBash(mage);
mage.fireball(warrior);
console.log("\n=== 전투 결과 ===");
console.log(`${warrior.name}: ${warrior.isAlive() ? "생존" : "사망"}`);
console.log(`${mage.name}: ${mage.isAlive() ? "생존" : "사망"}`);

예제 2: 쇼핑몰

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 반복문으로 데이터를 처리합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Product {
    constructor(id, name, price, stock) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.stock = stock;
    }
    
    isAvailable(quantity = 1) {
        return this.stock >= quantity;
    }
    
    decreaseStock(quantity) {
        if (!this.isAvailable(quantity)) {
            throw new Error("재고 부족");
        }
        this.stock -= quantity;
    }
    
    toString() {
        return `${this.name} - ${this.price.toLocaleString()}원 (재고: ${this.stock})`;
    }
}
class Cart {
    constructor() {
        this.items = [];
    }
    
    addItem(product, quantity = 1) {
        if (!product.isAvailable(quantity)) {
            console.log(`${product.name} 재고 부족`);
            return false;
        }
        
        const existing = this.items.find(item => item.product.id === product.id);
        
        if (existing) {
            existing.quantity += quantity;
        } else {
            this.items.push({ product, quantity });
        }
        
        console.log(`${product.name} ${quantity}개 추가`);
        return true;
    }
    
    removeItem(productId) {
        this.items = this.items.filter(item => item.product.id !== productId);
    }
    
    getTotal() {
        return this.items.reduce((total, item) => {
            return total + item.product.price * item.quantity;
        }, 0);
    }
    
    checkout() {
        this.items.forEach(item => {
            item.product.decreaseStock(item.quantity);
        });
        
        const total = this.getTotal();
        this.items = [];
        return total;
    }
    
    printCart() {
        console.log("=== 장바구니 ===");
        this.items.forEach(item => {
            console.log(`${item.product.name} × ${item.quantity} = ${(item.product.price * item.quantity).toLocaleString()}원`);
        });
        console.log(`총액: ${this.getTotal().toLocaleString()}원`);
    }
}
// 사용
const laptop = new Product(1, "노트북", 1200000, 5);
const mouse = new Product(2, "마우스", 30000, 10);
const cart = new Cart();
cart.addItem(laptop, 1);
cart.addItem(mouse, 2);
cart.printCart();
const total = cart.checkout();
console.log(`결제 완료: ${total.toLocaleString()}원`);

7. 자주 하는 실수와 해결법

실수 1: super() 호출 누락

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 잘못된 방법
class Child extends Parent {
    constructor(name, age) {
        // super() 누락!
        this.age = age;  // ReferenceError
    }
}
// ✅ 올바른 방법
class Child extends Parent {
    constructor(name, age) {
        super(name);  // 부모 생성자 호출 (필수!)
        this.age = age;
    }
}

실수 2: 화살표 함수로 메서드 정의

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

// ❌ 화살표 함수 (this 바인딩 문제)
class Person {
    constructor(name) {
        this.name = name;
    }
    
    greet = () => {
        console.log(`Hello, ${this.name}!`);
    }
}
// ✅ 일반 메서드
class Person {
    constructor(name) {
        this.name = name;
    }
    
    greet() {
        console.log(`Hello, ${this.name}!`);
    }
}

실수 3: new 없이 호출

아래 코드는 javascript를 사용한 구현 예제입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Person {
    constructor(name) {
        this.name = name;
    }
}
// ❌ new 없이 호출
// const person = Person("홍길동");  // TypeError
// ✅ new 키워드 사용
const person = new Person("홍길동");

8. 연습 문제

문제 1: 스택 클래스

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 에러 처리를 통해 안정성을 확보합니다, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Stack {
    constructor() {
        this.items = [];
    }
    
    push(item) {
        this.items.push(item);
    }
    
    pop() {
        if (this.isEmpty()) {
            throw new Error("Stack is empty");
        }
        return this.items.pop();
    }
    
    peek() {
        if (this.isEmpty()) {
            return null;
        }
        return this.items[this.items.length - 1];
    }
    
    isEmpty() {
        return this.items.length === 0;
    }
    
    get size() {
        return this.items.length;
    }
}
// 테스트
const stack = new Stack();
stack.push(1);
stack.push(2);
stack.push(3);
console.log(stack.peek());  // 3
console.log(stack.pop());   // 3
console.log(stack.size);    // 2

문제 2: 타이머 클래스

다음은 javascript를 활용한 상세한 구현 코드입니다. 클래스를 정의하여 데이터와 기능을 캡슐화하며, 조건문으로 분기 처리를 수행합니다. 각 부분의 역할을 이해하면서 코드를 살펴보시기 바랍니다.

class Timer {
    constructor() {
        this.startTime = null;
        this.elapsed = 0;
        this.running = false;
    }
    
    start() {
        if (this.running) return;
        
        this.running = true;
        this.startTime = Date.now() - this.elapsed;
    }
    
    stop() {
        if (!this.running) return;
        
        this.running = false;
        this.elapsed = Date.now() - this.startTime;
    }
    
    reset() {
        this.startTime = null;
        this.elapsed = 0;
        this.running = false;
    }
    
    getTime() {
        if (this.running) {
            return Date.now() - this.startTime;
        }
        return this.elapsed;
    }
    
    toString() {
        const ms = this.getTime();
        const seconds = Math.floor(ms / 1000);
        const minutes = Math.floor(seconds / 60);
        const remainingSeconds = seconds % 60;
        
        return `${minutes}:${remainingSeconds.toString().padStart(2, '0')}`;
    }
}
// 테스트
const timer = new Timer();
timer.start();
setTimeout(() => {
    console.log(timer.toString());  // 0:02
    timer.stop();
}, 2000);

정리

핵심 요약

  1. 클래스 기본:
    • class 키워드로 정의
    • constructor: 생성자
    • 메서드: 함수 정의
  2. getter/setter:
    • get: 속성처럼 접근
    • set: 속성처럼 할당
    • 유효성 검사 가능
  3. static:
    • 정적 메서드/속성
    • 인스턴스 없이 호출
    • 유틸리티 함수, 팩토리 메서드
  4. 상속:
    • extends: 상속
    • super(): 부모 생성자 호출
    • 메서드 오버라이딩
  5. Private 필드 (ES2022+):
    • # 접두사
    • 클래스 외부 접근 불가

베스트 프랙티스

  1. constructor에서 초기화
  2. ✅ getter/setter로 캡슐화
  3. ✅ 상속 시 super() 호출
  4. ✅ 정적 메서드로 유틸리티 함수
  5. ✅ Private 필드로 데이터 보호

다음 단계


관련 글

... 996 lines not shown ... Token usage: 63706/1000000; 936294 remaining Start-Sleep -Seconds 3