JS/Node - 객체 지향 JavaScript

2021. 10. 5. 16:36·SE Bootcamp 내용 정리

JS/Node - 객체 지향 JavaScript

객체 지향 프로그래밍(OOP; Object-oriented programming)은 데이터와 기능을 한 곳에 모아 처리하는 것. 속성과 메소드가 하나의 “객체” 라는 개념에 포함되고 자바스크립트의 타입은 object와는 다른 것이다.
→ 클래스(Class)라고 부른다

학습 목표

* 클래스와 인스턴스
- new 키워드 사용법
- class 키워드 사용법

* 객체 지향 프로그래밍의 특징
- 캡슐화
- 상속
- 추상화
- 다형성

* JavaScript에서 객체 지향 프로그래밍 구현
-  ProtoType에 대한 이해

* 객체 지향 프로그래밍의 상속(inheritance)
- 상속 관계를 가진 현실 세계의 모델을 코드로 작성하기
- 클래스 상속의 원리
- ProtoType chain에 대한 이해(`__proto__`)

클래스를 이용한 모듈화

객체 Review

메소드 호출

객체.메소드()    의 형태

주의! 메소드 호출 방식을 이용할 때는 화살표 함수를 쓰지 않는다. Why?
화살표 함수는 자신의 this를 가지고 있지 않는다(this가 바인딩되지 않음)
그래서 메소드로 불러올 때 그 값을 가져올 수 없어서 Object.defineProperty() 이 발생
또한 생성자로서도 사용 불가(new 연산자 사용 x)
prototype 속성도 사용 불가(prototype 속성이 없음)

 

ex) Singleton 패턴으로 구현한 counter 예시

let counter123 = {
  value: 0,
  increase: function() {
    this.value++ // 메소드 호출을 할 경우, this는 counter123을 가리킴
  },
  decrease: function() {
    this.value--
  },
  getValue: function() {
    return this.value
  }
}

counter123.increase()
counter123.increase()
counter123.decrease()
counter123.getValue() // 1

클로저를 이용해 매번 새로운 객체 생성하기

Singleton 패턴은 단 하나의 객체만 생성 가능. 그런데, 만약 똑같은 기능을 하는 카운터가 여러 개 필요할 때는 그렇다면 코드를 여러 개 만들어야 하나? 이는 비효율적!
→ 클로저 모듈 패턴을 이용하자

ex) 클로저 모듈 패턴으로 구현한 counter 예시

function makeCounter() {    // 함수(외부 함수) 안에 함수(내부 함수)가 있고 이를 리턴하는 구조인 클로저 함수
  return {
    value: 0,
    increase: function() {
      this.value++ // 메소드 호출을 할 경우, this는 makeCounter 함수가 리턴하는 익명의 객체
    },
    decrease: function() {
      this.value--
    },
    getValue: function() {
      return this.value;
    }
  }
}

let counter1 = makeCounter()
counter1.increase()
counter1.getValue() // 1

let counter2 = makeCounter()
counter2.decrease()    // 0에서 시작
counter2.decrease()
counter2.getValue() // -2

// 각각의 객체가 독립적이라고 생각하면 편할 듯?

클래스와 인스턴스

객체 지향 프로그래밍

하나의 모델이 되는 청사진을 만들고 이를 바탕으로 한 객체(Object)를 만드는 프로그래밍 패턴
→ 여기서의 청사진: class, 객체: instance (instance 객체)

객체를 만들 때 일반 함수를 정의하듯 만든다. 다만 차이점이 존재!
→ new 키워드 사용(새로운 인스턴스 생성 시)
→ 클래스 생성 시에는 대문자, 일반명사로 만들기(암묵적인 규칙!)

ex) 클래스와 인스턴스 예시

function Car(color) { }    // 클래스

let sonata= new Car(‘black’);    // 인스턴스
let mini = new Car(‘green’);
let beetles = new Car(‘silver’);

클래스를 만드는 새로운 문법이 ES6에 도입됨에 따라 최근에는 ES6 방법을 주로 사용

ex) ES5에서의 클래스 생성: 함수(function)로 정의

function Car(brand, name, color) {
// 인스턴스가 만들어질 때 실행되는 코드 작성 부분
}

