프로토타입 언어라고 부르는 이유 with JS

·2022년 7월 6일
1

JavaScript

목록 보기
10/13
post-thumbnail

자바스크립트를 처음 공부할 무렵, 많이 보는 MDN에는 이런 것이 붙어있던 것을 기억한다.

바로 prototype이라는 것이 모든 메소드마다 붙어있었다.

그러면서 난 이게 도대체 무엇일까, 에 대한 고민보다
아. 일단 문제를 풀어야하는데... 라면서 넘어갔던 기억이 난다.

하지만 찜찜한 마음은 있었다. 나는 Array를 선언만 했을 뿐인데, 어떻게 다양한 메소드 들을 사용할 수 있는걸까?

그렇게 공부를 이어가며 시간이 흐른 후, 객체지향이라는 단어에 대하여 알게 되었다.
절차지향, 함수지향, 객체지향 등등 프로그래밍 언어에는 다양한 것을 지향하는 언어들이 존재한다.

그 중 내가 공부를 하던 자바스크립트는 프로토타입 객체지향 언어라고 불린다는 것을 보게 되었다.
그리고 이것은 진정한 객체지향이 아니라, 그저 짝퉁 객체지향으로 부르는 사람도 봤다.

도대체 객체지향이란 무엇이길래, 프로토타입은 뭐길래 라는 생각으로 개발공부를 시작한지 한달도 되지 않았던 시기에 읽었던 글이 있었다.

바로 자바스크립트는 왜 프로토타입을 선택했을까. 라는 글이였다.

클릭하시면 해당 글로 넘어갑니다.

그러나 저 당시에는 이해가 가지 않았다.
하지만 그것은 알 수 있었다, 객체지향이라는 것이 무엇인가?에 대한 큰 그림을 그려볼 수 있었던 것 같다.

객체지향이란 무엇인가?

객체지향이라는 것은, 현실세계를 모방해서 새로운 세계를 구축하는 것을 이야기한다.

제일 근본에 가까운 것을 본을 떠놓은 후, 그것에 살을 붙여나가는 것이였다.
왜 본을 떠서 거기에 붙이냐면, 이미 구축되어있는 것이 있기 때문에 새로 만들지말고 재사용을 하자는 것이었다.

객체지향이 뭘 하려는지는 이제 조금 알 것 같다.

객체지향에서 지향하는 것이 무엇인가?

그럼 굳이 프로토타입이라는 용어는 왜 붙었으며, 프로토타입은 도대체 무엇일까?

객체지향 프로그래밍의 특징

일단 객체지향 프로그래밍의 특징부터 한번 훑어보고 가려고 한다.
A랑 B를 둘 다 알아야 비교를 할 수 있으니 말이다..


객체지향은 객체라는 집합으로 프로그램을 표현하려는 패러다임이다.

객체지향 프로그래밍은 서양철학을 기반으로 현실세계에 존재하는 물건을 가상공간에 복제품을 놓는 것으로 시작됬는데

실체를 어떠한 것으로 표현을 하려는 것을 속성(property)라고 표현을 한다.
또한 실체에는 다양한 속성들이 존재하는데, 필요한 속성만을 추려내서 표현하는 것을 추상화(abstraction)라고 한다.

이렇게 다양한 속성을 가진 것을 하나의 단위로 구성하여 복합적인 자료구조객체라고 이야기하는데,
독립적 객체의 집합을 활용하려는 것이 주된 목적이다.

객체지향을 설명할 때 주로 나오는 것들이 있는데. 그것은 아래와 같다.

  • 상태
    • 상태란 현재 객체의 데이터를 이야기한다. 자신이 정의해놓은 객체가 상태라고 할 수 있다.
  • 동작
    • 동작이란 객체의 상태를 조작할 수 있는 것을 이야기한다. 반복문이라던가 조건문 같은 것이다.
  • 메소드
    • 메소드란 객체의 상태를 동작할 수 있는 명령을 뜻한다.
  • 메세지
    • 객체와 객체간은 서로 독립된 상태이기 때문에 메세지라는 매개체를 통하여 메소드를 통해 객체의 상태를 바꿀 수 있다.
  • 상속
    • 특정 객체의 프로퍼티 혹은 메소드를 그대로 상속받아 사용할 수 있는 것을 이야기한다.

