🟨 1-14. 객체지향 설계 심화 — 캡슐화, 상속, 추상화, 다형성을 자바스크립트로 구현하기
1. 객체지향의 4대 핵심 원칙
객체지향 프로그래밍(OOP)은 단순히 클래스를 쓰는 게 아니다.
진짜 핵심은 “설계 철학”이다.
자바스크립트에서도 이 철학은 그대로 적용된다.
원칙 설명 자바스크립트 적용 방식
| 캡슐화 (Encapsulation) | 데이터와 메서드를 한곳에 묶고, 외부 접근을 제한 | private 필드, getter/setter |
| 상속 (Inheritance) | 기존 코드를 재사용하여 새로운 클래스 생성 | extends, super() |
| 추상화 (Abstraction) | 불필요한 세부 구현은 숨기고 핵심만 노출 | 클래스/인터페이스 구조 |
| 다형성 (Polymorphism) | 같은 메서드가 다른 방식으로 동작 | 메서드 오버라이딩, 인터페이스 구현 |
2. 캡슐화(Encapsulation) — 내부 데이터를 보호하라
캡슐화는 “데이터를 직접 조작하지 못하게 하고, 메서드로만 접근하게 만드는 것”이다.
class BankAccount {
#balance = 0; // private field
deposit(amount) {
if (amount <= 0) throw new Error("금액은 0보다 커야 합니다.");
this.#balance += amount;
}
withdraw(amount) {
if (amount > this.#balance) throw new Error("잔액이 부족합니다.");
this.#balance -= amount;
}
get balance() {
return this.#balance;
}
}
const acc = new BankAccount();
acc.deposit(1000);
acc.withdraw(400);
console.log(acc.balance); // 600
이렇게 #으로 선언된 필드는 클래스 내부에서만 접근 가능하다.
외부에서 acc.#balance를 접근하면 에러가 발생한다.
즉, 클래스의 내부 구현을 숨기고,
필요한 기능만 ‘공식 인터페이스’로 제공하는 것이 캡슐화다.
3. getter / setter — 안전한 데이터 접근
캡슐화와 함께 자주 쓰이는 개념이 getter/setter다.
이건 속성처럼 보이지만 실제로는 함수다.
class User {
#name;
constructor(name) {
this.#name = name;
}
get name() {
return this.#name;
}
set name(value) {
if (value.length < 2) throw new Error("이름은 두 글자 이상이어야 합니다.");
this.#name = value;
}
}
const user = new User("기범");
console.log(user.name); // 기범
user.name = "민수"; // setter 실행
console.log(user.name); // 민수
이런 구조는 외부에서 잘못된 값을 직접 넣는 것을 방지하고,
필요하면 내부적으로 추가 로직(검증, 로그 기록 등)을 수행할 수 있다.
4. 상속(Inheritance) — 코드를 재사용하라
이전 편에서도 봤듯이, extends 키워드로 부모 클래스의 기능을 자식 클래스가 물려받을 수 있다.
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}이(가) 소리를 냅니다.`);
}
}
class Dog extends Animal {
speak() {
console.log(`${this.name}이(가) 멍멍 짖습니다.`);
}
}
const dog = new Dog("해피");
dog.speak(); // 해피이(가) 멍멍 짖습니다.
부모의 생성자에 접근할 때는 반드시 super()를 호출해야 한다.
class Cat extends Animal {
constructor(name, color) {
super(name);
this.color = color;
}
}
상속은 중복 코드를 줄이고, 공통 로직을 한 번만 정의할 수 있도록 한다.
5. 추상화(Abstraction) — 불필요한 복잡성을 감춰라
자바스크립트에는 “추상 클래스”나 “인터페이스” 같은 문법이 없다.
하지만 다음과 같이 구현할 수 있다.
class Shape {
area() {
throw new Error("하위 클래스에서 area()를 구현해야 합니다.");
}
}
class Circle extends Shape {
constructor(radius) {
super();
this.radius = radius;
}
area() {
return Math.PI * this.radius ** 2;
}
}
const circle = new Circle(5);
console.log(circle.area()); // 78.5398...
Shape 클래스는 설계만 제공하고, 실제 구현은 하위 클래스에서 담당한다.
이게 바로 “추상화”의 핵심이다.
6. 다형성(Polymorphism) — 같은 인터페이스, 다른 동작
같은 메서드 이름을 갖지만, 각 클래스마다 다르게 동작할 수 있다.
class Shape {
draw() {
console.log("도형을 그립니다.");
}
}
class Circle extends Shape {
draw() {
console.log("원을 그립니다.");
}
}
class Rectangle extends Shape {
draw() {
console.log("사각형을 그립니다.");
}
}
const shapes = [new Circle(), new Rectangle()];
shapes.forEach(shape => shape.draw());
출력 결과:
원을 그립니다.
사각형을 그립니다.
즉, 같은 draw() 메서드라도 객체의 타입에 따라 다르게 실행된다.
이것이 다형성의 본질이다.
7. 실무 예시 — UI 컴포넌트 추상화
프론트엔드에서 이런 객체지향 설계는 실제로 UI 컴포넌트 구조 설계에 자주 쓰인다.
class Component {
constructor(id) {
this.el = document.getElementById(id);
}
render() {
throw new Error("render()는 반드시 구현해야 합니다.");
}
}
class Button extends Component {
constructor(id, label) {
super(id);
this.label = label;
}
render() {
this.el.innerHTML = `<button>${this.label}</button>`;
}
}
class Input extends Component {
constructor(id, placeholder) {
super(id);
this.placeholder = placeholder;
}
render() {
this.el.innerHTML = `<input placeholder="${this.placeholder}" />`;
}
}
const btn = new Button("app", "클릭!");
btn.render();
이런 구조는 React의 “컴포넌트 추상화” 원리와 매우 유사하다.
Component가 추상 클래스 역할을 하며, 각 파생 클래스는 구체적인 UI를 구현한다.
8. 상속보다 조합(Composition)을 고려하라
실무에서는 상속이 남용되면 오히려 유지보수가 어려워진다.
그래서 최근에는 “조합(Composition over Inheritance)” 철학이 더 많이 쓰인다.
즉, “상속으로 확장하기보다 필요한 기능을 합성하자.”
const clickable = {
onClick() {
console.log("클릭됨!");
},
};
const draggable = {
onDrag() {
console.log("드래그 중...");
},
};
function createButton(name) {
return {
name,
...clickable,
...draggable,
};
}
const button = createButton("버튼");
button.onClick();
button.onDrag();
이런 식으로 여러 기능을 조립하면 훨씬 유연한 구조를 만들 수 있다.
React 훅(Hooks)이나 Vue 믹스인(Mixins)도 같은 철학을 따른다.
9. 정리
개념 핵심 역할 자바스크립트 구현 방식
| 캡슐화 | 데이터 보호 | private 필드, getter/setter |
| 상속 | 코드 재사용 | extends, super() |
| 추상화 | 복잡성 감춤 | 메서드 미구현, 공통 인터페이스 제공 |
| 다형성 | 같은 메서드, 다른 동작 | 오버라이딩 |
| 조합 | 유연한 구조 | 객체 병합, 믹스인 패턴 |
10. 마무리
객체지향은 단순히 “class를 쓰는 문법”이 아니다.
그것은 “코드를 사람처럼 사고하는 방식”이다.
자바스크립트는 느슨하지만 강력한 객체지향 언어다.
캡슐화로 데이터를 보호하고,
상속으로 로직을 재사용하며,
추상화로 복잡성을 감추고,
다형성으로 유연하게 대응한다.
결국 이 모든 구조는
“확장성 있고 예측 가능한 코드”를 만드는 데 목적이 있다.
다음 편에서는
1-15. 모듈화 + 객체지향 설계로 프로젝트 구조 설계하기 — 폴더 구조부터 설계 철학까지
를 통해 지금까지 배운 모든 개념(모듈, 클래스, 상속, 캡슐화)을
하나의 실무 프로젝트 구조로 엮어볼 거야.