ex) ES6에서의 클래스 생성: class 키워드를 이용해 정의

class Car{
    constructor(brand, name, color){
    // 인스턴스가 만들어질 때 실행되는 코드 작성 부분
    // 생성자 함수는 return값을 만들지 않는다!
    }

}

인스턴스를 만들 때는 new 키워드를 사용
→ 즉시 생성자 함수가 실행되면서, 변수에 클래스의 설계를 꼭 닮은 새로운 객체; 인스턴스가 할당됨

속성과 메소드

클래스에 속성과 메소드를 정의하고 인스턴스에서 이용

ex) 자동차로 비유한 속성과 메소드

속성: 브랜드, 차 이름 ,색상, 최고 속력 등
메소드: 객체에 딸린 함수. 연료주입, 속력 설정, 운전 등

클래스: 속성의 정의

this 키워드란?
“인스턴스 객체”를 의미

class Car {
    constructor(brand, name, color) {
        this.brand = brand;    // 만들어진 인스턴스(this)에 해당 브랜드
        this.name = name;    // 이름
        this.color = color;    // 색상을 부여하겠다는 의미
    }

}

클래스: 메소드의 정의

여기서는 ES5와 ES6의 문법이 다르다

ex) ES5에서의 문법

function Car(brand, name, color) {/*코드 생략*/ }    // ES5에서는 이게 생성자(constructor) 함수!

Car.prototype.refuel = function(){
    // 연료 공급을 구현하는 코드 작성
}

Car.prototype.drive = function(){
    // 운전을 구현하는 코드 작성
}

// ES5에서는 메소드 정의시 prototype을 이용해야 함
// 클래스(생성자 함수) 밖에다가 따로 작성함(메소드를)

ex) ES6에서의 문법

class Car{
    constructor(brand, name, color){/*코드 생략*/     }

    refuel(){
        // 연료 공급을 구현하는 코드 작성
    }

    drive(){
        // 운전을 구현하는 코드 작성
    }

}

// 메소드를 클래스 안쪽에 묶어서 정의함

인스턴스에서의 사용

아래와 같이 사용하면 된다

let sonata = new Car(‘hyundai’, ‘sonata’, ‘black’);
sonata.color;    //’black’
sonata.drive();    // 소나타 운전을 구현하는 코드에 따른 작동

용어들에 대한 설명

prototype: 모델의 청사진을 만들 때 쓰는 원형 객체(original form)

constructor: 인스턴스가 초기화될 때 실행하는 “생성자” 함수

this: 함수가 실행될 때, 해당 scope 마다 생성되는 고유한 실행 context(excution context)
new 키워드로 인스턴스를 생성했을 때는 해당 인스턴스가 바로 this(this의 값)가 됨

클래스 문법을 이용한 카운터 만들기 실습

class Counter {
  constructor() {
    this.value = 0; // 생성자 호출을 할 경우, this는 new 키워드로 생성한 Counter의 인스턴스
  }
  increase() {
    this.value++
  }
  decrease() {
    this.value--
  }
  getValue() {
    return this.value    // 메소드에서는 return문 가능
  }
}

let counter1 = new Counter() // 생성자 호출
counter1.increase()
counter1.increase()
counter1.decrease()
counter1.getValue() // 1

Prototype

객체 지향 프로그래밍

cf. 절차적 언어: 초기의 프로그래밍 언어(C, 포트란 등)
→ 순차적인 명령의 조합

