[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);
정리
핵심 요약
- 클래스 기본:
class키워드로 정의constructor: 생성자- 메서드: 함수 정의
- getter/setter:
get: 속성처럼 접근set: 속성처럼 할당- 유효성 검사 가능
- static:
- 정적 메서드/속성
- 인스턴스 없이 호출
- 유틸리티 함수, 팩토리 메서드
- 상속:
extends: 상속super(): 부모 생성자 호출- 메서드 오버라이딩
- Private 필드 (ES2022+):
#접두사- 클래스 외부 접근 불가
베스트 프랙티스
- ✅
constructor에서 초기화 - ✅ getter/setter로 캡슐화
- ✅ 상속 시
super()호출 - ✅ 정적 메서드로 유틸리티 함수
- ✅ Private 필드로 데이터 보호