들어가기 전에

자바스크립트 개발자라면 알아야 할 33가지 개념 #17 자바스크립트 : 클래스 vs 프로토타입

댓글을 보다 충격적인 문장 하나를 발견하여 이 글을 쓰게 됐습니다.

*"ECMA Script 2015 표준부터 순수한 자바스크립트도 올바른 클래스를 갖게 되었습니다."*

음.... 틀렸습니다. 그리고 왜 인지 알려드리겠습니다.

'class' 키워드가 존재하기 전

우리가 자바스크립트에서 'classes' 를 구현하는 예제입니다. 이렇게 구현하던 시절도 사실 그렇게 오래전은 아닙니다. 첫번째 함수는 constructor 일 겁니다. Cat 클래스를 만들고, 여기에 meow 함수를 추가할 겁니다.

cat-class.jpeg

키워드 new를 이용하여 cat을 만들었습니다.

만일 new 키워드를 까먹는다면 어떻게 될까요? 아무런 에러도 나지 않습니다. 그냥 최상위 오브젝트인 window 오브젝트의 name 프로퍼티만 수정됩니다.

withoutnewkeyword.jpeg

this가 참조하는 방식의 문제입니다.

프로토타입은 어디에 존재하나요?

콘솔에서, 프로토타입 체인을 볼 수 있습니다.

prototype-chain.jpeg

cat을 갖고 있는 녀석입니다.

여러분이 만든 cat은 name을 가진 오브젝트입니다. 또, meow 함수를 가지고 Cat 생성자 함수를 가진 proto 라는 오브젝트를 갖고 있습니다. 이 오브젝트/프로토타입은 또한 자바스크립트 오브젝트 중에 최상위 오브젝트인 Object도 갖고 있습니다. (자바를 해보셨다면 최상위 오브젝트 개념이 무엇인지 아실 겁니다. 모든 오브젝트는 최상위 오브젝트를 상속하게 됩니다.) 아마 여기까지 잘 이해하셨다면 프로토타입 체인이 왜 프로토타입 체인(chain) 인지 이해하셨을 겁니다.

delete cat.__proto__.meow

콘솔에 위와 같은 코드를 입력하시면, 모든 고양이의 meow() 메소드를 삭제하게 됩니다. 왜냐하면 모든 고양이가 같은 프로토타입에 대한 참조를 공유하기 때문입니다.

이제 Dog 클래스를 만들어봅시다. cat과 같은데 meow() 메소드 대신 bark() 메소드를 넣을 겁니다.

dog-class.jpeg

이제 dog와 cat이 하나씩 있습니다. 이제 진짜 심각하게 금지된 나쁜 코드를 실행할 겁니다. 일할 때는 절대로 이런 코드를 실행하지 마세요.

cat.__proto__ = dog.__proto__

cat 안에 무슨 일이 일어나는지 봅시다.

mutantcat.jpeg

오 이런! cat이 이젠 짖을(bark) 수 있게 됐습니다. 아직 Cat이지만 프로토타입은 Dog의 프로토타입을 갖고 있습니다.

이제 어떻게 자바스크립트에서 모든 것들이 오브젝트의 프로토타입에 연결되는지 이해되기 시작할 겁니다.

보셨죠? 이게 자바스크립트가 미친 이유 중 하나입니다. 여러분은 아무런 행위나 할 수 있습니다. 심지어 단순히 프로토타입만 바꿔버리면 cat이 bark하게 할 수도 있습니다. 문제는 확실히 이러한 자율성에 있습니다. 그래서 많은 실수가 일어날 수 있습니다.

이제 class를 써봅시다

MDN에 따른 자바스크립트에서의 클래스의 정의부터 다시 한번 짚고 넘어갑시다.

자바스크립트 클래스는 ECMAScript 2015에서 도입된 현재 존재하는 자바스크립트의 프로토타입 기반 상속을 다루는 문법적 부가기능입니다. 클래스 문법은 새로운 객체지향 상속 모델을 자바스크립트에 도입하진 않습니다.

만일 위의 글을 읽었을 때 분명하게 이해되지 않는다면, 왜 새로운 객체지향 모델을 자바스크립트에 도입하지 않았다고 했는지 보여드리겠습니다. 얼마나 객체지향 같지 않은지 한번 보세요.

클래스 문법을 써서 cat과 dog의 예제를 계속해봅시다.

supersupercat.jpeg

어떤 OOP 언어를 써서 작성된 것 같은 코드를 얻었습니다. class, constructor, super, extends 그리고 심지어 재미로 static 함수까지 추가했습니다. 하지만 우리의 superCat은 어떤 모양인지 한번 볼까요?

super-cat2.jpeg

확실히 좀 더 큰 고양이입니다.

우리는 extends 키워드를 사용했습니다. extends의 의미는 사실 우리가 프로토타입 체인에 깊이를 추가했다는 사실입니다. 지금 우리는 name과 superpower를 가진 superCat 오브젝트를 갖고 있습니다. superCat 오브젝트는 물론 SuperCat 생성자를 가진 __proto__ 오브젝트도 갖고 있고 SuperCat 메소드 meow()까지 갖고 있습니다.

이것이 갖는 의미는 사실 아까 class를 쓰지 않았을 때와 같습니다. 프로토타입의 프로퍼티나 메소드를 삭제하는 것이 가능합니다. 그리고 그렇게 한다면 우리가 생성했던 모든 인스턴스는 망가질 것입니다. 아무것도 변하지 않은 것 같지만 class를 사용함으로써 무언가 변했습니다.

withoutnewkeywordsueprcat.jpeg

이걸 보려고 우린 20년을 기다려야 했습니다.

우리가 이전에 했던 것처럼 superCat의 프로토타입을 dog의 프로토타입으로 바꿔보세요. 아니면 아무 미친짓이나 해보시기 바랍니다. 프로토타입 세상은 여러분의 것입니다.

catproto.jpeg

클래스 키워드 뒤에는 여전히 예전과 같은 프로토타입 접근법이 있다는 것을 이해했을 겁니다. 자바스크립트 클래스 코드를 사용할 때 이 점을 알아두시기 바랍니다.