객체 지향 언어: 클래스를 이용한 코드 작성. “데이터”와 “기능”이 별개로 취급되지 않고 한번에 묶어 처리
→ 현대 언어의 대다수가 객체 지향의 특징을 가짐(Java, C++, C# 등)
→ JavaScript: 객체 지향으로 작성 가능(엄밀히 말하면 객체 지향 언어는 아님)

OOP(객체 지향 프로그래밍) Basic Concepts

애플리케이션을 만들 때 좋은 설계를 하기 위해 객체 지향을 잘 이해하고 응용해야 한다.
객체 지향의 주요 컨셉은 크게 4가지 기본 컨셉이 존재한다.

* Encapsulation(캡슐화)
* Inheritance(상속)
* Abstraction(추상화)
* Polymorphism(다형성)

Encapsulation(캡슐화)

데이터(속성)와 기능(메소드)를 하나의 단위(객체 안에)로 묶는 것
은닉: 구현(자세한 구현 코드)은 숨기고 동작만 노출시킨다(메소드만)

느슨한 결합(Loose Coupling)에 유리: 언제든 구현을 수정할 수 있어 직관적이고 유지보수에 유리
→ 코드 실행 순서에 따라 절차적으로 코드를 작성하는 것이 아닌, 코드가 상징하는 실제 모습과 닮게 코드를 모아 결합하는 것을 의미함

→ 코드가 복잡하지 않게 하고 재사용성을 높임

Abstraction(추상화)

내부 구현은 아주 복잡한데, 실제로 노출되는 부분은 단순하게 만든다는 개념
→ 간단하게 복잡한 물건의 구현을 단순화해서 생각하는 것

추상화를 통해 인터페이스가 단순해진다

추상화는 캡슐화와 비교해서 헷갈리기 쉽다!
→ 캡슐화는 은닉(데이터, 코드 등)에 초점!
→ 추상화는 필요하지 않은 메소드는 딱히 노출시키지 않고 단순한 이름으로 정의하는 것에 초점!

클래스 정의 시 메소드와 속성만 정의한 것을 “인터페이스” 라 부른다
→ 인터페이스가 추상화의 본질!

→ 코드가 복잡하지 않게 만들고, 단순화된 사용으로 인해 변화에 대한 영향을 최소화

Inheritance(상속)

부모 클래스의 특징을 자식 클래스가 물려 받는 것(상속)
→ 불필요한 코드를 줄여 재사용성을 높임

Polymorphism(다형성)

poly는 “많은”, morph는 “형태” 라는 뜻
→ 다양한 형태를 가질 수 있다는 말

“말하다” 라는 동작을 볼 때, 각기 다른 동물들의 말하는 소리가 제각각인 것 처럼, 객체 역시 똑같은 메소드라도 다른 방식으로 구현될 수 있다.
→ “말하다”라는 메소드가 있으면 “말” 에서는 이를 “히히힝”으로 재정의하고 “소”에서는 이를 “음메”로 재정의하고 그러는 것!

→ 동일한 메소드에 대해 if/else if와 같은 조건문 대신 객체의 특성에 맞게 달리 작성하는 것이 가능해짐

클래스와 프로토타입

JavaScript는 프로토타입(원형 객체) 기반 언어
→ 객체를 상속하기 위해 프로토타입을 이용함

JavaScript는 흔히 프로토타입 기반 언어(prototype-based language)라 불립니다.— 모든 객체들이 메소드와 속성들을 상속 받기 위한 템플릿으로써 프로토타입 객체(prototype object)를 가진다는 의미입니다. 프로토타입 객체도 또 다시 상위 프로토타입 객체로부터 메소드와 속성을 상속 받을 수도 있고 그 상위 프로토타입 객체도 마찬가지입니다. 이를 프로토타입 체인(prototype chain)이라 부르며 다른 객체에 정의된 메소드와 속성을 한 객체에서 사용할 수 있도록 하는 근간입니다.

정확히 말하자면 상속되는 속성과 메소드들은 각 객체가 아니라 객체의 생성자의 prototype이라는 속성에 정의되어 있습니다. 프로토타입 체인에서 한 객체의 메소드와 속성들이 다른 객체로 복사되는 것이 아님을 재차 언급합니다. — 위에서 보시다 시피 체인을 타고 올라가며 접근할 뿐입니다.

프로토타입 속성도 하나의 객체이다

상속 받는 멤버들은 prototype 속성(sub-namespace라고도 하죠)에 정의되어 있다 — Object.로 시작하는게 아니라, Object.prototype.로 시작하는 것들이죠.

prototype 속성도 하나의 객체이며 프로토타입 체인을 통해 상속하고자 하는 속성과 메소드를 담아두는 버킷으로 주로 사용되는 객체입니다. Object.is(), Object.keys()등 prototype 버킷에 정의되지 않은 멤버들은 상속되지 않습니다. 이 것들은 Object() 생성자에서만 사용할 수 있는 멤버들이죠.

프로토타입 객체 vs 프로토타입 속성

프로토타입 객체: __proto__ 속성으로 접근 가능한 내장 객체
프로토타입 속성: 상속시키려는 멤버들이 정의된 객체

인스턴스의 생성자 이름 알아내기

instanceName.constructor.name

// 복잡한 로직에 사용할 때 에는 instanceof 연산자를 사용하자(동일 용도)

실습 예제

아래와 같이 Human 클래스를 만들어서 구현하여 테스트 해보자

ex) OOP 패턴으로 구현한 Human 예시