여기서 객체지향은 객체라는 것을 통하여 뭐를 하겠다는 것을 알겠고 재사용을 하는 것이 특징이라는 것도 알 것 같다.

한번 아래의 코드결과를 보면서 다시 이야기를 해보자.

  • 몬스터는 레벨과 생명력이라는 프로퍼티를 가지고 있고.
  • create라는 메소드를 사용하여 새로운 몬스터를 만들어낼 수 있다.

그런데 2마리의 몬스터를 생성하기 위하여 사용하는 create 메소드가 서로 다르다고 결과값으로 false를 띄워주고 있다.

만약 1000마리의 몬스터를 생성하려고 한다면, 같은 동작을 하는 메소드가 천개나 존재하는 것이고 결국은 메모리의 낭비로 이어진다.

그림으로 보면 아래와 같이 표현할 수 있다.

그래서 이것을 개선하기 위하여 자바스크립트에서 사용하는 것이 바로 프로토타입이다.

잠깐! 생성자 함수가 도대체 뭐에요?

그냥 넘어가려고 했다가, 이건 설명을 하고 가야할 것 같아서 적는 챕터다.

우리는 일반적으로 객체를 생성할 때 const obj = {happy:"빵"} 이런식으로 구현을 했을 것이다.
이러한 방식을 객체 리터럴에 의한 객체 생성이라는 표현을 사용한다.

하지만 만약에 동일한 프로퍼티로 수백, 수천개의 객체를 생성해야한다면 어떻게 할 것인가?

그럴 때 아래처럼 작성하는 것은, 과거 C언어로 별찍으라고 할 때 하드코딩을 하는 것과 동일하다.
const bread1 = {happy:"빵1"}
const bread2 = {happy:"빵2"}
....
const bread100 = {happy:"빵100"}

쓸때없는 반복을 사용하지 않기 위하여 사용하는 것이 생성자 함수라는 것이다.

클래스쓰면 되잖아요? 왜 굳이 저런걸 써야해요? 라고 물어본다면

프로토타입의 JS에서는 원래 이렇게 사용하는 것이 맞다.
단지 ES6에 익숙한 클래스가 도입이 되면서 클래스를 많이 써서 그렇다.

JS에서 구현되어있는 프로토타입

그럼 이제 정말로 어떻게 구현이 되는지, 객체지향의 문제를 프로토타입으로 풀어낸 코드를 봐보자.

뭔가 prototype이라는 라인이 추가되었더니 create 메소드가 true로 표시가 되어있는 것을 볼 수 있다.

그림으로 보면 아래와 같은 형태로 되어있다.

어떤식으로 작용이 되었길래, 이런 것이 가능할 것일까?

프로토타입의 구현 형태

글을 시작할 때 나왔던 MDN에는 메소드 앞에 프로토타입이 붙어있던 것을 볼 수 있었다.
그렇다면 이것이 어떤 구조로 되어있는지 알 수 있다면 조금은 이해를 할 수 있지 않을까?

그렇다면 크롬 콘솔창에 아무거나 하나 선언을 한 후, __proto__를 쳐보면 알 수 있다.

이것은 배열을 선언한 후, 프로토를 쳤을 때 나오는 결과인데, 어디선가많이 익숙한 단어들이 보일 것이다.

pop, push, reduce, find, map, filter... 등등 지금까지 사용을 했던 메소드들이 존재하는 것을 확인할 수 있다.

이것이 가능한 이유는, 우리는 Array라는 객체를 만들때 프로토타입에 지정되어있는 메소드를 공유하기 때문이다

자바스크립트에서 이야기하는 프로토타입은 완전 밑바닥에 다양한 메소드들을 구현을 해놓은 것을
선언하는 것만으로도 상속을 받게 되어, 메소드를 공유할 수 있는 것을 뜻한다.

이것을 애플코딩님의 유투브에서는 이러한 비유를 사용한다.

우리는 몸 속에는 부모님의 유전자가 존재하잖아요?
프로토타입이라는걸 유전자라고 생각하면 돼요.
내 몸이 어떤 것에 의해서 형성이 되었는지, 유전자에 전부 다 남아있는 것처럼

자바스크립트의 Array, Object에도 원형(근본)의 메소드들이 담겨있는거지.
그래서 우리가 따로 함수를 만들어서 넣지 않아도 자체적으로 적용되어있는 메소드를 쓸 수 있는거에요.
(원래 이렇게 친절하게 말하는 분은 아닙니다ㅋㅋ)


