7 분 소요


1. Class 개요

1.1 Class 도입 배경

지금까지 객체 리터럴, 생성자함수를 사용하여 객체를 생성하는 방법을 배웠다.

  • 객체 리터럴은 단일 객체를 생성하기 때문에 비슷한 객체를 여러개 만들어야 해 번거롭다.
  • 생성자 함수는 생성자 함수를 상속할 때 프로토타입 체인을 조작해야 하기 때문에 복잡하다.

    → 따라서 ES6부터 생성자 함수를 좀 더 편하게 쓸 수 있도록 클래스 문법을 도입했다.


1.2 Class 개념

클래스와 객체

  • 붕어빵에 비유하자면 클래스는 붕어빵 틀, 객체는 붕어빵 틀에서 생성된 붕어빵, 각각의 붕어빵을 인스턴스라고 할 수 있다.

  • 클래스는 객체를 만들기 위한 설계도라고 생각할 수 있고, 이 설계도를 바탕으로 만들어진 실제 객체들은 인스턴스라고 생각할 수 있다.

→ 즉, 클래스는 객체를 생성하기 위한 일종의 템플릿인 것이다!



2. Class 생성

2.1 Constructor 개념

Constructor는 Class의 생성자 함수이다.

생성자 함수는 객체를 생성할 때 호출되며, 객체를 초기화하는 역할을 한다.

아래 코드에서 constructor 메소드는 name과 age를 인자로 받아 this.name과 this.age 속성을 초기화한다.

class Person {
  // constructor 키워드를 사용하여 정의한다.
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    console.log(
      `Hello, my name is ${this.name} and I'm ${this.age} years old.`
    );
  }
}

const person = new Person("Alice", 20);


2.2 Class 생성 예제

class를 생성하기 위해서는 class 키워드를 사용한다.

class Person {
  // (1) 클래스(설계도) 생성
  // Person을 생성하는 것이기 때문에,
  // age를 필수 요소로 넣어주자.

  constructor(name, age) {
    // (2) constructor(생성자 함수)로 인스턴스 초기화
    // 여기서 말하는 this는 만들어질 인스턴스(만들고자 하는 실체)를 의미한다.
    this.name = name;
    this.age = age;
  }

  // (3) 메서드 생성
  sayHello() {
    console.log(
      `안녕하세요, 제 이름은 ${this.name}이고, 나이는 ${this.age}입니다.`
    );
  }
}

// (4) 인스턴스(실제 사물) 생성
const person1 = new Person("sieun", 22);
const person2 = new Person("kori", 8);

// (5) 만든 객체를 토대로 메서드 호출하기
person1.sayHello(); // "안녕하세요, 제 이름은 sieun이고, 나이는 22입니다."
person2.sayHello(); // "안녕하세요, 제 이름은 kori이고, 나이는 8입니다."


2.3 Class 생성 연습

문제

  1. Car라는 새로운 클래스를 만들되, 처음 객체를 만들 때는 다음 네 개의 값이 필수로 입력돼야한다.

  2. modelName, modelYear, type, price
  3. makeNoise() 메서드를 만들어 클락션을 출력해보자.
class Car {
  constructor(modelName, modelYear, type, price) {
    this.modelName = modelName;
    this.modelYear = modelYear;
    this.type = type;
    this.price = price;
  }
  makeNoise() {
    console.log(`${this.modelName}가 클락션을 울리네요. 빵빵!`);
  }
}
const car1 = new Car("티코", "2002", "e", 5000);
const car2 = new Car("벤츠", "2024", "g", 3000);
const car3 = new Car("율무차", "2022", "t", 4500);

car1.makeNoise(); // "티코가 클락션을 울리네요. 빵빵!"
car2.makeNoise(); // "벤츠가 클락션을 울리네요. 빵빵!"
car3.makeNoise(); // "율무차가 클락션을 울리네요. 빵빵!"



3. Getter와 Setter

3.1 Getter와 Setter 개념

Class에서는 getter와 setter를 사용하여 Class의 속성에 접근할 수 있다.

getter는 속성 값을 반환하는 메소드이며, setter는 속성 값을 설정하는 메소드이다.

예를 들어, new 키워드로 생성한 사람이란 인스턴스가 나이라는 속성을 가질때, Class에서는 getter와 setter을 사용하여 외부로부터 값이 들어왔을 때 내부적으로 검증 후 세팅이 가능하다.

this로 접근하는 property에 underscore(_)를 사용해야해야 무한루프에 빠지지 않는다는 것을 기억하자!


3.2 Getter와 Setter 예제

