이메일 보기
Javascript

프로토타입(ProtoType) 알아보기

최종 수정일2024년 8월 2일

예전에 같이 일하던 프론트엔드 개발자님께 ‘프로토타입이 뭔지 아시나요?’란 질문을 받은 적이 있는데요. 머리 속에 둥둥 떠오르는 내용들을 말로 설명하기 어려웠던 기억이 납니다. 공부를 하다보면 강의를 듣거나 문서를 읽은 것만으로 이해했다고 착각하기 쉬운 것 같습니다. 머리로 어렴풋하게 알고 있는 것과 실제로 다른 사람에게 말로 설명하는 것에는 아주 큰 차이가 있는데 말이죠. 오늘은 제가 알고 있다고 착각했던 프로토타입에 대해 정리해보겠습니다.

프로토타입이란?


사전적 의미의 프로토타입은 원래의 형태 또는 전형적인 예, 기초 또는 표준입니다. 원래의 형태, 즉 원형에 주목하여 자바스크립트에서의 프로토타입을 정의하면 특정 객체의 원형이 되는 부모 객체라고 할 수 있습니다. 자바스크립트에서 대부분의 객체는 원형 객체, 즉 프로토타입을 가지게 되며 프로토타입에 있는 프로퍼티와 메서드를 상속 받아 사용할 수 있게 됩니다.


javascript
const obj = {};

console.log(obj)

console.log(obj.[[Prototype]]);
// SyntaxError: Unexpected token '['
console.log(Object.getPrototypeOf(obj))

obj의 프로토타입

객체 리터럴을 통해 빈 객체 obj를 만들고 콘솔을 통해 확인해보면 [[prototype]]에 Object 프로토타입이 존재하는 것을 확인할 수 있습니다. 또한 프로토타입에 정의된 메서드를 사용할 수도 있죠.


다만 직접 프로토타입에 접근하려고 할 때 obj.[[Prototype]]으로는 접근할 수 없고 Object.getPrototypeOf() 메서드를 사용하여 객체의 프로토타입에 접근할 수 있습니다.



프로토타입을 사용하는 이유


자바스크립트는 객체 지향 프로그래밍 언어입니다. 객체 지향 프로그래밍이란 데이터를 객체 단위로 묶고, 객체 간의 상호작용을 통해 프로그램을 구성하는 방식을 말하는데요. 객체 지향 프로그래밍의 특징으로는 각 객체 간의 공통된 속성(프로퍼티)과 기능(메서드)을 추려 정의하는 추상화가 있습니다.


아이돌 그룹 에스파를 예로 들어볼까요? (사심 1000%)


에스파 각 멤버들의 공통된 속성을 떠올려봅시다. 멤버들은 이름, 포지션(메인보컬, 메인 댄서…)과 같은 속성(프로퍼티)을 가지고 있을 것이고 랩, 댄스, 노래와 같은 공통된 기능(메서드)을 수행합니다. 이렇게 공통된 특징을 추리고 정의하는 것을 추상화라고 합니다.


javascript
function Idol(name, role) { 
// 각 멤버들의 공통된 속성과 기능을 정의 (추상화)
	this.name = name;
    this.role = role;
    this.rap = () => {
		console.log(`${this.name}이(가) 랩을 한다.`);
    };
    this.dance = () => {
	    console.log(`${this.name}이(가) 춤을 춘다.`);
    };
    this.sing = () => {
	    console.log(`${this.name}이(가) 노래를 한다.`);
    };
}

// Idol 생성자 함수를 통해 멤버(instance) 생성
const karina = new Idol("카리나", "메인 댄서"); 
const winter = new Idol("윈터", "메인 보컬");
const ningning = new Idol("닝닝", "리드 보컬");
const giselle = new Idol("지젤", "메인 랩퍼");

console.log(karina);
console.log(winter);
console.log(ningning);
console.log(giselle);


프로토타입 체이닝

이렇게 각 멤버들의 공통된 프로퍼티와 메서드를 추상화하여 Idol 생성자 함수를 만들고 생성자 함수를 통해 멤버(인스턴스)들을 만들 수 있게 되었습니다.


