frontend/javascript

🟨 2-19. 프로토타입(Prototype) 완벽 가이드 — 자바스크립트 객체지향의 뼈대 구조 이해하기

mirabo01 2025. 11. 7. 08:54

1. 프로토타입이란 무엇인가

자바스크립트는 클래스 기반 언어(C++, Java)와 달리
프로토타입 기반(Prototype-based) 언어다.

즉, 모든 객체는 다른 객체를 상속(prototype link) 받아서 동작한다.

“모든 객체는 자신의 부모 역할을 하는 원형(Prototype)을 가진다.”

const user = { name: "기범" };
console.log(user.__proto__); // Object.prototype

✅ 모든 객체는 내부적으로 __proto__ (또는 [[Prototype]])를 갖는다.
✅ 이걸 통해 상위 객체의 속성과 메서드를 참조할 수 있다.


2. 프로토타입 체인(Prototype Chain)

프로토타입 체인은 상속의 연결 고리다.
자바스크립트는 객체의 프로퍼티를 찾을 때 다음 순서로 탐색한다.

1️⃣ 객체 자신
2️⃣ 상위 프로토타입 (__proto__)
3️⃣ 최상위 Object.prototype
4️⃣ 없으면 undefined 반환

const user = { name: "기범" };
console.log(user.toString()); // [object Object]

✅ toString()은 user 객체에 없음
✅ 따라서 상위 Object.prototype 에서 찾는다


3. 함수와 프로토타입의 관계

모든 함수는 생성자 함수가 될 수 있다.
즉, new 키워드로 호출하면 프로토타입을 기반으로 한 객체가 만들어진다.

function User(name) {
  this.name = name;
}
User.prototype.sayHi = function () {
  console.log(`안녕, 나는 ${this.name}`);
};

const kb = new User("기범");
kb.sayHi(); // 안녕, 나는 기범

✅ kb는 User의 인스턴스
✅ User.prototype에 정의한 메서드는 모든 인스턴스가 공유한다.


4. 인스턴스 구조 시각화

function User(name) {
  this.name = name;
}
User.prototype.sayHi = function () {
  console.log(`Hello, ${this.name}`);
};

const kb = new User("기범");

구조:

kb (인스턴스)
│
└── __proto__ → User.prototype
                   │
                   └── __proto__ → Object.prototype

✅ kb.sayHi()를 호출하면 자바스크립트는 다음 순서로 탐색한다.
1️⃣ kb 자체에 sayHi가 있는가? → 없음
2️⃣ User.prototype에 있는가? → 있음 → 실행


5. 프로토타입 체인의 끝

모든 프로토타입 체인의 끝에는 Object.prototype이 있다.
그 위에는 아무것도 없다.

console.log(Object.prototype.__proto__); // null

즉, 자바스크립트의 모든 객체는 궁극적으로 Object를 상속받는다.


6. 프로토타입 상속 확장

기존 객체를 확장하고 싶을 때도 프로토타입을 활용할 수 있다.

function Animal(name) {
  this.name = name;
}
Animal.prototype.move = function () {
  console.log(`${this.name}이(가) 움직인다`);
};

