본문 바로가기
📌 Front End/└ TypeScript

[TypeScript] 클래스(Class)

by 쫄리_ 2024. 7. 31.
728x90
반응형

클래스(Class)

클래스는 객체를 생성하기 위한 템플릿으로서,

해당 클래스로부터 생성된 객체를 인스턴스라고 한다.

클래스를 사용하면 데이터와 해당 데이터에 대한 메서드를 하나로 묶어서

사용자 정의 타입을 생성할 수 있다.


클래스 구성요소(Members)

  1. 필드(field)
  2. 생성자(constructor)
  3. 메소드(method)

 

📝 클래스 선언

         클래스 몸체에 클래스 프로퍼티를 사전 선언

class 클래스명 {
  [private | protected | public] property name[?]: property type[...]
}
class 클래스명 {
  멤버필드명: 타입;

  constructor(매개변수명: 타입) {
    this.멤버필드명 = 매개변수명;
  }
  
  메소드명(this: 클래스명) {
    //this 사용
  }
}

new 클래스명(타입에 맞는 값);

필드 (field)

- 클래스 내부 변수. 보통 필드 보다는 '멤버 변수'로 많이 부른다.
- 클래스 내부에서 타입과 함께 선언.
필드는 클래스 내에서 생성자에 의해 반드시 초기화되어야 한다.

 

● ! 옵션 : 필드 이름 뒤에 !를 붙여주면 undefined로 자동 초기화된다.
             따라서 생성자에서 초기화 안해줘도 에러 안난다.

  readonly 옵션 : 필드 이름 앞에 readonly 옵션을 넣어주면 해당 필드는 인스턴스 생성 이후 변경 불가.
                           즉, 생성자 안에서만 초기화 가능.

class Point {
    x: number; 		// Error - 생성자 초기화 안해줌
    y!: number; 	// ! 옵션은 undefined로 자동 초기화
    readonly z: number 	// 인스턴스 생성 이후 변경 불가
}

 


생성자 (constructor)

- 필드 초기화 함수로, 함수명은 constructor로 고정된다.
new 연산자와 함께 호출되면서 새로운 객체를 생성한다. 이때 생성된 객체를 '인스턴스'
- 클래스 내애서 생성자는 오직 한개만 존재할 수 있다.  
this 는 클래스 자신을 말한다.

class Person {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  sayHello() {
    console.log(`안녕하세요! 제 이름은 ${this.name}이고, 나이는 ${this.age}살입니다.`);
  }
}

const person = new Person('Sohyun', 29);
person.sayHello();

 


메소드 (method)

- 클래스 내 생성자 이외의 함수들
- 함수 선언 시 'function'이 생략된 것에 주의

class Pdoint {
  x: number;
  y: number;
 
  // 생성자
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }

  // 메소드
  distance(): number {
    return Math.pow(this.x - this.y, 2);
  }
}

 


인스턴스 생성

클래스는 데이터와 이를 조작하는 메서드 등을 하나로 뭉쳐놓은 추상화된 '몸체'다. 

이는 아직 객체라 할수 없으며 객체화를 위해선 인스턴스 생성이 필요하다. 

 

인스턴스 생성은 new + 클래스명을 코드 하단에 입력하면 된다. 

클래스 입력 후 가장 하단에서 불러와야 인스턴스가 생성된다. (잠정적 데드존)

 

new 클래스명(); 을 통해,

new 클래스명('매개변수');

클래스 내부의 코드들을 호출하고 생성자(constructor)를 실행 및 초기화한다.

class 클래스명 {
  멤버필드명: 타입;

  constructor(매개변수명: 타입) {
    this.멤버필드명 = 매개변수명;
  }
}

new 클래스명(타입에 맞는 값);

 


