Frontend

[Frontend] 프로토타입 알아보기

teddy bear 2026. 7. 15. 14:53
반응형

자바스크립트 프로토타입, 정말 이해하고 계신가요?

class 키워드에 익숙해진 요즘, 프로토타입은 "예전에 쓰던 문법" 정도로 여겨지곤 합니다. 하지만 자바스크립트 엔진 내부에서는 지금 이 순간에도 프로토타입 체인이 쉼 없이 동작하고 있습니다. 면접에서 유독 이 주제가 자주 나오는 이유도 여기에 있습니다 — "자바스크립트를 얼마나 깊이 이해하고 있는가"를 가장 효율적으로 확인할 수 있는 질문이기 때문입니다.

이 글에서는 프로토타입이 왜 존재하는지부터, 프로토타입 체인이 실제로 어떻게 동작하는지, 그리고 class 문법이 이것을 어떻게 감싸고 있는지까지 순서대로 정리해보겠습니다.


1. 프로토타입은 왜 필요했을까

자바스크립트에는 원래 클래스가 없었습니다. 대신 객체가 다른 객체를 참조해서 속성과 메서드를 "빌려 쓰는" 방식을 택했는데, 이게 바로 프로토타입 기반 상속입니다.

function Person(name) {
  this.name = name;
}

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

const kim = new Person("김철수");
kim.sayHello(); // "안녕하세요, 김철수입니다."

여기서 kim 객체 자체에는 sayHello라는 메서드가 없습니다. 그런데도 호출이 됩니다. 왜일까요?

kim.sayHello()를 호출하면 자바스크립트 엔진은 다음 순서로 탐색합니다.

  1. kim 객체 자신에게 sayHello가 있는지 확인
  2. 없다면 kim의 프로토타입(Person.prototype)에서 확인
  3. 거기에도 없다면 그 프로토타입의 프로토타입으로 계속 이동
  4. Object.prototype까지 가서도 없으면 undefined

이 연결 고리가 바로 프로토타입 체인입니다.


2. prototype과 __proto__, 헷갈리는 두 단어 정리

이 두 단어를 혼용해서 쓰다가 개념이 꼬이는 경우가 정말 많습니다. 역할이 완전히 다릅니다.

구분 역할 존재하는 대상

prototype 함수가 new로 생성할 인스턴스에게 물려줄 "설계도" 함수(정확히는 생성자 함수)에만 존재
__proto__ 실제로 인스턴스가 참조하고 있는 프로토타입 객체 모든 객체에 존재
console.log(kim.__proto__ === Person.prototype); // true
console.log(Person.prototype.constructor === Person); // true

즉, kim.__proto__는 Person.prototype을 가리키는 실제 연결 통로이고, Person.prototype은 그 통로 끝에 있는 객체입니다. 참고로 __proto__는 표준화 이전부터 쓰이던 관습적 프로퍼티라, 요즘은 Object.getPrototypeOf(kim)을 쓰는 것이 권장됩니다.


3. 프로토타입 체인을 눈으로 그려보면

kim
 └─ __proto__ → Person.prototype
                  ├─ sayHello()
                  └─ __proto__ → Object.prototype
                                    ├─ toString()
                                    ├─ hasOwnProperty()
                                    └─ __proto__ → null

체인의 끝은 항상 null입니다. 여기까지 가도 못 찾으면 자바스크립트는 더 이상 찾을 곳이 없다고 판단하고 undefined를 반환합니다.

실무에서 자주 겪는 실수 하나를 예로 들어보겠습니다.

for (const key in kim) {
  console.log(key);
}
// name, sayHello 둘 다 출력됨

for...in은 프로토타입 체인을 타고 올라가며 열거 가능한(enumerable) 모든 프로퍼티를 순회합니다. 그래서 kim 자신의 속성뿐 아니라 Person.prototype에 있는 sayHello까지 나옵니다. 배열이나 객체를 순회할 때 의도치 않게 상속받은 속성까지 딸려 나오는 버그는 대부분 이 지점에서 발생합니다. 이런 이유로 실무에서는 Object.keys()나 hasOwnProperty 체크를 함께 쓰는 경우가 많습니다.


4. class는 사실 프로토타입 위에 그려진 문법

ES6에서 등장한 class는 완전히 새로운 상속 모델이 아니라, 프로토타입 기반 상속을 좀 더 익숙한 형태로 표현한 **문법적 설탕(syntactic sugar)**입니다.

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(); // "초코이(가) 짖습니다."

이걸 내부적으로 풀어보면 다음과 동일합니다.

function Animal(name) {
  this.name = name;
}
Animal.prototype.speak = function () {
  console.log(`${this.name}이(가) 소리를 냅니다.`);
};

function Dog(name) {
  Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.speak = function () {
  console.log(`${this.name}이(가) 짖습니다.`);
};

extends가 하는 일은 결국 Dog.prototype.__proto__를 Animal.prototype으로 연결해주는 것뿐입니다. class가 더 안전하고 읽기 쉬운 문법을 제공하는 건 맞지만, 그 아래에서 돌아가는 원리는 여전히 프로토타입 체인이라는 걸 이해하고 있으면 super, static, 오버라이딩 같은 개념도 훨씬 자연스럽게 받아들여집니다.


5. 왜 실무에서 이걸 알아야 할까

이론으로만 남겨두기 아까운 개념이라, 실제로 도움이 되는 순간들을 몇 가지 짚어보겠습니다.

메모리 효율: 메서드를 인스턴스마다 만들지 않고 프로토타입에 하나만 두면, 인스턴스가 수천 개 생성되어도 메서드는 메모리에 단 하나만 존재합니다. this.method = function() {...}처럼 생성자 내부에 메서드를 정의하면 이 이점을 잃게 됩니다.

디버깅: console.log로 객체를 찍었을 때 [[Prototype]]에 뭐가 걸려 있는지 이해하면, "이 메서드는 대체 어디서 온 거지?"라는 순간에 훨씬 빠르게 원인을 추적할 수 있습니다.

라이브러리 코드 읽기: 오래된 라이브러리나 폴리필 코드는 여전히 prototype을 직접 조작하는 경우가 많습니다. 이 개념 없이는 그런 코드를 읽어내기 어렵습니다.


6. 면접에서 자주 나오는 질문 정리

준비하시는 분들을 위해 실제로 자주 등장하는 질문 유형을 정리해봤습니다.

  • prototype과 __proto__의 차이를 설명해달라
  • 프로토타입 체인의 끝에는 무엇이 있는가
  • class 문법이 내부적으로 어떻게 동작하는지 설명해달라
  • Object.create()와 new 키워드의 차이는 무엇인가
  • 프로토타입 오염(prototype pollution)이 무엇이고 왜 보안 이슈가 되는가

특히 마지막 질문은 최근 시니어 포지션 면접에서 자주 등장합니다. Object.prototype에 악의적인 속성을 주입해 애플리케이션 전역에 영향을 주는 공격 기법인데, 프로토타입 체인의 동작 원리를 모르면 이 개념 자체를 이해하기 어렵습니다.


마무리하며

프로토타입은 화려한 최신 문법은 아니지만, 자바스크립트라는 언어의 뼈대를 이루는 개념입니다. class, extends, super 같은 익숙한 키워드들도 결국 이 뼈대 위에서 동작한다는 걸 이해하고 나면, 코드를 읽는 시야 자체가 달라집니다.

다음 글에서는 이 개념을 확장해서 Object.create()를 활용한 순수 프로토타입 패턴과, 실무에서 자주 마주치는 프로토타입 오염 이슈를 좀 더 깊게 다뤄보겠습니다.