
자바스크립트를 공부하다 보면 프로토타입(Prototype)이라는 개념을 마주하게 됩니다. 클래스 기반 언어에 익숙한 개발자들에게는 다소 생소할 수 있지만, 프로토타입은 자바스크립트의 객체지향 프로그래밍을 이해하는 핵심 요소입니다. 오늘은 프로토타입 상속이 어떻게 동작하는지 자세히 알아보겠습니다.
프로토타입은 자바스크립트에서 객체 간의 상속을 구현하는 메커니즘입니다. 마치 가족의 유전자처럼, 부모 객체의 특성을 자식 객체가 물려받을 수 있게 해주는 시스템이라고 생각하면 됩니다.
자바스크립트의 모든 객체는 [[Prototype]]이라는 숨김 프로퍼티를 가지고 있습니다. 이 프로퍼티는 다른 객체를 참조하거나 null 값을 가질 수 있습니다.
Object.create()나 함수 생성자의 prototype 프로퍼티를 통해 설정됩니다프로토타입 상속의 핵심은 프로토타입 체인(Prototype Chain)입니다. 이는 마치 회사의 보고 체계와 같습니다. 직원이 해결할 수 없는 문제가 있으면 상급자에게, 상급자도 해결할 수 없으면 그 위의 상급자에게 올라가는 것처럼 작동합니다.
[[Prototype]]이 가리키는 프로토타입 객체에서 탐색null에 도달할 때까지 체인을 따라 계속 탐색이 과정을 통해 원하는 프로퍼티를 찾거나, 프로토타입 체인의 끝에 도달하게 됩니다.
// 부모 객체 정의
const dog = {
species: 'Canis lupus',
greet() {
console.log('Hello from dog!');
},
bark() {
console.log('Woof! Woof!');
}
};
// 자식 객체 생성 (dog를 프로토타입으로 설정)
const maru = Object.create(dog);
maru.name = 'Maru';
maru.age = 3;
// 프로토타입 체인을 통한 메서드 호출
maru.greet(); // "Hello from dog!" 출력
maru.bark(); // "Woof! Woof!" 출력
console.log(maru.species); // "Canis lupus" 출력
// 생성자 함수 정의
function Dog(name, age) {
this.name = name;
this.age = age;
}
// 프로토타입에 메서드 추가
Dog.prototype.greet = function() {
console.log(`Hello, I'm ${this.name}!`);
};
Dog.prototype.bark = function() {
console.log('Woof! Woof!');
};
Dog.prototype.getInfo = function() {
return `Name: ${this.name}, Age: ${this.age}`;
};
// 인스턴스 생성
const maru = new Dog('Maru', 3);
const bori = new Dog('Bori', 5);
// 프로토타입 메서드 사용
maru.greet(); // "Hello, I'm Maru!" 출력
bori.greet(); // "Hello, I'm Bori!" 출력
console.log(maru.getInfo()); // "Name: Maru, Age: 3" 출력
구체적인 예시를 통해 프로토타입 체인이 어떻게 동작하는지 살펴보겠습니다.
// 프로토타입 체인 예시
const animal = {
alive: true,
eat() {
console.log('Eating...');
}
};
const dog = Object.create(animal);
dog.bark = function() {
console.log('Woof!');
};
const maru = Object.create(dog);
maru.name = 'Maru';
// 프로토타입 체인 탐색 과정
console.log(maru.name); // 1. maru 객체에서 찾음 → "Maru"
maru.bark(); // 2. maru → dog에서 찾음 → "Woof!"
maru.eat(); // 3. maru → dog → animal에서 찾음 → "Eating..."
console.log(maru.alive); // 4. maru → dog → animal에서 찾음 → true
// 존재하지 않는 프로퍼티
console.log(maru.nonExistent); // undefined (체인 끝까지 탐색 후 못 찾음)
maru.eat() 호출 시:
| 단계 | 탐색 위치 | 결과 |
|---|---|---|
| 1단계 | maru 객체 | eat 메서드 없음 |
| 2단계 | dog 객체 (maru의 프로토타입) | eat 메서드 없음 |
| 3단계 | animal 객체 (dog의 프로토타입) | eat 메서드 발견! |
| 결과 | 탐색 종료 | animal.eat() 실행 |
// 프로토타입 방식
function Dog(name) {
this.name = name;
}
Dog.prototype.bark = function() {
console.log(`${this.name} barks!`);
};
// ES6 클래스 방식 (내부적으로는 프로토타입 사용)
class Dog {
constructor(name) {
this.name = name;
}
bark() {
console.log(`${this.name} barks!`);
}
}
두 방식 모두 내부적으로는 프로토타입을 사용하지만, 클래스 문법이 더 직관적이고 다른 언어와 유사한 구조를 제공합니다.
// 기본 플러그인 인터페이스
const BasePlugin = {
init() {
console.log('Plugin initialized');
},
destroy() {
console.log('Plugin destroyed');
}
};
// 특정 기능 플러그인
const chartPlugin = Object.create(BasePlugin);
chartPlugin.render = function(data) {
console.log('Rendering chart with data:', data);
};
chartPlugin.updateChart = function(newData) {
console.log('Updating chart with:', newData);
};
// 사용
chartPlugin.init(); // "Plugin initialized"
chartPlugin.render([1,2,3]); // "Rendering chart with data: [1,2,3]"
// 1단계: 기본 사용자
const User = {
login() {
console.log('User logged in');
}
};
// 2단계: 관리자 (User 상속)
const Admin = Object.create(User);
Admin.deleteUser = function(userId) {
console.log(`Deleting user ${userId}`);
};
// 3단계: 슈퍼 관리자 (Admin 상속)
const SuperAdmin = Object.create(Admin);
SuperAdmin.systemReset = function() {
console.log('System reset initiated');
};
// 모든 기능 사용 가능
const superUser = Object.create(SuperAdmin);
superUser.login(); // User의 메서드
superUser.deleteUser(123); // Admin의 메서드
superUser.systemReset(); // SuperAdmin의 메서드
// ❌ 나쁜 예: 내장 객체 프로토타입 수정
Array.prototype.customMethod = function() {
// 전역적으로 모든 배열에 영향을 줌
};
// ✅ 좋은 예: 별도 객체 생성
const CustomArray = Object.create(Array.prototype);
CustomArray.customMethod = function() {
// 안전한 확장
};
// 자주 호출되는 메서드는 가까운 프로토타입에 배치
const fastAccess = {
commonMethod() {
// 자주 사용되는 메서드
}
};
const myObject = Object.create(fastAccess);
// commonMethod는 1단계만 올라가면 찾을 수 있음
프로토타입 상속은 자바스크립트의 독특하면서도 강력한 특징입니다. 처음에는 복잡해 보일 수 있지만, 프로토타입 체인의 동작 원리를 이해하면 다음과 같은 이점을 얻을 수 있습니다:
모던 자바스크립트에서는 ES6 클래스를 주로 사용하지만, 프로토타입의 동작 원리를 이해하는 것은 여전히 중요합니다. 라이브러리 내부 동작을 이해하거나, 복잡한 상속 구조를 설계할 때, 그리고 성능 최적화를 할 때 프로토타입에 대한 깊은 이해가 큰 도움이 됩니다.