본문 바로가기
지금, 개발하기/Javascript

javascript] 자바스크립트의 클래스

by Seaco :) 2023. 8. 14.

javascript에서 객체를 생성하는 방법에는 여러가지가 있습니다. 그 중에서도 class를 사용하는 방법은 javascript 뿐만아니라 다른 언어에서도 보편적으로 사용하는 방법이고, 객체지향의 원리를 이해하는데도 도움이됩니다. 

 

1. 클래스란?

: javascript에서 클래스는 객체를 생성하는데 사용되는 템플릿입니다. 클래스를 사용하여 객체를 생성하면 해당 클래스에 정의된 속성과 메서드를 가지는 새로운 객체가 생성이 됩니다. 

📌 클래스 선언, 객체 생성

class Student {
  constructor(name, birthdate) {
    this.name = name;
    this.birthdate = birthdate;
  }

  celebrate(gift) {
    console.log(`${this.name} gets ${gift.name}`);
  }
}

const student1 = new Student('youjin', '1995-04-21');
const student2 = new Student('heesu', '1994-08-09');
const student3 = new Student('hana', '1992-01-24');

위의 예시에서 Student라는 클래스를 선언했습니다.  프로퍼티는 constructor 안에 정의하고, 메소드는 constructor 밖에 정의하였습니다.  클래스를 사용하여 객체를 만들 때는 클래스명 앞에 new를 붙여서 student1, student2, student3이라는 새로운 객체를 생성하였습니다. 

 

2. 클래스의 핵심개념

📌 1) 추상화(Abstraction)

: 추상화는 클래스를 정의할 때 해당 클래스의 특징과 행동을 정의하는 과정을 말합니다. 쉽게 말해서 가상의 존재를 프로그램 내에서 사용할 수 있도록 적절히 설계하는 과정입니다. javascript에서 클래스의 추상화는 일반적으로 클래스를 정의하고 클래스의 속성과 메서드를 사용하여 객체를 생성하는 것을 말합니다. 이를 통해 객체를 사용자는 객체 내부의 복잡한 원리를 모르더라도 공개된 프로퍼티와 메소드만을 가지고 객체를 잘 사용할 수 있습니다. 

class Student {
  constructor(name, subject) {
    this.name = name;
    this.subject = subject;
  }

  signUP(subject) {
    console.log(`${this.name} signed up for a ${subject} class`);
  }
}

const student1 = new Student('youjin', 'math');
const student2 = new Student('heesu', 'english');

위 예시에서는 Student 클래스를 추상화 클래스로 정의하였고, 추상 메서드인 signUp()을 구현하여 수강신청을 하는 동작을 수행하도록 하였습니다.

 

📌 2) 캡슐화(Encapsulation)

캡슐화는 객체의 외부에서 프로퍼티나 메소드에 직접 접근하지 못하고, 필요한 경우에만 공개된 다른 메소드를 통해서 접근할 수 있도록 하여 내부의 객체를 보호 합니다.

class Car {
    constructor(company, model) {
      this.company = company;
      this.model = model;
      this._speed= 0; // private 멤버 변수
    }
  
    // getter 메소드: 메서드를 통해 speed 속성에 접근
    get speed() {
      return this._speed;
    }
  
    // setter 메소드: speed 속성을 변경하기 위한 메서드
    set speed(value) {
      if (value >= 0) {
        this._speed = value;
      } else {
        console.log("마이너스 속도로 달릴 수 없습니다.");
      }
    }
  
    // 외부에서 접근 가능한 메서드
    speedUp(value) {
      this._speed += value;
    }
  }
  
  const myCar = new Car("Hyundai", "Genesis");
  
  //  getter 메소드:  speed는 메서드를 통해서만 접근 가능
  console.log(myCar.speed); // 0
  
  //  setter 메소드: speed 속성 변경 시 유효성 검사 수행
  myCar.speed = 50; // 유효한 값, speed 변경
  myCar.speed = -30; // 유효하지 않은 값, 콘솔에 "마이너스 속도로 달릴 수 없습니다." 출력
  
  myCar.speedUp(20);
  console.log(myCar.speed); // 50 + 20 = 70

