Home JavaScript와 TypeScript의 Class
Post
Cancel

JavaScript와 TypeScript의 Class

Class를 사용하는 이유

Class를 사용하여 객체를 생성하면 재사용성이 용이하고 코드의 중복을 피할 수 있다는 장점이 있습니다.

예를 들어 아무나 참여 가능한 익명 채팅 웹사이트를 만든다고 가정했을 때 Person이라는 class를 정의하게 된다면, 접속한 유저를 class로 생성하면 깔끔하고 간결하게 유저를 추가할 수 있습니다.

기본적인 Class의 형태

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person { 
  // Person class에서 사용할 변수 정의
  // 이렇게 class 내부에서 정의되는 변수를 property (프로퍼티)라고 합니다.
  name: string;
  age: number;
  gender: string;
  isHuman: boolean = true;
  
  // constructor(생성자) 설정
  // 이 값은 class가 선언될 때 받아야 할 필수 인자입니다.
  // ex) const xeros = new Person('name', 20, 'male');
  // constructor는 생략 가능합니다. 만약 아래 constructor가 생략되었다면 class 선언은 다음과 같아집니다.
  // ex) const xeros = new Person();
  constructor(name: string, age: number, gender:string) { 
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
}

// xeros라는 Person class 생성
const xeros = new Person('Xeros', 21, 'male');

console.log(xeros); // Person { name: 'Xeros', age: 21, gender: 'male', isHuman: true }

위와 같이 Person class를 선언하고 선언한 Person class를 이용해 “xeros” 라는 class형 객체를 찍어냈습니다.

이처럼 class는 빵틀과 빵으로 비유할 수 있습니다.

  • 빵틀 : class로 정의된 Person
  • 빵 : Person 으로 생성된 객체 xeros

class(빵틀)은 한 번만 정의해도 영구적으로 객체(빵)를 만들어낼 수 있습니다.

정의된 Person class로 더 많은 객체를 생성하기 위해선 아래 코드를 추가하면 됩니다.

1
2
3
4
5
const kim = new Person('kim', 30, 'male');
const lee = new Person('lee', 25, 'female');

console.log(kim);
console.log(lee);

위 코드를 추가하고 다시 한번 실행해보면 다음과 같은 결과가 출력됩니다.

1
2
3
4
Person { isHuman: true, name: 'Xeros', age: 21, gender: 'male', isHuman: true }
Person { isHuman: true, name: 'hello', age: 21, gender: 'male', isHuman: true }
Person { isHuman: true, name: 'kim', age: 30, gender: 'male', isHuman: true }
Person { isHuman: true, name: 'lee', age: 25, gender: 'female', isHuman: true }

프로퍼티 은닉

프로퍼티를 은닉하는 이유는 개발의 편의성과 코드를 깔끔하게 하기 위해 주로 사용합니다. 아래 예제를 살펴봅시다.

lee라는 class형 객체에 .을 찍어보면 코드 에디터의 인텔리전스 기능으로 사용 가능한 프로퍼티나 함수들이 노출됩니다.

만약 개발자가 직접 수정하거나 건드리지 않아도 되는 메서드들도 모두 노출된다면, 유지보수 측면에서 측면에서 좋지 않은 코드가 발생하게 되고, 직접 수정해선 안되는 메서드를 호출하여 수정하게 될 경우 문제가 될 수 있습니다.

이런 부분을 사전에 차단하기 위해 존재하는 기능이 바로 프로퍼티 은닉 기능입니다.

사실 자바스크립트 최신 버전인 ES2019 이전에는 자바스크립트에 프로퍼티 은닉 기능이 없어서 직접 수정해선 안되는 프로퍼티에는 _을 Prefix로 변수명 가장 앞에 붙여 암묵적인 규칙으로 사용했으나 이제 프로퍼티 은닉 기능을 정식으로 지원하므로 더이상 이런 꼼수를 사용하지 않아도 됩니다.

프로퍼티 혹은 함수를 은닉시키기 위한 방법

  • # Prefix : ES2019 이상 버전의 JavaScript 혹은 TypeScript에서 사용 가능 변수 앞에 # Prefix를 붙여 은닉
  • private 지시자 : 오직 TypeScript에서만 사용 가능 변수 앞에 private 지시자를 붙이면 은닉됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Person { 
  name: string;
  age: number;
  gender: string;
  // TypeScript에서만 허용되는 private 지시자
  private _isHuman: boolean = false;
  
  // TypeScript와 JavaScript 모두 허용되는 # Prefix (ES2019 이상에서만 사용 가능)
  // #_isHuman: boolean = true; // typescript
  // #_isHuman = true; // javascript

  constructor(name: string, age: number, gender:string) { 
    this.name = name;
    this.age = age;
    this.gender = gender;
    
    // Private 지시자를 사용했을 경우
    this._isHuman = true;
    
    // # Prefix를 사용했을 경우
    // this.#_isHuman = true;
  }
}

const xeros = new Person('Xeros', 21, 'male');

console.log(xeros); // Person { _isHuman: true, name: 'Xeros', age: 21, gender: 'male' }

위와 같이 코드를 작성했을 경우 코드 인텔리전스에 Public 프로퍼티인 name, age, gender만이 노출되고 은닉된 _isHuman 프로퍼티는 노출되지 않습니다.

Class 객체의 값 수정

Class 내부의 프로퍼티의 값을 읽거나 수정하는 방법에는 두가지가 있습니다. 일반적으로 간단한 직접 수정하는 방법이 있고, Private 처리된 은닉된 프로퍼티를 읽고 수정하기 위한 getter, setter가 존재합니다.

프로퍼티에 직접 접근하여 읽고 수정하는 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Person {
  private _name: string;

  constructor(name: string) { 
    this._name = name;
  }
  
  // 읽기
  console.log(xeros._name); // xeros
  // 쓰기
  xeros._name = 'new xeros'; // 'xeros' => 'new xeros'
  // 읽기
  console.log(xeros._name); // new xeros
}

const xeros = new Person('xeros');

console.log(xeros);

Getter, Setter

은닉된 프로퍼티의 값을 읽거나 쓰기 위해서는 getter 혹은 setter 함수를 만들어야 합니다. 대부분의 프로퍼티는 은닉시켜두고 getter, setter를 이용해 값에 접근하는것이 올바른 방법입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Person { 
  private _name: string;
  private _age: number;
  private _gender: string;
  private _isHuman: boolean = false;

  constructor(name: string, age: number, gender:string) { 
    this._name = name;
    this._age = age;
    this._gender = gender;
  }

  // Getter 함수
  get name():string { 
    return this._name;
  }

  // Setter 함수
  set name(name:string) { 
    this._name = name;
  }
}

const xeros = new Person('Xeros', 21, 'male');

// 읽기 (Getter 호출 함수)
console.log(xeros.name); // xeros
// 쓰기 
xeros.name = 'new xeros'; // xeros => new xeros
// 읽기
console.log(xeros.name); // new xeros

Class의 상속

상속을 사용하는 이유는 다음과 같은 예시를 들어 설명할 수 있습니다.

인간과 원숭이는 모두 동물이라는 범주에 속합니다. 이를 코드로 나타내면 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Animal { 
  name: string;
  height: number;
  weight: number;

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

  eat(): string { 
    return '냠냠';
  }

  sleep(): string { 
    return 'zzz';
  }
}

class Human extends Animal { 
  race: string = '';
}

class Monkey extends Animal { 
  race: string = '';
}

인간과 원숭이 모두 Animal이라는 class를 상속받습니다. Animal 이라는 class 내부에는 eat() 함수와 sleep() 함수가 선언되어있는데, 이는 인간과 원숭이가 동일하게 하는 행위 중 하나입니다.

즉 큰 범용성을 가진 하나의 class를 만들고 그 아래에 하위 class를 만들어 상속시키면 더 가독성이 좋은 코드로 만들 수 있게됩니다.

This post is licensed under CC BY 4.0 by the author.