결국은 상속이라는 개념을 모르고 객체지향도 모르고 있었지만
객체지향에서 제일 중요한 재사용을 우리는 프로토타입이라는 것을 이용하여 메소드를 다양하게 사용할 수 있던 것이다.

프로토타입 체인

객체지향에서 중요한 것 중 하나는 바로 상속이였다.
그렇다면 프로토타입에서는 상속을 어떤 방식으로 지원하는지 알아보자.

정식 명칭은 소제목처럼 프로토타입 체인 이라고 부른다.

코드를 함께 보면서 이해를 해보도록 하자.

Person이라는 생성자 함수를 통하여 me라는 객체를 생성했다.

그런데 me라는 객체에서 hasOwnProperty 메소드를 호출할 수 있는 것을 볼 수 있다.
해당 메소드는 Object.prototype.hasOwnProperty 라는 Object의 프로토타입에서만 사용할 수 있는 메소드 인데 말이다.

이것은 그림과 함께 보는 것이 이해가 빠를 것 같아서 그려왔다(draw.io 최고야)

me객체를 만들기 위해서 Person 생성자 함수를 이용하여 만들었는데
Person의 프로토타입은 Object의 프로토타입을 본떠서 만든 것이다.
그래서 결국 me라는 객체에는 Person.prototype의 메소드와 Object.prototype의 메소드를 모두 사용할 수 있는 것이다.

여기서, person에는 해당하는 메소드가 없어서
person 객체의 부모인 object 프로토타입의 메소드를 사용할 수 있게 하는 것을 프로토타입 체인이라고 이야기한다.

결국은 있을 때까지 계속 올라간다는 것이 특징인데
프로토타입 체인의 최상위에는 무조건 Object.prototype이 존재한다.

그렇기 때문에 어떤 짓을 하더라도 다양한 메소드를 사용할 수 있는 것이다.

그래서 Object.prototype를 프로토타입 체인의 종점(end of prototype chain)이라고도 부른다고 한다.

아무튼 프로토타입에서는 프로토타입 체인이라는 것을 사용하여 상속을 구현한다.

메소드를 만들어서 써보자

문득 이런 생각이 들 수도 있을 것이다.
자주 사용하는 것이 있어서, 이것을 메소드로 아예 등록해버릴 순 없을까?

그럼 그냥 함수를 만들어서 Array.prototype.함수이름 = function(){} 을 하면 메소드를 추가할 수도 있다.

Array에는 create라는 메소드가 없었지만, 함수를 추가하는 것으로 아래 생겨난 것을 확인할 수 있다.


JS의 정적 메소드

그런데 문득, MDN을 보다보니 prototype이 적혀있지 않은 메소드가 있다는 것을 볼 수 있었다.

이러한 메소드를 정적(static)메소드라고 부르는데, 인스턴스를 생성하지 않더라도 사용할 수 있는 것을 이야기한다.

클래스 내부에서 사용을 할때는 메소드 이름 앞에 static을 붙여서 사용을 할 수 있다고 한다.

정적메소드를 클래스 내부에 구현해본 적은 없어서(....) 존재하니 언급은 하겠지만
자세하게는 좀 알아봐야 할 것 같다.

뭔가 시간이라던가, 회원가입 이메일을 보내줄 때 사용하면 좋다고 하는 것 같다!

자바스크립트의 클래스

객체지향을 이야기할 때 빠지지 않고 나오는 것이 바로 클래스다.

하지만 자바스크립트는 프로토타입을 이용하여 객체지향을 구현했는데, 왜 클래스를 도입하게 되었을까?

책에도 적혀있고, 이곳저곳을 찾아본 바로는 *문법적 설탕(syntactic sugar)를 위해 ES6에 도입이 됐다고 한다.

문법적 설탕이란 말그대로 코드를 짜거나, 읽는 사람이 편할 수 있도록 만든 문법을 이야기합니다.

클래스 객체지향 언어를 사용하던 개발자들을 위해 만들어졌고, 매우 흡사한 메커니즘을 지원한다.
하지만 프로토타입를 적용시킨 것을 클래스 기반처럼 보이게 할 뿐이라고 표현하고 있다.