그런데 name과 role처럼 인스턴스마다 고유한 값을 가지는 경우가 아닌 항상 동일한 기능을 수행하는 rap, dance, sing을 살펴봅시다. 완전히 같은 기능을 수행하는 메서드가 각 인스턴스 마다 포함되어 있는데요. 이렇게 되면 생성자 함수를 통해 만들어진 인스턴스의 수만큼 같은 코드가 중복으로 생성되게 됩니다. 실제로 각 메서드는 완전히 동일하지만 각각 중복으로 메모리에 저장되어있기 때문에 값을 비교하면 false가 나오게 되는 것을 알 수 있습니다.


javascript
console.log(karina.dance === winter.dance); // false

이처럼 인스턴스가 생성될 때마다 같은 코드가 중복으로 생성되어 메모리에 저장된다면 프로그램의 성능이 저하될 수 있겠죠.


이런 불필요한 중복 방지를 위해 필요한 것이 바로 상속입니다. 상속이란 특정 객체의 속성이나 프로퍼티를 다른 객체가 그대로 이어받아 사용할 수 있는 것을 말합니다. 그리고 자바스크립트는 바로 프로토타입을 사용하여 상속을 구현합니다.


javascript
function Idol(name, role) {
	this.name = name;
    this.role = role;
}

Idol.prototype.rap = () => {
    console.log(`${this.name}이(가) 랩을 한다.`);
};
Idol.prototype.dance = () => {
	console.log(`${this.name}이(가) 춤을 춘다.`);
};
Idol.prototype.sing = () => {
	console.log(`${this.name}이(가) 노래를 한다.`);
};

const karina = new Idol("카리나", "메인 댄서"); 
const winter = new Idol("윈터", "메인 보컬");

console.log(karina.dance === winter.dance); //true

생성자 함수를 통해 생성된 인스턴스는 해당 생성자 함수의 prototype 프로퍼티를 프로토타입으로 가지는데요. (저는 예전에 karina의 프로토타입이 Idol이라고 착각한 적이 있는데 Idol은 생성자함수인 것이고 Idol.prototype이 karina의 프로토타입이 됩니다.)


위와 같이 Idol.prototype을 통해 공통된 메서드를 정의하게 된다면 이제 모든 인스턴스들은 프로토타입에 정의된 메서드를 상속받아 사용할 수 있게 되고 더이상 여러번의 불필요한 코드 중복을 발생시키지 않게 됩니다. karina.dance와 winter.dance를 비교해도 true가 나오는 것을 알 수가 있습니다.


이처럼 프로토타입은 자바스크립트의 객체 지향 프로그래밍에서 상속을 구현할 수 있게 하며 자바스크립트가 프로토타입 기반의 객체 지향 언어라고 불리는 이유이기도 합니다.


상속 구현하기


이제 우리는 프로토타입이 무엇인지 프로토타입을 왜 사용하는지에 대해 알게 되었습니다. 그럼 이제 프로토타입을 이용하여 상속을 구현해봅시다.


javascript
function Singer(name){
	this.name = name;
}
      
Singer.prototype.sing = function () {
	console.log(`${this.name}이(가) 노래를 한다.`);
};

function Idol(name, role) {
  Singer.call(this, name); // Singer 생성자 함수를 호출
	this.role = role;
}

Idol.prototype = Object.create(Singer.prototype); // prototype 연결

Idol.prototype.rap = function () {
	console.log(`${this.name}이(가) 랩을 한다.`);
};

Idol.prototype.dance = function () {
	console.log(`${this.name}이(가) 춤을 춘다.`);
};

const karina = new Idol("카리나", "메인 댄서");

karina.dance();


  1. Object.create('Signer.prototype') 을 사용하여 새 객체를 생성합니다. 이때 첫번째 인자에 들어가는 값이 생성되는 객체의 프로토타입이 됩니다. 즉 Idol.prototype은 Singer.prototype을 프로토타입으로 갖게 됩니다. (프로토타입탈트 붕괴..)
  2. Idol 생성자 함수에서 Singer 함수를 호출합니다. 이때 call 메서드를 사용하여 this와 인자값을 전달합니다.

이제 Singer.prototype을 상속받아 karina.sing()를 호출할 수 있습니다!


프로토타입 체이닝