캡슐화 

  • Private Field (#필드명)
  • Accessors (접근자) : Getter & Setter
class Fruit {
    #name: string;
    #price: number;

    constructor(name:string, price:number){
        this.#name = name;
        this.#price = price;
    }

    set Name(name:string) { this.#name = name; }
    set Price(price:number) { this.#price = price; }

    get Name():string { return this.#name; }
    get Price():number { return this.#price; }
}

let fruit: Fruit = new Fruit("딸기", "5000");
fruit.Name = "멜론";     // fruit.Name("멜론");
console.log(fruit.Name);   // console.log(fruit.Name());

// '#name' 속성은 프라이빗 식별자를 포함하기 때문에 'Fruit' 클래스 외부에서 액세스할 수 없습니다.ts(18013)
// console.log(fruit.#name);

Private Field (#필드명)

ES6부터 클래스에 프라이빗 필드를 설정할 수 있다. 

앞에 #을 붙이면 설정이 가능한데 이는 클래스의 외부에서 사용뿐만 아니라 탐지가 불가능하게 된다.
이 뿐만 아니라 프라이빗 필드를 사용하면

클래스를 상속할 때 해당 필드에 값이 덮어씌워지는 것을 막을 수 있다.
프라이빗 필드는 클래스 내의 프로퍼티를 은닉하고 캡슐화하기 위해 존재한다.

 

# 접두사 : 변수를 선언할 때 #를 붙이면 해당 변수가 프라이빗 필드가 됩니다.


완전한 프라이버시 : 클래스 외부에서는 이 필드에 절대 접근할 수 없으며,
                                 심지어 클래스 인스턴스를 통해서도 접근이 불가능합니다.
                                 이 접근은 클래스 내부에서만 가능합니다.


접근 제한 : 이 필드에 접근하거나 수정하려면, 클래스 내에서만 가능합니다.
                   외부에서 접근하려고 하면 컴파일러가 오류를 발생시킵니다.

 

class MyClass {
    #privateField: string;

    constructor(value: string) {
        this.#privateField = value;
    }

    public getPrivateField() {
        return this.#privateField;
    }
}

const myInstance = new MyClass("Hello");
// console.log(myInstance.#privateField); // 오류 발생: '#privateField'은(는) private 필드이므로 클래스 외부에서 접근할 수 없습니다.
console.log(myInstance.getPrivateField()); // "Hello"

 


Accessors (접근자) : Getter & Setter

- 비공개로 설정하려는 속성은 private로 설정하고, 속성 값을 읽고 수정하는 getter/setter 함수를 사용한다.
- class의 속성에 직접 접근하는 것을 막고,

  getter / setter 함수를 사용해 값을 받아오거나 수정한다.
- 속성에 직접 접근해 수정하면 데이터 무결성이 깨질 수 있다. (캡슐화 권장)
- 각 객체의 멤버에 접근하는 방법을 세밀하게 제어할 수 있다.

- 괄호를 적을 필요 없이 get함수와 set함수는 일반 변수처럼 이용

 

📝 게터(getter) 형태 - 값 반환

// ▶정의
get 함수명() {
  return this.property명;
}

// ▶사용
// this  → 현재 class를 가리키는 참조 변수
// super → 부모 class를 가리키는 참조 변수
this.함수명;
super.함수명;

 

📝 세터(setter) 형태 - 값 변경

// ▶정의
set 함수명(매개변수명: 타입) {
  // property 수정 처리
}

// ▶사용
this.함수명 = value; 
super.함수명 = value;

 

 

사용예시

만약 프로퍼티에 제약조건을 적용한다고 가정하자.

이 때, Setter를 통해 인스턴스 초기화 전에 조건을 걸 수 있다.

또, private 프로퍼티에 접근해야 하는 경우, Getter 함수를 통해 이를 연동해야 한다.

* _[변수명] 은, private 프로퍼티를 정의하는 컨벤션으로 '_'(언더스코어)를 앞에 붙여준다.

class Employee {
	private _fullName: string;

	// Getter : private 프로퍼티 접근
	get fullName(): string {
		return this._fullName;
	}

	// Setter : name 길이가 10 미만인 경우만, _fullName 프로퍼티에 할당
	set fullName(newName: string) {
		if (newName && newName.length > 10) {
			throw new Error("fullName has a max length of ten");
 		}
		this._fullName = newName;
	}
}

let employee = new Employee();
employee.fullName = "Bob Smith";

if (employee.fullName) {
    console.log(employee.fullName);
}

 


🏷️ 접근제어자

해당 접근제어자를 사용한 변수에 접근할 수 있는 곳은 위와 같다.

  선언한 클래스 상속받은 클래스 인스턴스
public
protected
private

 


🏷️ this와 super의 차이

this

 

현재 class를 가리키는 참조 변수입니다.
현재 class 및 부모 class의 속성 및 method에 접근할 수 있습니다.
   단, 부모 Class의 private 접근 제한자를 가진 속성 및 method는 사용할 수 없습니다.

 

super

부모 class를 가리키는 참조 변수입니다.
public 또는 protected로 상속받은 method만을 사용할 수 있습니다. 직접 접근이 불가능합니다. 
  그래서 getter나 setter를 이용하여 속성을 제어해야 합니다. 

사용 이유?
  ① 자식, 부모 class의 속성 및 method 구분을 하는 데 사용합니다.
  ② overrding 할 때 편하게 하기 위해서 사용 가능합니다.


728x90
반응형