그래도 완벽하게 같진 않고, 어느정도의 차이가 존재하는데 일반적으로 클래스의 방식이 많이 사용되고 있고
상속 관계를 구현하는 것이 편리하여 많은 개발자들이 클래스를 사용하여 JS를 개발하고 있다.

그렇다면 프로토타입으로 구현하는 것과, 클래스로 구현한 것의 차이를 코드로 직접 확인해서 봐보자.

정말 유사한 구조를 가지고 있다는 것을 볼 수 있는데,
여기서 보면 프로토타입 메서드도 지원하는 것을 확인할 수 있다.

서로 다른 객체의 create 메소드가 같다고 출력이 되고 있다.

클래스와 프로토타입의 상속

그리고, 확연한 차이를 느낄 수 있는 것이 있는데 그것은 바로 상속(extends)이다.

개념이 완전 다른데, 프로토타입의 상속은 프로토타입 체인을 이용하여 상속을 구현한다.

  • 프로토타입 체인(상속)
    • 자식이 없다고 하면 부모한테 물어보고 부모도 없다고 하면 증조(?)까지 찾아가서 있으면 꺼내쓴다.
  • 클래스 상속
    • 명확하게 누구에게서 상속을 받아올 것인지 명시해서 부모가 가지고 있는 모든 것을 활용한다.

이해가 묘하게 안될때는 그림이 최고라서 또 그려왔다....

  • 아기는 잘 수 있다.
    • 그런데 사람이라서 나이가 존재한다.
    • 움직일 수도 있다.
  • 아이는 공부를 할 수 있다.
    • 얘도 사람이라서 나이가 존재한다.
    • 움직일 수도 있다.

상당히 느낌이 다른데, JS의 클래스는 결국 프로토타입을 지원해서 조금 더 복합적으로 사용할 수 있다는 장점이 있는 것 같다.


그러니 프로토타입이란 것을 몰라도 지금까지 지장이 없던게 아닌가? 라는 생각도 든다.

최근 OOP를 시작으로 좀 큰 그림에 대해 공부를 하고 있는데
아, 이래서 이렇게 돌아가는 것이였구나. 라는 생각이 최근 많이 들고 있다.

아직은 모자르다고 생각해서 더 공부를 하겠지만... 참 파다보면 재밌다.
마치 역사책을 읽는 느낌이랄까?

정리

사실 프로토타입에 대한 것을 정의해서 설명을 하려면 정말 JS를 깊숙하게 공부한 사람만 할 수 있다고 생각한다(...)
내가 참고하는 책으로만 해도 프로토타입에 대하여 설명하는 페이지가 대략 50페이지가량이 되는데

그 중에서 프로토타입이 무엇이고, 어떻게 작동이 되는 것인지만 딱 분류를 해놨기 때문에
더 세부적인 내용이 필요하겠다. 라고 생각한다면 직접 찾아봐야한다고 생각한다.

왜냐하면 JS의 this의 작동이 타언어와 다른 것의 이유가 바로 프로토타입이라는 것에 있다는 이야기를 얼핏 본 것 같기 때문인데, 결국은 JS의 중심 속에는 프로토타입이 자리잡고 있기 때문에 개인이 공부를 더 해야하는 영역이라고 생각한다.

한국인은 한줄 요약을 좋아하니, 프로토타입에 대한 한줄 요약은 아래와 같다.

  • 우리는 객체나 배열을 생성해서 다양한 메소드를 만들어 쓸 수 있는데
    이유는 프로토타입(근원)에 우리가 사용하고 있던 메소드들이 정의가 되어있기 때문에
    객체&배열(자식)이 프로토타입(부모)의 다양한 메소드를 상속받아 활용할 수 있다.

한줄요약이 안되네

이 다음에 작성해보고 싶은 것은 얕은 복사와 깊은 복사에 대한 이야기인데
자료수집을 좀 해봐야할 것 같다.

언제나 틀린 부분에 대한 피드백은 받고 있습니다 :D

참고하거나 & 같이 보면 좋은 자료

자바스크립트는 왜 프로토타입을 선택했을까
JavaScript 객체 지향 프로그래밍 - 15. prototype vs proto - 생활코딩
이거보고 prototype 이해 못하면 강의접음 - 코딩애플
JavaScript Classes – How They Work with Use Case Example
프로토타입 상속
클래스 상속
책) 모던 자바스크립트 Deep Dive

profile
물류 서비스 Backend Software Developer

0개의 댓글