// Getters와 Setters
// 객체지향 프로그래밍 언어 -> G, S
// 클래스 --> 객체(인스턴스)
// 프로퍼티(constructor)
// new Class(a, b, c)
class Rectangle {
  constructor(height, width) {
    // underscore : private(은밀하고, 감춰야 할 때)
    this._height = height;
    this._width = width;
  }

  // width를 위한 getter
  get width() {
    return this._width;
  }

  // width를 위한 setter
  set width(value) {
    // 검증 1 : value가 음수이면 오류!
    if (value <= 0) {
      //
      console.log("[오류] 가로길이는 0보다 커야 합니다!");
      return;
    } else if (typeof value !== "number") {
      console.log("[오류] 가로길이로 입력된 값이 숫자타입이 아닙니다!");
      return;
    }
    this._width = value;
  }

  // height를 위한 getter
  get height() {
    return this._height;
  }

  // height를 위한 setter
  set height(value) {
    // 검증 1 : value가 음수이면 오류!
    if (value <= 0) {
      //
      console.log("[오류] 세로길이는 0보다 커야 합니다!");
      return;
    } else if (typeof value !== "number") {
      console.log("[오류] 세로길이로 입력된 값이 숫자타입이 아닙니다!");
      return;
    }
    this._height = value;
  }

  // getArea : 가로 * 세로 => 넓이
  getArea() {
    const a = this._width * this._height;
    console.log(`넓이는 => ${a}입니다.`);
  }
}

// instance 생성
const rect1 = new Rectangle(10, 7);
rect1.getArea();
// const rect2 = new Rectangle(10, 30);
// const rect3 = new Rectangle(15, 20);


3.3 Getter와 Setter 연습

문제

  1. Getter와 Setter을 목차 2.3 Class 생성 연습에서 작성한 Car class에 적용해보자
  2. modelName, modelYear, price를 가져오는 메서드 getModelName, getModelYear, getPrice를 작성하자.
  3. 그리고 가격변동이 일어났을 때 사용할 수 있는 메서드인 setPrice도 작성하자
class Car {
  constructor(modelName, modelYear, type, price) {
    this._modelName = modelName;
    this._modelYear = modelYear;
    this._type = type;
    this._price = price;
  }

  get modelName() {
    return this._modelName;
  }

  // set은 입력값에 대한 검증까지 가능하다.
  set modelName(value) {
    // 유효성 검사
    if (value.length <= 0) {
      console.log("[오류] 모델명이 입력되지 않았습니다. 확인해주세요!");
      return;
    } else if (typeof value !== "string") {
      console.log("[오류] 입력된 모델명이 문자형이 아닙니다!");
      return;
    }

    // 검증이 완료된 경우에만 setting
    this._modelName = value;
  }

  get modelYear() {
    return this._modelYear;
  }

  set modelYear(value) {
    // 유효성 검사
    if (value.length !== 4) {
      console.log("[오류] 입력된 년도가 4자리가 아닙니다.확인해주세요!");
      return;
    } else if (typeof value !== "string") {
      console.log("[오류] 입력된 모델명이 문자형이 아닙니다!");
      return;
    }

    // 검증이 완료된 경우에만 setting
    this._modelYear = value;
  }

  get type() {
    return this._type;
  }

  set type(value) {
    if (value.length <= 0) {
      console.log("[오류] 타입이 입력되지 않았습니다. 확인해주세요!");
      return;
    } else if (value !== "g" && value !== "d" && value !== "e") {
      // 유효성 검사
      // g(가솔린), d(디젤), e(전기차)가 아닌 경우 오류
      console.log("[오류] 입력된 타입이 잘못되었습니다. 확인해주세요!");
      return;
    }

    // 검증 완료!
    this._type = value;
  }

  get price() {
    return this._price;
  }

  set price(value) {
    if (typeof value !== "number") {
      console.log("[오류] 가격으로 입력된 값이 숫자가 아닙니다. 확인해주세요!");
      return;
    } else if (value < "1000000") {
      console.log("[오류] 가격은 100만원보다 작을 수 없습니다. 확인해주세요!");
      return;
    }

    // 검증이 완료된 경우에만 setting
    this._price = value;
  }

  // 해당 자동차가 몇년도 모델인지 출력하는 메서드 작성
  printModelYear() {
    console.log(
      this._modelName + "" + this._modelYear + "년도의 모델입니다."
    );
  }
}

// 자동차 만들기
const car1 = new Car("티코", "2002", "e", 5000);
const car2 = new Car("벤츠", "2024", "g", 3000);
const car3 = new Car("율무차", "2022", "t", 4500);

// 출력
car1.modelName = 1; // "[오류] 입력된 모델명이 문자형이 아닙니다!"

// getter 예시
console.log(car1.modelName); // "티코"
console.log(car3.type); // "t"

