🏰 JavaScript 객체지향 프로그래밍(OOP) 완전 정복|클래스, 프로토타입, 상속 흐름 이해하기
안녕하세요, 퍼블리셔 노미입니다!
이제 우리는 HTML, CSS, JavaScript의 기본 문법을 넘어 비동기 처리(async/await)까지 배웠습니다.
그런데 진짜로 탄탄한 코드를 짜기 위해서는 "객체지향 프로그래밍(OOP)"을 반드시 이해해야 합니다.
JavaScript는 함수형 프로그래밍과 객체지향 프로그래밍을 모두 지원하는 언어입니다.
특히 ES6 이후 '클래스(class)' 문법이 도입되면서 객체지향 프로그래밍이 훨씬 자연스럽게 가능해졌어요.
이번 글에서는 **객체 → 생성자 함수 → 프로토타입 → 클래스 → 상속** 흐름까지 아주 자세하게 정리해드릴게요.
📌 객체(Object)란 무엇인가?
자바스크립트에서 객체는 속성(프로퍼티)과 값(메소드)를 가지는 데이터 구조입니다.
기본 객체 생성 방법
const user = {
name: '노미',
age: 28,
greet: function() {
console.log(`안녕하세요, ${this.name}입니다.`);
}
};
user.greet(); // "안녕하세요, 노미입니다."
- key: value 쌍으로 데이터 저장
- 메소드는 함수(Function)를 값으로 가지는 속성
→ 객체는 데이터를 구조화하고 행동을 정의하는 기본 단위입니다.
🛠 객체를 만드는 다양한 방법
- 리터럴 방식 ({}로 생성)
- Object 생성자 사용 (
new Object()
) - 생성자 함수(Constructor Function)
- 클래스(Class) 사용
Object 생성자 사용 예시
const car = new Object();
car.brand = '현대';
car.model = '그랜저';
console.log(car.brand); // "현대"
🏗 생성자 함수(Constructor Function)
생성자 함수는 객체를 대량으로 찍어내는 틀 역할을 합니다.
생성자 함수 기본 형태
function User(name, age) {
this.name = name;
this.age = age;
}
const user1 = new User('노미', 28);
const user2 = new User('코코', 30);
console.log(user1.name); // "노미"
console.log(user2.name); // "코코"
- new 키워드를 사용하면 this가 새 객체를 가리킴
- this에 속성과 메소드를 정의
→ 생성자 함수는 객체 생성 로직을 재사용할 때 매우 유용합니다!
🔗 프로토타입(Prototype)이란?
JavaScript는 모든 객체가 프로토타입(Prototype)이라는 숨겨진 링크를 통해 다른 객체를 참조할 수 있습니다.
이걸 프로토타입 체인(Prototype Chain)이라고 합니다.
프로토타입 연결 구조
function Person(name) {
this.name = name;
}
Person.prototype.greet = function() {
console.log(`안녕하세요, ${this.name}입니다.`);
};
const p1 = new Person('노미');
p1.greet(); // "안녕하세요, 노미입니다."
- Person.prototype에 메소드를 추가
- 모든 Person 인스턴스가 공유
→ 메모리 절약 + 공통 기능 공유
🔍 프로토타입 체인 자세히 보기
객체를 찾을 때 JavaScript 엔진은 다음과 같은 순서로 탐색합니다.
- 자기 자신에게 해당 프로퍼티 있는지 찾기
- 없으면 프로토타입으로 올라가 찾기
- 계속해서 상위 프로토타입을 따라감
- 끝까지 없으면 undefined 반환
→ 이 과정을 프로토타입 체인이라고 부릅니다.
👑 ES6 클래스(Class) 등장
ES6(ES2015)부터 자바스크립트에도 클래스(class) 문법이 추가되었습니다.
사실상 생성자 함수 + 프로토타입을 더 깔끔하게 쓸 수 있게 만든 문법이에요.
클래스 기본 구조
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}이(가) 소리를 냅니다.`);
}
}
const dog = new Animal('코코');
dog.speak(); // "코코이(가) 소리를 냅니다."
- constructor() 메소드 안에 초기화 코드 작성
- 공통 메소드는 클래스 내부에 정의
→ 훨씬 깔끔하고 읽기 쉬운 코드!
🏰 클래스 상속(Inheritance) 이해하기
클래스는 다른 클래스로부터 속성과 메서드를 물려받아 재사용할 수 있습니다.
이걸 상속(inheritance)이라고 부릅니다.
상속 기본 구조
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name}이(가) 소리를 냅니다.`);
}
}
class Dog extends Animal {
bark() {
console.log(`${this.name}이(가) 짖습니다.`);
}
}
const dog = new Dog('코코');
dog.speak(); // "코코이(가) 소리를 냅니다."
dog.bark(); // "코코이(가) 짖습니다."
extends
키워드로 부모 클래스 상속- 부모 메서드를 그대로 사용하거나, 새 메서드 추가 가능
→ 코드 재사용성 극대화!
🧙 super() 키워드 사용법
자식 클래스에서 부모 클래스의 constructor를 호출할 때는 super()
를 사용합니다.
이걸 반드시 해야 this를 사용할 수 있어요!
super() 예시
class Animal {
constructor(name) {
this.name = name;
}
}
class Bird extends Animal {
constructor(name, color) {
super(name); // 부모 생성자 호출
this.color = color;
}
}
const bird = new Bird('참새', '회색');
console.log(bird.name); // "참새"
console.log(bird.color); // "회색"
→ super()는 반드시 constructor 내부 최상단에 호출해야 합니다!
⚡ 메소드 오버라이딩(Method Overriding)
자식 클래스는 부모 클래스의 메서드를 재정의(override)할 수 있습니다.
오버라이딩 예시
class Animal {
speak() {
console.log('동물이 소리를 냅니다.');
}
}
class Cat extends Animal {
speak() {
console.log('고양이가 야옹합니다.');
}
}
const cat = new Cat();
cat.speak(); // "고양이가 야옹합니다."
- 같은 이름의 메서드를 정의하면 덮어씀
- 특정 상황에 맞게 동작 커스터마이징 가능
🧬 instanceof 연산자
instanceof
를 사용하면 객체가 특정 클래스의 인스턴스인지 확인할 수 있습니다.
예시
class User {}
const user = new User();
console.log(user instanceof User); // true
console.log(user instanceof Object); // true
→ 프로토타입 체인을 따라 확인합니다!
🎯 캡슐화(Encapsulation)와 private 필드
클래스 내부에서만 접근 가능한 비공개(private) 속성을 만들 수 있습니다.
ES2022부터 #
문법으로 private 필드를 지원합니다.
private 필드 예시
class BankAccount {
#balance = 0;
deposit(amount) {
this.#balance += amount;
}
getBalance() {
return this.#balance;
}
}
const account = new BankAccount();
account.deposit(1000);
console.log(account.getBalance()); // 1000
console.log(account.#balance); // 에러! (직접 접근 불가)
- #로 선언한 필드는 외부에서 직접 접근 불가
- 캡슐화를 통해 데이터 보호
🔀 다형성(Polymorphism) 기초 이해
다형성(Polymorphism)이란, 같은 메서드 이름이더라도 상황에 따라 다르게 동작하는 특성을 의미합니다.
예시
class Shape {
draw() {
console.log('도형을 그립니다.');
}
}
class Circle extends Shape {
draw() {
console.log('원을 그립니다.');
}
}
class Square extends Shape {
draw() {
console.log('사각형을 그립니다.');
}
}
const shapes = [new Circle(), new Square()];
for (const shape of shapes) {
shape.draw();
}
// 출력: "원을 그립니다.", "사각형을 그립니다."
- 같은 메서드 이름(draw) → 다른 결과 출력
→ 다형성 덕분에 코드 확장성이 매우 좋아집니다!
🧙 추상 클래스(Abstract Class) 패턴
자바스크립트에는 진짜 '추상 클래스'가 존재하지 않지만, 추상 클래스처럼 동작하는 패턴을 구현할 수 있습니다.
(※ 타입스크립트에서는 공식적으로 지원함)
추상 클래스 패턴 예시
class Animal {
constructor() {
if (new.target === Animal) {
throw new Error("추상 클래스를 직접 생성할 수 없습니다.");
}
}
speak() {
throw new Error("speak() 메소드를 반드시 구현해야 합니다.");
}
}
class Dog extends Animal {
speak() {
console.log('멍멍!');
}
}
const dog = new Dog();
dog.speak(); // "멍멍!"
const animal = new Animal(); // 에러 발생!
new.target
을 이용해 직접 생성 차단- 필수 메소드 구현 강제 가능
→ 설계 강제성 부여 가능!
🔧 믹스인(Mixin) 패턴
Mixin은 여러 객체의 기능을 조합하여 하나의 클래스로 합치는 패턴입니다.
상속 없이 다양한 기능을 추가하고 싶을 때 유용합니다.
Mixin 예시
const Flyable = {
fly() {
console.log(`${this.name}이(가) 날아갑니다!`);
}
};
const Swimmable = {
swim() {
console.log(`${this.name}이(가) 수영합니다!`);
}
};
class Animal {
constructor(name) {
this.name = name;
}
}
Object.assign(Animal.prototype, Flyable, Swimmable);
const duck = new Animal('오리');
duck.fly(); // "오리이(가) 날아갑니다!"
duck.swim(); // "오리이(가) 수영합니다!"
Object.assign()
으로 여러 기능 조합
→ 상속 없이 다양한 기능을 조합할 수 있습니다!
⚔ 객체지향 vs 함수형 프로그래밍 비교
구분 | 객체지향 프로그래밍(OOP) | 함수형 프로그래밍(FP) |
---|---|---|
기본 단위 | 객체(Object) | 함수(Function) |
상태 관리 | 객체의 속성으로 관리 | 불변 데이터(Immutable) |
주요 개념 | 상속, 캡슐화, 다형성 | 순수 함수, 고차 함수 |
장점 | 복잡한 구조 모델링에 강함 | 코드 예측 가능성, 테스트 용이 |
단점 | 구조가 복잡해질 수 있음 | 대규모 프로젝트에 익숙하지 않을 수 있음 |
🌟 객체지향 프로그래밍 4대 원칙 정리
- 추상화(Abstraction): 필요한 정보만 공개하고 복잡성은 숨긴다
- 캡슐화(Encapsulation): 내부 데이터 보호
- 상속(Inheritance): 코드 재사용
- 다형성(Polymorphism): 상황에 따라 다양한 형태로 동작
→ 이 4대 원칙을 잘 이해하고 적용하면, 유지보수하기 쉽고 확장성 있는 프로그램을 만들 수 있어요.
🚀 JavaScript OOP 실전 적용 예제
실제 서비스 코드에서는 이렇게 OOP를 활용합니다.
예제: 유저 관리 시스템
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
introduce() {
console.log(`저는 ${this.name}입니다.`);
}
}
class Admin extends User {
constructor(name, email, role) {
super(name, email);
this.role = role;
}
manage() {
console.log(`${this.name}이(가) 시스템을 관리합니다.`);
}
}
const user = new User('노미', 'nomi@example.com');
const admin = new Admin('코코', 'coco@example.com', 'superadmin');
user.introduce(); // "저는 노미입니다."
admin.introduce(); // "저는 코코입니다."
admin.manage(); // "코코이(가) 시스템을 관리합니다."
- 일반 사용자(User)와 관리자(Admin) 분리
- 상속과 메소드 오버라이딩 활용
📚 마무리하며
JavaScript 객체지향 프로그래밍(OOP)은 단순히 클래스를 쓰는 걸 넘어, 코드 구조를 설계하고 유지보수하기 쉽게 만드는 기술입니다.
오늘 정리한 흐름대로 객체 → 생성자 함수 → 프로토타입 → 클래스 → 상속 → 캡슐화 → 다형성까지 마스터하면, 여러분은 진짜로 **프론트엔드 개발의 한 단계 높은 레벨**로 올라서게 됩니다.
다음 글에서는 디자인 패턴(Factory, Singleton, Observer 패턴)을 활용해 OOP를 실전에서 어떻게 적용하는지도 이어서 배워볼게요! 끝까지 함께 가요! 🚀
#JavaScript객체지향 #클래스상속 #프로토타입체인 #Mixin패턴 #OOP4대원칙 #캡슐화 #다형성 #퍼블리셔노미 #프론트엔드심화 #자바스크립트OOP
'차근차근' 카테고리의 다른 글
JavaScript 에러 처리와 디버깅 완전 정복|try-catch, throw, 디버깅 실전 방법 (1) | 2025.05.08 |
---|---|
JavaScript 디자인 패턴 완전 정복|싱글턴, 팩토리, 옵저버 패턴 쉽게 배우기 (2) | 2025.05.07 |
JavaScript 비동기 처리 완전 정복|콜백, 프로미스, async/await 흐름 한 번에 이해하기 (1) | 2025.05.05 |
React Router로 웹페이지 이동 흐름 완전 정복|SPA 라우팅 구현 모든 방법 (1) | 2025.05.04 |
React에서 모달 시스템 제대로 설계하기|Context 전역 관리부터 Portal까지 실전 구현 (1) | 2025.04.28 |