(TIL) D+20 객체 지향 프로그래밍, 프로토타입

JulyK9·2022년 7월 25일
0

객체 지향 프로그래밍(OOP, Object Oriented Programming)

✅  절차적 언어

  • 순차적인 명령을 조합함
  • 초기 프로그래밍 언어들이 주로 이런 방식(C, 포트란 등)

✅ 객체 지향 언어

  • 클래스라고 부르는 데이터 모델의 청사진을 사용해 코드 작성
  • 현대의 언어들 대부분 (java, C++, C# 등)
  • JavaScript는 엄밀히 말해 객체 지향 언어는 아니지만, 객체 지향 패턴으로 작성 가능

✅ 객체 지향 프로그래밍

  • 프로그램 설계 철학
  • 모든 것은 ‘객체’로 그룹화됨
    • 객체 내에는 데이터와 기능이 함께 있다라는 원칙 ⇒ 객체 내에는 메서드와 속성이 존재 ⇒ 클래스를 만들때 속성과 메서드를 함께 만들어 줬던 걸 생각하면 좋을 것 같다.
    • 이를 통해 새로운 객체를 만들면서 속성에 고유한 값을 부여할 수 있음 ⇒ new 키워드로 인스턴스를 만들면서 생성자 함수 this를 통해 속성에 고유한 값을 부여할 수 있었음
  • 4가지 주요 개념을 통해 재사용성을 확보하는게 핵심

✅ 클래스와 인스턴스와 관련해서 이해

  • 클래스: 일종의 원형(original form), 객체 생성을 위한 아이디어, 청사진, 설계도, 붕어빵틀
    • 즉, 아직 세부사항(속성)이 들어가지 않은 청사진 ⇒ 여기서 세부사항인 속성만 부여된다면 객체가 되는 것!
    • 그럼 세부사항은 어떻게 넣어주나? ⇒ 생성자를 통해 함수에 인자를 넣듯이 속성을 넣어줌!
  • 인스턴스: 클래스의 사례, 클래스를 바탕으로 생성된 객체
  • 속성과 메서드의 구분
    • 속성 : 객체가 가진 고유한 속성(예, 자동차의 색상, 가격, 마력 등)
    • 메서드 : 객체가 가진 기능(예, 시작, 전진, 후진, 멈춤 등)

❓ 왜 객체 지향을 알아야 하는가?

  • 좋은 설계를 하기 위해서! (정리도 절차적으로 하지 말고 객체 지향으로 해보고 싶다..)
  • 그래서 알아야 하는 4가지 기본 개념 : 캡슐화, 상속, 추상화, 다형성

1️⃣ 캡슐화 (Encapsulation)

⇒ 코드를 복잡하지 않게 만들고 재사용성을 높임

  • 코드와 데이터의 은닉이 포커스
  • 데이터와 기능을 하나의 단위로 묶는 것
    • 따로 정의하는 것이 아니라 한 객체 안에 넣어서 묶는 것(느슨하게 결합되었다고 표현)
  • 은닉(hiding)화의 특징을 포함
    • 내부 데이터나 내부 구현은 숨기고,
    • 객체 외부에서 필요한 동작(메서드)만 노출시키는 것
    • 이런 관점에서 더 엄격한 클래스의 경우 속성의 직접 접근을 막고, 설정하는 함수(setter), 불러오는 함수(getter)를 철저하게 나누기도 함
  • 느슨한 결합에 유리함 : 언제든 구현을 수정할 수 있음
    • 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아니라
    • 코드만 보고도 인스턴스 객체의 기능을 상상할 수 있게 작성하는 것

2️⃣ 추상화 (Abstraction)

⇒ 코드가 복잡하지 않게 만들고, 단순화로 변화에 대한 영향 최소화

  • 내부는 복잡, 노출되는 부분은 단순하게! (애플?)
  • 인터페이스 단순화
    • ux,ui 관점에서도 추상화가 중요할 듯 ⇒ 사용자에게 필요하지 않은 메서드나 속성을 노출시키지 않고 단순한 이름으로 정의
    • 단순화를 통해 예상치 못한 사용상의 변화를 줄일 수 있음
  • 인터페이스란 클래스 정의 시, 메서드와 속성만 정의한 것, 추상화의 본질

3️⃣ 상속 (Inheritance)

⇒ 불필요한 코드를 줄여 재사용성 높임

  • 부모 클래스의 특징을 자식 클래스가 물려받음
  • 기본 클래스(base class)의 특징을 파생 클래스(derived class)가 상속받음
  • 생각해보기 아주 좋은 예

4️⃣ 다형성 (Polymorphism)

⇒ 조건문 대신 객체의 특성에 맞게 달리 작성하는 것이 가능

  • 같은 이름을 가진 메서드라도 조금씩 다르게 작동
  • 다형성이 제공되지 않는다면 기본(부모) 클래스에 종류별로 분기를 시켜 하나하나 다 다르게 만들어야 할 것
if (type === select) {
	renderSelect()
}
else if (type === 'text') {
	renderTextBox()
}
else if (type === 'checkbox') {
	renderCheckBox()
}

✅ 결론

  • 객체 지향 프로그래밍은 우리가 세계를 보고 이해하는 방법과 흡사 ⇒ 평소에 사물이나 현상을 보고 객체 단위로 구분하고 속성과 메소드를 생각해보는 연습을 해보자
  • 코딩, 설계할 때도 화면에 보이는 하나의 요소를 객체 단위로 구분해서 생각하고 복잡하지 않고 재사용성을 높일 수 있도록 작성하기

📌 추가내용

  • 은닉화의 한계
    • 은닉화를 지원하는 기능이 JS 에서는 널리 쓰이지 않음
    • JS 에서는 일반적으로 은닉화를 위해 클로저 모듈 패턴 사용
    • 클래스 인스턴스 형태로 만들 때는 #이라는 키워드가 도입됨 ⇒ 은닉화를 위해 보다 객체 지향적 언어인 TypeScript를 배워야 할 필요가 있음(클래스 내부에서만 쓰이는 속성, 메서드 구분, private 키워드)
  • 추상화 기능 부재
    • 즉 인터페이스(단순화) 기능 부재 ⇒ TS는 주요 기능으로 interface 를 구현해놓음 ⇒ 노출된 인터페이스를 통해 “이 클래스는 메서드 이름이 의도한 바대로 작동할 것이다"라는 것을 드러냄 ⇒ 이는 실질적인 구현 방법은 노출하지 않으면서 사용법은 노출시키기에도 유리함 ⇒ 이러한 인터페이스의 대표적인 예가 API(Application Programmin Interface)

프로토타입

  • 익숙한 개념으로 이해해보기
    • Array 는 클래스다
    • 지금까지 써온 배열은 Array 클래스의 인스턴스이며, Array.prototype에는 push, pop 등 다양한 메서드가 있음

✅ 프로토타입

  • 원형 객체
  • 쉽게 말하면 js 에서 상속을 위한 매커니즘이라고 보면 된다(mdn에 정의된 핵심 단어)
  • JS에서 객체를 상속하기 위한 방식
    • 상속은 기본 클래스의 속성이나 메서드를 가져와 기존의 기능을 확장할 때 필요

💡 사람에 관한 속성과 메서드를 가진 user 라는 객체가 있는데 , user와 유사하지만 약간 차이가 있는 admin과 guest 객체를 만들어야 하는 경우. 이때 user의 메서드를 복사하거나 재구현하지 않고 user에 약간의 기능을 얹어 admin과 guest 객체를 만들고 싶을 때 프로토타입 상속을 이용할 수 있음

  • JS에서 객체는 [[Prototype]] 이라는 숨은 속성이 있는데, 이것의 값은 null 이거나 다른 객체를 바라보며, 상속을 구현하는데 사용됨 ⇒ 즉 [[Prototype]] 이 참조하는 객체를 프로토타입 이라고 함 ⇒ [[Prototype]] 속성은 내부속성이면서 숨김 속성이지만 값을 설정할 수 있음 ⇒ 객체는 obj.proto 를 사용하여 자신의 프로토타입 [[Prototype]] 에 접근할 수 있음
  • 프로토타입의 동작 방식
    • 해당 객체에서 특정 속성을 읽으려고 하는데 그 속성이 없으면 JS는 자동으로 프로토타입에서 그 속성을 찾는다 (프로토타입 상속)

      let animal = {
      	eats: true,
      	walk() {
      		alert("동물이 걷습니다");
      	}
      };
      
      let rabbit = {
      	jumps: true,
      	__proto__: animal
      };
      
      rabbit.__proto__ = animal;  // 속성 eats와 jumps를 rabbit 에서도 사용할 수 있게 됨
      // => rabbit의 프로토타입음 animal이 되었음
      // => rabbit은 animal을 상속받는다
      // => 프로토타입을 설정해준 덕분에 rabbit에서도 animal 에 구현된 속성과 메서드를 사용할 수 있게 됨
      
      alert(rabbit.eats); // alert 함수가 rabbit.eats 를 읽으려 하지만 eats 속성이 없으므로 [[Prototype]]이 참조하고 있는 객체인 animal에서 eats 를 찾아냄
      
      rabbit.walk() // 동물이 걷습니다
      // => 메서드 walk 는 rabbit의 프로토타입인 animal에서 상속받았음
  • 객체의 속성 순회 관련
    • for.. in 반복문은 객체 자체에서 정의한 속성 뿐만 아니라 상속된 속성도 순회 대상에 포함
    • 반면, 키-값과 관련된 내장 메서드 대부분은 상속된 속성은 제외하고 객체 자체 속성만 대상으로 동작함

✅ .__proto__

  • __proto__ 프로퍼티는 [[Prototype]] 내부 슬롯이 가리키는 프로토타입 객체에 접근하기 위해 사용하는 접근자 프로퍼티이다
  • 직접 접근할 수 없으며 __proto__ 접근자 프로퍼티를 통해 간접적으로 프로토타입 객체에 접근할 수 있다.
  • 객체가 직접 소유하는 프로퍼티가 아니라 모든 객체의 프로토타입 객체인 Object.prototype 객체의 프로퍼티이다.
  • 즉 모든 객체는 Object.prototype 의 접근자 프로퍼티 __proto__ 를 상속받아 사용할 수 있다.
  • 함수도 객체이므로 __proto__ 접근자 프로퍼티를 통해 프로토타입 객체에 접근할 수 있다.

✅ .prototype

  • 생성자 함수에 기본으로 세팅되는 속성(F.prototype)으로 생성자 함수로 [[Prototype]] 과는 다름
  • 모든 객체는 자신의 프로토타입 객체를 가리키는 [[Prototype]] 이라는 내부 슬롯을 가지며 이는 상속을 위해 사용되는데
  • 함수도 객체이므로 [[Prototype]] 을 가지지만 함수 객체는 일반 객체와는 달리 prototype 프로퍼티도 소유하게 됨
  • 즉 함수 객체만이 소유하는 프로퍼티이며. 일반 객체에는 prototype 프로퍼티가 없음
  • prototype 프로퍼티는 함수가 객체를 생성하는 생성자 함수로 사용될때, 생성자 함수가 생성한 인스턴스의 프로토타입 객체를 가리킴
  • 즉 일반 객체와 생성자 함수로 호출할 수 없는 non-constructor 에는 prototype 프로퍼티가 없음
  • 화살표 함수와 ES6 메서드 축약 표현으로 정의된 메서드는 non-constructor로 prototype 프로퍼티가 없다.
  • [[Prototype]] 은 객체의 입장에서 자신의 부모 역할을 하는 프로토타입 객체를 가리키며 함수 객체의 경우 Function.prototype 을 가리킴

✅ Array(배열)로 보는 클래스, 인스턴스, 프로토타입의 관계

  • 우리가 보는 arr는 실은 Array 클래스의 인스턴스이며, 프로토타입에 다양한 메서드가 있음
  • 참고

https://www.howdy-mj.me/javascript/prototype-and-proto/

https://ko.javascript.info/prototypes

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/proto

https://poiemaweb.com/js-prototype

https://poiemaweb.com/js-function#65-proto-접근자-프로퍼티

profile
느리지만 꾸준하게. 부족하거나 잘못된 부분은 알려주시면 감사하겠습니다.

0개의 댓글