자바스크립트의 비밀스러운 삶: 프로토타입 이해하기

발행: (2025년 12월 11일 오후 01:07 GMT+9)
6 min read
원문: Dev.to

Source: Dev.to

객체는 위임하고, 복사하지 않는다

티모시가 노트북을 들고 도서관에 도착했지만 표정이 어리둥절했다. 그는 JavaScript 상속에 대해 읽고 있었는데, 뭔가 맞지 않았다.

Timothy: “JavaScript가 상속에 프로토타입을 사용한다는 걸 배웠어요. 그런데 이제는 클래스도 있다고 들었어요. 그럼 어느 쪽인가요? JavaScript는 프로토타입을 쓰나요, 아니면 클래스를 쓰나요?”
Margaret: “두 다. 그리고 어느 쪽도 아니야. 그보다 더 미묘해.”

상속이란 무엇인가?

Timothy: “자식 클래스가 부모 클래스를 상속받으면, 부모의 모든 속성과 메서드를 얻게 돼요.”
Margaret: “Python이나 Java 같은 언어에서는 그렇지. 자식 클래스가 부모의 동작을 복사하거나 포함해. 하지만 JavaScript는 위임을 사용해.”

위임 vs. 복사

속성을 복사하는 대신, JavaScript의 객체들은 다른 객체를 가리키며 이렇게 말한다: “내게 원하는 것이 없으면, 저쪽 객체에게 물어봐.”

간단한 예시

const parent = {
  greet: function() {
    console.log("Hello, " + this.name);
  }
};

const child = Object.create(parent);
child.name = "Alice";

child.greet(); // "Hello, Alice"
  • Object.create(parent) 은 숨겨진 [[Prototype]] 링크가 parent 를 가리키는 새 객체를 만든다.
  • child.greet() 가 호출되면, JavaScript 는 먼저 child 에서 greet 를 찾는다. 없으면 child 의 프로토타입(parent) 에서 찾는다. 찾으면 thischild 로 바인딩해 실행한다.

같은 프로토타입을 공유하는 여러 객체

const parent = {
  greet: function() {
    console.log("Hello, " + this.name);
  }
};

const alice = Object.create(parent);
alice.name = "Alice";

const bob = Object.create(parent);
bob.name = "Bob";

alice.greet(); // "Hello, Alice"
bob.greet();   // "Hello, Bob"

alicebob 은 동일한 parent 객체에 위임하지만, greet 내부의 this 는 호출한 객체를 가리킨다.

[[Prototype]] vs. .prototype

숨겨진 링크 [[Prototype]]

const obj = Object.create(parent);
// obj.[[Prototype]] points to parent (invisible)

함수에 있는 .prototype 프로퍼티

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

// User.prototype is a regular property on the User function
console.log(User.prototype); // { constructor: User }

new 사용하기

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

User.prototype.greet = function() {
  console.log("Hello, " + this.name);
};

const alice = new User("Alice");
alice.greet(); // "Hello, Alice"

new User("Alice") 가 실행될 때 JavaScript 는:

  1. 완전히 새로운 객체를 만든다.
  2. 그 객체의 [[Prototype]]User.prototype 으로 설정한다.
  3. User 함수를 this 가 새 객체가 되도록 호출한다.
  4. 새 객체를 반환한다.

링크를 확인할 수 있다:

console.log(Object.getPrototypeOf(alice) === User.prototype); // true

Object.getPrototypeOf() (또는 레거시 __proto__) 는 보이지 않는 프로토타입 체인을 드러낸다.

클래스: 프로토타입 위에 얹은 문법 설탕

앞 예시의 클래스 버전

class User {
  constructor(name) {
    this.name = name;
  }

  greet() {
    console.log("Hello, " + this.name);
  }
}

const alice = new User("Alice");
alice.greet(); // "Hello, Alice"
  • 클래스 문법은 내부적으로 동일한 프로토타입 체인을 만든다.
  • 클래스 안에 정의된 메서드(greet) 는 User.prototype 에 배치된다.
  • new User() 는 여전히 [[Prototype]]User.prototype 을 가리키는 객체를 만든다.

왜 “문법 설탕”인가?

이 용어는 문법이 다르게 보이고 보통 더 명확하지만, 근본적인 메커니즘—프로토타입 위임—은 변하지 않는다는 뜻이다. 클래스는 extendssuper() 같은 편리한 키워드를 제공해 프로토타입 체인을 자동으로 처리해 주지만, 새로운 상속 모델을 도입하는 것은 아니다.

요약

  • JavaScript 객체는 숨겨진 [[Prototype]] 링크를 통해 다른 객체에 위임한다.
  • 생성자 함수에 있는 .prototype 프로퍼티는 new 로 만든 객체들의 숨겨진 링크를 설정하는 방법이다.
  • Object.getPrototypeOf() (또는 __proto__) 로 프로토타입 체인을 검사할 수 있다.
  • ES6 클래스는 같은 프로토타입 기반 패턴을 더 깔끔하게 작성할 수 있게 해 주는 것이며, 프로토타입을 대체하지 않는다.
Back to Blog

관련 글

더 보기 »