class Human {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sleep() {
    console.log(`${this.name}은 잠에 들었습니다`);
  }
}

let kimcoding = new Human('김코딩', 30);

// 테스트
Human.prototype.constructor === Human; // true
Human.prototype === kimcoding.__proto__; // false
Human.prototype.sleep === kimcoding.sleep; // true

프로토타입 체인

상속을 JavaScript에서 구현할 때는 프로토타입 체인을 사용
JavaScript에서는 extends와 super 키워드를 이용해서 상속을 구현할 수 있다.

부모 클래스인 Person을 생성

class Person {
  constructor(first, last, age, gender, interests) {
    this.name = {
      first,
      last
    };
    this.age = age;
    this.gender = gender;
    this.interests = interests;
  }

  greeting() {
    console.log(`Hi! I'm ${this.name.first}`);
  };

  farewell() {
    console.log(`${this.name.first} has left the building. Bye for now!`);
  };
}

자식 클래스인 Teacher를 생성하고 부모꺼를 상속받음

class Teacher extends Person {
  constructor(first, last, age, gender, interests, subject, grade) {
    super(first, last, age, gender, interests);    //super키워드를 통해 상위 클래스의 멤버를 상속 받음

    // subject and grade are specific to Teacher    // Teacher에서 추가된 멤버
    this.subject = subject;
    this.grade = grade;
  }
}

let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
snape.greeting(); // Hi! I'm Severus.
snape.farewell(); // Severus has left the building. Bye for now.
snape.age // 58
snape.subject; // Dark arts

Sprint – Beesbeesbees 만들기

Class Structure

├── Grub
│   └── Bee
│       ├── HoneyMakerBee
│       └── ForagerBee

위와 같은 구조로 짜여지게 클래스를 만들어서 진행

아래는 작성한 코드 내용

class Grub {
  // TODO..
  constructor(age, color, food){
    this.age=0;
    this.color="pink";
    this.food="jelly";
    // console.log(Grub.name)  //”Grub” 이라는 클래스 이름을 리턴
  }

  eat(){
    return `Mmmmmmmmm ${this.food}`
  }
}

class Bee extends Grub{// Grub을 상속받
  // TODO..
  constructor(age, color, food, job){
    super(age, color, food);  //age=0을 받은 상태
    this.age=5; //age=5
    this.color="yellow";
    this.job="Keep on growing";
  }
}


class HoneyMakerBee extends Bee{
  // TODO..
  constructor(age, color, food, job, honeyPot){
    super(age, color, food, job);
    this.age=10;
    this.job="make honey";
    this.honeyPot=0;
  }

  makeHoney(){
    this.honeyPot++
  }

  giveHoney(){
    this.honeyPot--
  }
  isHoney(){
    return this.honeyPot; 
  }
}

let mkBee = new HoneyMakerBee(15, "blue", "apple", "make apple honey", 3);    // 어차피 클래스 내에 설정된 값이 되므로 임의의 값을 넣음
mkBee.makeHoney();    // 초기값 0에서 +1인 1
mkBee.makeHoney();    //2
mkBee.makeHoney();    //3
mkBee.giveHoney();    //2
mkBee.isHoney();    //2


class ForagerBee extends Bee{
  // TODO..
  constructor(age, color, food, job, canFly, treasureChest){
    super(age, color, food, job);
    this.age=10;
    this.job="find pollen";
    this.canFly=true;
    this.treasureChest=[];
  }


