이미지 로딩 중...
CodeDeck AI
2025. 11. 8. · 1 Views
Prototype Pattern 트러블슈팅 가이드
JavaScript의 Prototype Pattern 사용 시 자주 발생하는 문제점들과 해결 방법을 다룹니다. 초급 개발자가 겪을 수 있는 실수와 Best Practice를 코드로 설명합니다.
들어가며
이 글에서는 Prototype Pattern 트러블슈팅 가이드에 대해 상세히 알아보겠습니다. 총 10가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- Prototype_메서드_덮어쓰기_문제
- Prototype_체인_끊김_문제
- 참조_타입_속성_공유_문제
- this_바인딩_문제
- 상속_시_부모_생성자_미호출_문제
- hasOwnProperty_체크_누락
- Object.create_null_문제
- instanceof_체크_실패_문제
- 프로토타입_오염_보안_문제
- 성능_최적화_패턴
1. Prototype_메서드_덮어쓰기_문제
개요
프로토타입 메서드를 개별 인스턴스에서 덮어쓰면 메모리 낭비가 발생합니다. 모든 인스턴스가 공유해야 할 메서드는 프로토타입에 정의해야 합니다.
코드 예제
// 잘못된 방법
function User(name) {
this.name = name;
this.greet = function() { // 인스턴스마다 함수 생성
return `Hello, ${this.name}`;
};
}
// 올바른 방법
function User(name) {
this.name = name;
}
User.prototype.greet = function() {
return `Hello, ${this.name}`;
};
설명
프로토타입에 메서드를 정의하면 모든 인스턴스가 하나의 함수를 공유하여 메모리를 절약할 수 있습니다. 생성자 내부에 함수를 정의하면 인스턴스마다 새로운 함수가 생성됩니다.
2. Prototype_체인_끊김_문제
개요
프로토타입을 객체 리터럴로 완전히 교체하면 constructor 속성이 사라집니다. 이는 디버깅과 인스턴스 타입 확인을 어렵게 만듭니다.
코드 예제
function Animal(name) {
this.name = name;
}
// 문제 발생
Animal.prototype = {
speak() { return 'Some sound'; }
};
// 해결 방법
Animal.prototype = {
constructor: Animal,
speak() { return 'Some sound'; }
};
설명
프로토타입을 새 객체로 교체할 때는 반드시 constructor 속성을 명시적으로 설정해야 합니다. 이를 통해 인스턴스의 타입을 올바르게 확인할 수 있습니다.
3. 참조_타입_속성_공유_문제
개요
프로토타입에 배열이나 객체 같은 참조 타입을 정의하면 모든 인스턴스가 같은 객체를 공유하게 됩니다. 이는 예상치 못한 버그를 발생시킵니다.
코드 예제
// 문제 발생
function Cart() {}
Cart.prototype.items = []; // 모든 인스턴스가 공유
const cart1 = new Cart();
cart1.items.push('apple');
const cart2 = new Cart();
console.log(cart2.items); // ['apple'] - 의도하지 않음
// 해결 방법
function Cart() {
this.items = []; // 각 인스턴스마다 새 배열
}
설명
참조 타입 속성은 생성자 함수 내부에서 초기화해야 합니다. 프로토타입의 참조 타입은 모든 인스턴스가 공유하므로 한 인스턴스의 변경이 다른 인스턴스에 영향을 줍니다.
4. this_바인딩_문제
개요
프로토타입 메서드를 다른 컨텍스트에서 호출하면 this가 예상과 다른 객체를 가리킵니다. bind, arrow function, 또는 명시적 컨텍스트 전달로 해결합니다.
코드 예제
function Timer(seconds) {
this.seconds = seconds;
}
Timer.prototype.start = function() {
setTimeout(function() {
console.log(this.seconds); // undefined
}, 1000);
};
// 해결 방법
Timer.prototype.start = function() {
setTimeout(() => {
console.log(this.seconds); // 정상 작동
}, 1000);
};
설명
화살표 함수는 상위 스코프의 this를 유지하므로 콜백에서 안전하게 인스턴스의 속성에 접근할 수 있습니다. 일반 함수는 호출 방식에 따라 this가 달라집니다.
5. 상속_시_부모_생성자_미호출_문제
개요
프로토타입 상속 시 자식 생성자에서 부모 생성자를 호출하지 않으면 부모의 속성이 초기화되지 않습니다. call 또는 apply를 사용해야 합니다.
코드 예제
function Vehicle(type) {
this.type = type;
}
// 문제 발생
function Car(brand) {
this.brand = brand;
}
Car.prototype = Object.create(Vehicle.prototype);
// 해결 방법
function Car(brand, type) {
Vehicle.call(this, type); // 부모 생성자 호출
this.brand = brand;
}
설명
자식 생성자에서 부모 생성자를 명시적으로 호출해야 부모의 속성이 올바르게 초기화됩니다. Object.create는 프로토타입 체인만 연결할 뿐 생성자를 실행하지 않습니다.
6. hasOwnProperty_체크_누락
개요
for...in 루프나 속성 확인 시 프로토타입 체인의 속성까지 검색됩니다. hasOwnProperty로 인스턴스 고유 속성만 필터링해야 합니다.
코드 예제
function Person(name) {
this.name = name;
}
Person.prototype.age = 25;
const person = new Person('John');
// 문제: 프로토타입 속성도 출력
for (let key in person) {
console.log(key); // name, age
}
// 해결: 인스턴스 속성만 출력
for (let key in person) {
if (person.hasOwnProperty(key)) {
console.log(key); // name만 출력
}
}
설명
hasOwnProperty는 객체 자신의 속성인지 확인합니다. 프로토타입 체인을 통해 상속받은 속성과 구분할 때 필수적으로 사용해야 합니다.
7. Object.create_null_문제
개요
Object.create(null)로 생성한 객체는 프로토타입이 없어 hasOwnProperty 같은 기본 메서드를 사용할 수 없습니다. Object.prototype.hasOwnProperty.call을 사용해야 합니다.
코드 예제
// 문제 발생
const obj = Object.create(null);
obj.name = 'Test';
// obj.hasOwnProperty('name'); // Error!
// 해결 방법 1: Object.prototype 메서드 직접 호출
const hasOwn = Object.prototype.hasOwnProperty.call(
obj,
'name'
);
// 해결 방법 2: Object.hasOwn (ES2022)
const hasOwn2 = Object.hasOwn(obj, 'name');
설명
Object.create(null)은 완전히 빈 객체를 생성하므로 프로토타입 메서드가 없습니다. Object.hasOwn이나 call을 통한 메서드 빌림 패턴을 사용해야 합니다.
8. instanceof_체크_실패_문제
개요
프로토타입을 잘못 설정하거나 여러 프레임워크 간 객체 전달 시 instanceof가 제대로 작동하지 않을 수 있습니다. Symbol.hasInstance로 커스터마이징 가능합니다.
코드 예제
function Bird() {}
Bird.prototype = { fly() {} }; // constructor 누락
const bird = new Bird();
console.log(bird instanceof Bird); // false!
// 해결 방법
Bird.prototype = {
constructor: Bird,
fly() {}
};
const bird2 = new Bird();
console.log(bird2 instanceof Bird); // true
설명
instanceof는 프로토타입 체인을 검사하므로 constructor가 올바르게 설정되어야 합니다. 프로토타입을 교체할 때는 항상 constructor를 명시해야 합니다.
9. 프로토타입_오염_보안_문제
개요
사용자 입력으로 프로토타입 속성을 수정하면 모든 객체에 영향을 주는 보안 취약점이 발생합니다. Object.create(null) 또는 Map을 사용해 방지합니다.
코드 예제
// 취약한 코드
const user = {};
user['__proto__'].isAdmin = true; // 모든 객체에 영향!
// 안전한 방법 1: null 프로토타입
const safeUser = Object.create(null);
safeUser.name = 'John';
// 안전한 방법 2: Map 사용
const userMap = new Map();
userMap.set('name', 'John');
userMap.set('__proto__', 'safe'); // 안전함
설명
__proto__나 constructor를 통한 프로토타입 조작을 막으려면 프로토타입이 없는 객체나 Map을 사용해야 합니다. 사용자 입력을 객체 키로 사용할 때 특히 주의해야 합니다.
10. 성능_최적화_패턴
개요
자주 사용되는 메서드는 프로토타입에 정의하고, 인스턴스별 데이터만 생성자에 두어 메모리와 성능을 최적화합니다.
코드 예제
// 비효율적
function Product(name, price) {
this.name = name;
this.price = price;
this.getPrice = function() { // 매번 생성
return `$${this.price}`;
};
}
// 효율적
function Product(name, price) {
this.name = name;
this.price = price;
}
Product.prototype.getPrice = function() {
return `$${this.price}`;
};
설명
메서드를 프로토타입에 정의하면 수천 개의 인스턴스를 생성해도 메서드는 메모리에 한 번만 저장됩니다. 인스턴스별 데이터만 각 객체에 저장하여 메모리를 절약합니다.
마치며
이번 글에서는 Prototype Pattern 트러블슈팅 가이드에 대해 알아보았습니다. 총 10가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#JavaScript #Prototype #ObjectOriented #Inheritance #DesignPattern