// setter 예시
car1.modelName = 1; // "[오류] 입력된 모델명이 문자형이 아닙니다!"
car3.type = "t"; // "[오류] 입력된 타입이 잘못되었습니다. 확인해주세요!"



4. 상속 (Inheritance)

4.1 상속 개념

Class는 상속을 통해 다른 Class의 기능을 물려받을 수 있다.

상속을 받는 Class를 subclass 또는 derived class라고 하며, 상속을 하는 Class를 superclass 또는 base class라고 한다. (부모-자식)


4.2 상속 예제

class Animal {
  // 생성자 (이름을 필수로 받아야 한다.)
  constructor(name) {
    this.name = name;
  }

  // 메서드 (동물 행동 정의)
  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

// 동물 클래스를 상속받는 Dog 클래스를 생성
class Dog extends Animal {
  // 부모에게서 내려받은 메서드를 재정의
  speak() {
    console.log(`${this.name} 멍멍!`);
  }
}

// Dog를 만들 때는 Animal의 상속을 받은 class이기 때문에 이름을 필수로 받아야 한다.
let cuttyPuppy = new Dog("코리");

// speak는 "makes a noise"가 아니라, "멍멍!"이 출력된다.
cuttyPuppy.speak(); // "코리 멍멍!"


부모로부터 내려받아 재정의 하는 것을 오버라이딩이라고 한다.

위 코드에서 Dog Class는 Animal Class를 상속받았다. Dog Class에서 speak 메소드를 오버라이딩하여 Animal Class의 speak 메서드를 덮어쓴 것이다.


4.3 상속 연습

문제

  1. 상속을 목차 2.3 Class 생성 연습에서 작성한 Car class에 적용해보자.
  2. Car 클래스로부터 상속받아 전기자동차 클래스를 만들어보자.
  3. 전기차이기 때문에 constructor 상에 type은 필요 없을 것이다. 따라서 재정의가 필요하기 때문에 constructor을 사용해야한다.
class Car {
  constructor(modelName, modelYear, type, price) {
    this.modelName = modelName;
    this.modelYear = modelYear;
    this.type = type;
    this.price = price;
  }
  makeNoise() {
    console.log(`${this.modelName}가 클락션을 울리네요. 빵빵!`);
  }
}

// 전기차 class 정의 (상속)
class ElectronicCar extends Car {
  // constuctor로 재정의
  constructor(modelName, modelYear, price) {
    // Car(부모 class)에게도 재정의 한 사항 알려주기
    super(modelName, modelYear, "e", price);
  }
}

const eleCar1 = new ElectronicCar("테슬라", "2022", 10000);

eleCar1.makeNoise(); // "테슬라가 클락션을 울리네요. 빵빵!"


문제

  1. 작성한 코드를 토대로 전기자동차의 특성을 고려해서 충전시간도 추가해보자
class Car {
  constructor(modelName, modelYear, type, price) {
    this.modelName = modelName;
    this.modelYear = modelYear;
    this.type = type;
    this.price = price;
  }
  makeNoise() {
    console.log(`${this.modelName}가 클락션을 울리네요. 빵빵!`);
  }
}

// 전기차 class 정의 (상속)
class ElectronicCar extends Car {
  // constuctor로 재정의
  constructor(modelName, modelYear, price, chargeTime) {
    // Car(부모 class)에게도 재정의 한 사항 알려주기
    super(modelName, modelYear, "e", price);
    this._chargeTime = chargeTime;
  }

  set chargeTime(value) {
    this._chargeTime = value;
  }
  get chargeTime() {
    return this._chargeTime;
  }
}

const eleCar1 = new ElectronicCar("테슬라", "2022", 10000, 60);

console.log(eleCar1._chargeTime); // 60
eleCar1.chargeTime = 20;
console.log(eleCar1._chargeTime); // 20



5. Static Method

Static Method는 인스턴스를 만들 필요가 없을 때 사용한다.

즉, 인스턴스 간에 복제할 필요가 없는 데이터 (똑같은 것을 공유해서 쓸 때)에 사용한다.

Class는 static 키워드를 사용하여 Class 레벨의 메소드를 정의할 수 있다.

Class 레벨의 메소드는 인스턴스에서 호출할 수 없으며, Class 이름으로 직접 호출할 수 있다.

class Calculator {
  // constructor 생략 가능
  // constructor(a, b) {
  //   this._a = a;
  //   this._b = b;
  // }

  static add(a, b) {
    return a + b;
  }

  static subtract(a, b) {
    return a - b;
  }
}

console.log(Calculator.add(1, 2)); // 3
console.log(Calculator.subtract(3, 2)); // 1

댓글남기기