  forage(treasure){ // mutable한 메서드를 사용
    this.treasureChest.push(treasure);  // mutable 메서드 -> 기존의 배열을 수정하는 메서드
    // this.treasureChest.splice(this.treasureChest.length,0,treasure);
    // mutable 메서드인 spilce를 이용해서 위와 같은 효과가 되게 작성해본 것
  }
}

JavaScript와 기타 다른 객체 지향 언어와의 차이점

은닉화(private 키워드)의 한계: JavaScript의 한계임

Java나 TypeScript 같은 프로그래밍 언어는 클래스 내부에서”만” 쓰이는 속성 및 메소드를 구분하는 용도로 private 키워드를 제공한다.
→ JS는 지원하는 브라우저가 매우 적어서 사실상 못 씀
→ 대신 은닉화를 위해 일반적으로 “클로저 모듈 패턴”을 사용함
(클래스/인스턴스 형태로 만들 때는 다른 방법을 사용함)

ex) TypeScript 에서의 private 키워드 사용
// TypeScript 문법입니다.

class Animal {
  private name: string;

  constructor(theName: string) {
    this.name = theName;
  }
}

new Animal("Cat").name; // 사용 불가
// Property 'name' is private and only accessible within class 'Animal'.

추상화(interface 키워드) 기능의 부재

객체 지향 프로그래밍에서 추상화는 속성과 메소드의 이름만 노출시켜서 사용을 단순화 한다는 의미를 가짐
→ 즉, 인터페이스의 단순화를 의미
→ Java, TypeScript 언어는 interface 키워드가 존재
→ JS에서는 interface 키워드가 존재하지 않는다.

ex) TypeScript에서 interface와 class를 따로 정의한 예시

// TypeScript 문법입니다.

interface ClockInterface {    // interface 키워드를 사용하여 정의
  currentTime: Date;
  setTime(d: Date): void;
}

class Clock implements ClockInterface {    // class 정의
  currentTime: Date = new Date();
  setTime(d: Date) {
    this.currentTime = d;
  }
  constructor(h: number, m: number) {}
}

이러한 인터페이스는 일종의 “규약”처럼 간주되기에, 인터페이스를 클래스로 구현하는 사람들이 이에 맞게 작성할 수 있게 도와주는 역할
→ “이 클래스는 메소드 이름이 의도한 바대로 작동할 것이다” 라는 것을 드러내는 것
→ 외부 공개용으로 모듈형으로 작동하는 인터페이스 사용 예: API(Application Programming Interface)

저작자표시 (새창열림)

'SE Bootcamp 내용 정리' 카테고리의 다른 글

자료구조/알고리즘 - 자료구조 기초(Stack, Queue, Graph, Tree, BST)  (0) 2021.10.11
자료구조/알고리즘 - 재귀  (0) 2021.10.11
React - 보충학습 2: states, Component Life Cycle  (0) 2021.10.01
React - 보충 학습 1: 부모/자식 컴포넌트, map 메소드, props  (0) 2021.09.30
SECTION 1 회고  (0) 2021.09.30
'SE Bootcamp 내용 정리' 카테고리의 다른 글
  • 자료구조/알고리즘 - 자료구조 기초(Stack, Queue, Graph, Tree, BST)
  • 자료구조/알고리즘 - 재귀
  • React - 보충학습 2: states, Component Life Cycle
  • React - 보충 학습 1: 부모/자식 컴포넌트, map 메소드, props
레실이
레실이
  • 레실이
    레실이의 티스토리
    레실이
  • 전체
    오늘
    어제
    • 분류 전체보기 (91)
      • SE Bootcamp 내용 정리 (63)
      • 알고리즘 연습 (7)
      • Project 주저리 (4)
      • 기술 면접 source (3)
      • 개발 일상 (12)
      • 생성 AI 활용 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    useRef
    인증/보안
    mongoDB
    CSR
    promise
    Python
    node.js
    JS
    react
    객체
    CORS
    ubuntu
    JavaScript
    Ajax
    DOM
    자료구조
    IT
    fastapi
    데이터베이스
    알고리즘
    비동기
    state
    PickAndDrink
    node
    ORM
    useState
    CSS
    Linux
    MVC
    문자열
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
레실이
JS/Node - 객체 지향 JavaScript
상단으로

티스토리툴바