function Dog(name) {
  Animal.call(this, name); // 부모 생성자 호출
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function () {
  console.log(`${this.name}이(가) 짖는다`);
};

const choco = new Dog("초코");
choco.move(); // 초코이(가) 움직인다
choco.bark(); // 초코이(가) 짖는다

✅ Object.create()를 통해 상위 프로토타입을 복제
✅ constructor를 다시 지정해야 올바른 참조 유지


7. 클래스(Class)와의 관계

ES6 이후 등장한 class 문법은 사실 프로토타입의 문법적 설탕(syntactic sugar) 이다.

class User {
  constructor(name) {
    this.name = name;
  }
  sayHi() {
    console.log(`안녕, ${this.name}`);
  }
}

const kb = new User("기범");
kb.sayHi();

위 코드는 내부적으로 이렇게 변환된다 👇

function User(name) {
  this.name = name;
}
User.prototype.sayHi = function () {
  console.log(`안녕, ${this.name}`);
};

✅ 클래스 내부의 메서드는 전부 prototype에 저장된다.
✅ 즉, 클래스도 결국 “프로토타입 상속” 위에서 돌아간다.


8. Object.create()로 직접 프로토타입 지정

Object.create()는 특정 프로토타입을 가진 객체를 직접 생성할 수 있다.

const animal = {
  move() {
    console.log("움직인다");
  },
};

const dog = Object.create(animal);
dog.bark = function () {
  console.log("멍멍!");
};

dog.move(); // 움직인다
dog.bark(); // 멍멍!

✅ dog.__proto__ === animal
✅ 상속 구조를 수동으로 만드는 가장 직관적인 방법


9. 프로토타입 확장의 위험성

프로토타입을 직접 수정하면 전역적인 부작용이 생길 수 있다.

Array.prototype.remove = function (value) {
  const index = this.indexOf(value);
  if (index > -1) this.splice(index, 1);
};
const arr = [1, 2, 3];
arr.remove(2);
console.log(arr); // [1, 3]

겉보기엔 편리하지만,
모든 배열에서 remove가 생기기 때문에
다른 코드나 라이브러리와 충돌할 수 있다.

✅ 그래서 내장 객체의 prototype 수정은 절대 금지!


10. 프로토타입 체인 확인

function User() {}
const kb = new User();

console.log(kb.__proto__ === User.prototype); // true
console.log(User.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true

이 세 줄이 프로토타입 체인의 모든 걸 설명한다.


11. instanceof 연산자

instanceof는 객체의 프로토타입 체인을 검사한다.

function Animal() {}
function Dog() {}
Dog.prototype = Object.create(Animal.prototype);

const choco = new Dog();
console.log(choco instanceof Dog); // true
console.log(choco instanceof Animal); // true
console.log(choco instanceof Object); // true

✅ instanceof는 객체의 실제 클래스보다 “프로토타입 체인”을 기준으로 판별한다.


12. 실무 예제 — 커스텀 객체 시스템

function Person(name) {
  this.name = name;
}
Person.prototype.introduce = function () {
  console.log(`안녕하세요, 저는 ${this.name}입니다.`);
};

function Developer(name, skill) {
  Person.call(this, name);
  this.skill = skill;
}
Developer.prototype = Object.create(Person.prototype);
Developer.prototype.constructor = Developer;

Developer.prototype.code = function () {
  console.log(`${this.name}은 ${this.skill}로 개발 중...`);
};

const kb = new Developer("기범", "React");
kb.introduce(); // 안녕하세요, 저는 기범입니다.
kb.code(); // 기범은 React로 개발 중...

✅ 생성자 + 프로토타입 상속으로 클래스 구조를 구현
✅ ES5 시절 클래스형 설계 패턴의 전형적인 형태


13. 프로토타입의 장점

장점 설명

✅ 메모리 절약 메서드를 인스턴스마다 복제하지 않음
✅ 상속 구현 가능 Object.create, __proto__ 활용
✅ 동적 확장 가능 런타임에 메서드 추가 가능
✅ 클래스 문법보다 유연 함수형 + 객체형 모두 표현 가능

14. 정리

항목 요약

Prototype 객체의 원형(부모 객체)
Prototype Chain 상속 구조
함수의 prototype 속성 인스턴스가 참조하는 공유 메서드 공간
__proto__ 객체의 내부 프로토타입 링크
클래스 문법 프로토타입 기반의 문법적 설탕
주의점 내장 객체 prototype 수정 금지

15. 마무리

프로토타입을 이해하면
자바스크립트의 “객체지향 구조”가 완전히 눈에 보이기 시작한다.

this, 클로저, 프로토타입 —
이 세 가지가 맞물릴 때, 비로소 자바스크립트를 진짜 언어로 이해할 수 있다.

“클로저가 함수의 기억이라면,
프로토타입은 객체의 유전자다.”