위 예제에서 karina 객체 내부에 dance 메서드가 없어도 실행시킬 수 있었는데요. 이는 객체에서 프로퍼티를 찾을 때 객체 내부에 해당 프로퍼티가 없다면 그 객체의 프로토타입을 확인하며 계속 위로 올라가면서 찾는 과정인 프로토타입 체이닝이 있기 때문입니다. 이를 통해 자바스크립트는 상속을 구현하게 됩니다. 그리고 프로토타입 체이닝의 끝에는 최상위 프로토타입인 Object.prototype이 있습니다. 즉 거의 모든 자바스크립트 객체는 Object.prototype를 상속 받게 됩니다. (맨 처음 프로토타입을 정의할 때 예시코드에서도 빈 객체인 user에 Object 프로토타입이 있었죠?)


상속 확인 하기


이렇게 상속이 이어지다 보면 특정 인스턴스가 어떤 생성자 함수를 통해 만들어졌는지 헷갈릴 수도 있을텐데요. 이럴때는 instandeOf 연산자를 사용하여 확인하면 됩니다. 인스턴스 instanceof 생성자 생성자 함수를 통해 생성된 인스턴스이면 true, 아니라면 false를 리턴합니다.


javascript
console.log(karina instanceof Singer)// true;
console.log(karina instanceof Idol) //true;
console.log(karina instanceof Actor) //false

오버라이딩


같은 프로토타입을 공유하고 있는 객체 중 하나의 객체에서만 프로퍼티나 메서드를 수정하고 싶을 땐 어떻게 해야할까요? 이를 위해 프로토타입에 정의된 메서드를 하위 객체에서 재정의하는 오버라이딩에 대해 알아봅시다.


에스파의 메인보컬인 닝닝은 주로 고음을 담당합니다. 닝닝의 sing 메서드를 수정해봅시다.


javascript
ningning.sing = ()=>{
	console.log(`${this.name}은(는) 고음을 부른다.`);
}

ningning.sing(); //닝닝은(는) 고음을 부른다.
giselle.sing(); //지젤이(가) 노래를 부른다.


ningning.sing을 재정의하여 ningning.sing이 오버라이딩 되었습니다. 하지만 giselle.sing은 기존의 메서드를 유지하고 있죠.


Idol의 instance

이것은 ningning.sing을 오버라이딩 할 때 프로토타입의 메서드를 수정한 것이 아니라 ningning 인스턴스에 메서드로 추가된 것이기 때문입니다. 이처럼 인스턴스 프로퍼티(프로퍼티와 메서드를 모두 포함)에 의해 프로토타입 프로퍼티가 가려지는 현상을 프로퍼티 섀도잉이라고 합니다.


포스팅을 마치며


여기까지 프로토타입의 의미, 왜 프로토타입이 사용되는지, 프로토타입을 통한 상속 구현에 대해 알아보았습니다. (더 상세한 내용을 알고 싶으시다면 자바스크립트 딥다이브 책을 추천드려요. 무려 50 페이지에 걸친 방대한 내용이 여러분을 기다리고 있습니다.)


사실 요즘에는 Class 문법을 사용하는 추세지만 그래도 그 근간에는 프로토타입이 있기 때문에 자바스크립트를 사용하는 개발자라면 프로토타입에 대해 알아두는 것이 중요할 것 같습니다.


포스팅 시작 전에 말했듯 프로토타입에 대해 어렴풋이 알고 있었지만 말로 설명하기가 어려웠는데요. 일단 프로토타입에 대해 완전히 이해하지 못한 상태였고 또 너무 방대한 내용이여서 어디부터 어디까지 이야기 해야할지 몰랐던 것 같기도 합니다. 하지만 이번 기회를 통해 프로토타입에 대해 명확히 알 수 있었고 자바스크립트 상속 매커니즘을 엿볼 수 있어서 재밌었습니다. 가끔 인스턴스, 프로토타입탈트 붕괴 현상이 찾아오긴 했지만.. 글을 읽으시는 분들도 많이 헷갈리실 것 같아서 걱정이 듭니다. 긴 글 읽어주셔서 감..사건은 다가와 Oh Eh 👻

게시글의 오류 지적, 내용 보충, 질문 등의 피드백은 언제나 환영입니다.
아래 댓글창 혹은 ysisys0202@gmail.com으로 남겨주세요.