[TypeScript] 클래스 본문

Web/TypeScript

[TypeScript] 클래스

미니모아 2022. 8. 17. 18:44
반응형

클래스

OOP?

Work with (real-life) Entities in your code

Class & Instnaces

  • Objects : The things you work with in code (Instance of classes)
  • Classes : blueprints for objects
    • 객체를 정의
    • 다수의 객체를 생성

Class 생성하기

class Department {
  //class field
  name:string;

  //생성자 함수
  constructor(n: string) {
    this.name = n;
  }
}


const accounting = new Department('Accounting');
console.log(accounting);

자바스크립트로 컴파일하기

"use strict";
class Department {
    constructor(n) {
        this.name = n;
    }
}
const accounting = new Department('Accounting');
console.log(accounting);
//# sourceMappingURL=app.js.map

public, private

클래스 필드에 배열 요소를 추가해보자

class Department {
  //class field
  name:string;
  employees:string[] = [];

  //생성자 함수
  constructor(n: string) {
    this.name = n;
  }

  describe(this: Department) {
    console.log('Department: ' + this.name);
  }

  addEmployee(employee: string) {
    this.employees.push(employee);
  }

  printEmployeeInformation() {
    console.log(this.employees.length);
    console.log(this.employees);
  }
}

이 경우 addEmployee 메소드를 이용해서 employees에 접근해 값을 추가하는 것 뿐만아니라 외부에서도 employees 배열을 참조하며 변경할 수 있다는 문제가 발생한다.

accounting.addEmployee('Max');
accounting.addEmployee('Manu');

accounting.employees[2] = 'Anna'; //외부에서 참조할 수 있다는 문제 발생

(자바스크립트에는 public의 개념만 있고 private의 개념은 없기 때문에 타입스크립트 컴파일 도중에는 중단 에러를 발생시키지만 이를 자바스크립트로 컴파일하면 에러 없이 런타임을 수행할 수 있다.)

이 경우 private 키워드를 사용하여 클래스 내부에서만 해당 프로퍼티에 접근할 수 있도록 지정할 수 있다.

private employees:string[] = [];

이와 반대로 public 키워드를 이용하여 외부에서도 참조할 수 있는 프로퍼티를 만들 수 있다. 기본값은 public으로 지정된다.

클래스 프로퍼티 초기화 축약

생성자 함수의 인자로 제어 제어자와 타입을 지정해주면 자동으로 동일한 프로퍼티가 생성된다.

class Department {
  //class field
  // private id: string;
  // private name:string;
  private employees:string[] = [];

  //생성자 함수
  constructor(private id: string, public name: string) {
    // this.id = id;
    // this.name = n;
  }

  describe(this: Department) {
    console.log(`Department (${this.id}) : ${this.name}`);
  }

읽기 전용

초기화 후에 변경되어서도 안되는 특정 필드가 있는 경우 readonly를 추가할 수있다. 읽기 전용이므로 초기화 중에 한 번만 사용할 수 있다.

constructor(private readonly id: string, public name: string) {}

상속

각 인스턴스 마다 고유의 특정 속성과 메소드를 갖는 경우 상속을 이용해 구현할 수 있다.

class ITDepartment extends Department { //Department 클래스를 상속
  admins: string[];
  constructor(id: string, admins: string[]) {
    super(id, 'IT'); // 부모 클래스 생성자 호출
    this.admins = admins;
  }
}
const it = new ITDepartment('d1', ['Max']);

it.addEmployee('Max');
it.addEmployee('Manu');

console.log(it);
it.describe();

protected

외부에서 변경이 불가능하지만 해당 클래스를 확장하는 모든 클래스에서도 사용 가능하게 하기 위해서는 protected 키워드를 사용할 수 있다.

class AccountingDepartment extends Department {
  constructor(id: string, private reports: string[]) {
    super(id, 'IT');
  }

  addEmployee(name: string) { //부모 메소드 오버라이드
    if (name === 'Max') return;
    this.employees.push(name);
  }

  addReport(text: string) {
    this.reports.push(text);
  }

  printReports() {
    console.log(this.reports);
  }
}

Getter, Setter

Getter

값을 가지고 올 때 함수나 메소드를 실행하는 속성으로 private 속성에 접근할 수 있도록 해준다. get 키워드를 사용하며 반드시 값을 리턴해야한다.

class AccountingDepartment extends Department {

  private lastReport: string;

  get mostRecentReport() {
    if(this.lastReport) {
      return this.lastReport; //반드시 return 해야함
    }
    throw new Error("No report found");

  }