위 코드에서 myCar의 speed를 나타내는 프로퍼티는 사실 _speed이고, _speed의 getter, setter 메소드의 이름이 speed입니다. 그래서 우리는 _speed에 직접 접근하지 않고, getter, setter를 이용하여 speed에 접근하면서 내부를 보호할 수 있습니다. 즉, 마치 speed 프로퍼티에 접근하는 것같은 코드를 작성하더라도 사실은 speed라는 getter, setter 메소드가 실행된 것입니다. 

참고로 javascript 에서는 프라이빗 변수를 구분할 때, 프라이밋 변수 이름 앞에 언더바(_)을 사용합니다. 이것은 변수가 외부에서 직접적으로 접근되어서는 안 된다는 의미를 갖고 있습니다.

 

📌 3) 상속(Inheritance)

상속이란 한 객체가 다른 객체의 속성과 메소드를 이어받아 사용하는 것을 의미하는데요. extends 키워드를 사용하면 손쉽게 부모클래스(상위클래스)의 속성과 메소드를 자식클래스(하위 클래스)가 상속받아 사용할 수 있습니다.  물론, 자식클래스는 상속받은 부모클래스의 기능 외에도 원하는 기능을 추가할 수 있고, 원한다면 부모의 기능을 재정의 할 수도  있습니다. 상속은 코드의 재사용성이 높이고, 구조화된 코드 작성을 할 수 있도록 해줍니다.

// 부모 클래스 정의
class Vehicle {
    constructor(model) {
      this.model = model;
    }
  
    display() {
      console.log(`이 차는 ${this.model}입니다.`);
    }
  }
  
  // 자식 클래스 정의
  class Sedan extends Vehicle {
    constructor(model, maker) {
      super(model);
      this.maker = maker;
    }
  
    display() {
      console.log(`이 차는 ${this.model}의 ${this.model}입니다.`);
    }
  }
  
  // 자식 클래스 정의
  class Electric extends Vehicle {
    constructor(model, maker) {
        super(model);
        this.maker = maker;
      }
  
      display() {
      console.log(`이 전기차는 ${this.model}의 ${this.model}입니다.`);
    }
  }
  

  
  const sonata = new Sedan('Sonata', 'Hyundai');
  const modelX = new Electric('ModelX', 'Tesla');

  sonata.display(); // 이 차는 Sonata의 Sonata입니다.
  modelX.display(); // 이 전기차는 ModelX의 ModelX입니다.

 

 

📌 4) 다형성(Polymorphism)

 다형성은 말그대로 하나의 객체가 '다양한 형태'로 변할 수 있는 능력을 입니다. 무슨말이냐면 예를 들어 '마신다'는 동작은 커피, 주스, 물 등 어떤 객체를 만나느냐에 따라 다양한 방식으로 변할 수 있습니다.  즉, javscript에서 다형성이란 여러 객체가 같은 메서드를 호출하더라도 각 객체에 따라 다른 동작을 할수있는 능력을 의미합니다. 주로 위에서 본 상속과 오버라이딩에의해서 구현되며, 이를 통해 코드의 유연성과 확장성을 높여줍니다. 

class Animal {
  constructor(name) {
    this.name = name;
  }
  
  makeSound() {
    return "동물 소리를 내다";
  }
}

class Dog extends Animal {
  makeSound() {
    return "멍멍!";
  }
}

class Cat extends Animal {
  makeSound() {
    return "야옹!";
  }
}

class Chicken extends Animal {
  makeSound() {
    return "꼬끼오!";
  }
}

// 다형성을 활용하여 다양한 동물 객체를 생성하고 메서드를 호출합니다.
const animals = [new Dog("강아지"), new Cat("고양이"), new Chicken("닭")];

animals.forEach(animal => {
  console.log(`${animal.name}이(가) ${animal.makeSound()} 소리를 내고 있습니다.`);
});

위의 코드에서는  Animal 클래스를 상속하는 Dog, Cat, Chicken클래스를 정의하였습니다. 그리고 각각의 클래스에서 makeSound 메서드를 오버라이딩하는 방법으로 다형성을 구현하였습니다.