 // ...

}

메소드가 아닌 프로퍼티로 접근할 수 있다.

const accounting = new AccountingDepartment('d2', []);
accounting.addReport('Something went wrong ...');
console.log(`mostRecentReport : ${accounting.mostRecentReport}`); //메소드로 접근 x, 프로퍼티로 접근해야함

Setter

getter와 마찬가지로 private 변수의 값을 설정하는 역할을 하며 set 키워드를 사용한다.

class AccountingDepartment extends Department {

  private lastReport: string;

  set mostRecentReport(value: string) {
    if (!value) {
      throw new Error('Please pass in valid value!');
    }
    this.addReport(value);
  }
  // (...)

}

마찬가지로 프로퍼티로 접근해야한다.

accounting.mostRecentReport = 'Year End Report';

static property, method

클래스의 인스턴스에서 접근할 수 없는 속성과 메소드를 클래스에 추가할 때 사용한다.

class Department {
  // (...)

  static fiscalYear = 2020;
  static createEmployee(name: string) {
    return {
      name,
    };
  }

  //(...)
}

해당 메소드나 프로퍼티를 호출할 때에는 새 키워드 없이 직접 클래스에서 호출하므로 클래스를 그룹화 메커니즘으로 사용하는 것과 같다.

const employee1 = Department.createEmployee('Max');
console.log(employee1, Department.fiscalYear);

클래스 내부의 정적이 아닌 부분들에서 정적 속성에 접근할 수 없다. this는 클래스를 기반으로 생성된 인스턴스를 참조하기 때문이다. 정적 속성은 인스턴스에서 유효하지 않다. 정적 속성과 정적 메소드의 전체적인 개념은 인스턴스와 분리되어 있기 때문이다.

constructor(private readonly id: string, public name: string) {
    console.log(this.fiscalYear); // error
    console.log(Department.fiscalYear); // no error
  }

추상 클래스

특정 클래스를 사용하거나 확장시킬 때 특정 메소드를 구현하거나 재정의하도록 해야하는 경우 기본 클래스에 추상화 메소드의 기본형만 정의해놓고 해당 클래스를 상속하는 모든 자식 클래스에서 해당 메소드를 재정의해서 쓰도록 할 수 있다.

abstract class Department {
    // (...)
    abstract describe(this: Department): void;
}

추상 메소드가 하나라도 있으면 해당 클래스에 abstract 키워드를 붙여야한다.

자식 클래스에서는 해당 메소드를 무조건 재정의해야한다.

class ITDepartment extends Department {
  // (...)

  describe(): void {
    console.log('IT Department - ID: ' + this.id);
  }
}

따라서 추상 클래스는 일부 상위 클래스를 기반으로 하는 모든 클래스가 일부 공통 메소드 또는 속성을 공유하도록 하는 경우 사용할 수 있다.

추상클래스는 자체적으로 인스턴스화 할 수 없고 기본적으로 상속되어야하는 클래스가 된다.

singleton & private constructor

싱글톤 패턴이란 특정 클래스의 인스턴스를 정확하게 하나만 갖도록 하는 패턴으로 정적 메소드나 속성을 사용할 수 없거나 사용하지 않고자 하는 동시에 클래스를 기반으로 정확히 하나의 객체만 가질 수 있도록 하고자할 때 사용할 수 있다.

싱글톤 패턴을 구현하기 위해서는 private constructor를 사용한다.

private constructor(id: string, private reports: string[]) {
    super(id, 'IT');
    this.lastReport = reports[0];
  }

이 경우 클래스 내부에서만 생성자 함수에 접근할 수 있어진다.

클래스 내부에 정적 변수로 인스턴스를 만든 후 정적 메소드로 인스턴스 생성 메소드를 구현한다.

static getInstance() {
    if(AccountingDepartment.instance) {
      return this.instance
    }
    this.instance = new AccountingDepartment('d2', []);
    return this.instance; 
  }

그렇기 때문에 AccountingDepartment 의 인스턴스는 정확히 한 개만 생성된다.

const accounting = AccountingDepartment.getInstance();
const accounting2 = AccountingDepartment.getInstance();

콘솔로그를 통해 accountingaccounting2는 같은 인스턴스라는 것을 확인할 수 있다.

반응형

'Web > TypeScript' 카테고리의 다른 글

[TypeScript] 고급타입  (0) 2022.08.24
[TypeScript] 인터페이스  (0) 2022.08.19
[TypeScript] TypeScript 컴파일러  (0) 2022.08.12
[TypeScript] Literal Type, Type alias, void, 함수, Unknown, Never  (0) 2022.08.10
Enum, Any, Union  (0) 2022.08.04
Comments