일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Kubernetes
- 코딩테스트
- Spring
- bean
- OOP
- java
- javascript
- 알고리즘
- Interceptor
- html
- GraphQL
- 인접리스트
- css
- REST API
- LifeCycle
- 인접행렬
- puppeteer
- JWT
- 탐욕법
- 프로그래머스
- 자료구조
- Deep Dive
- typescript
- node.js
- nestjs
- Linux
- MySQL
- TIL
- winston
- dfs
- Today
- Total
처음부터 차근차근
[Typescript] Class 본문
Typescript Class와 Javascript Class의 차이점
- Javascript에는 접근 제어자가 없습니다.
- But 최신 브라우저와 최신 Node.js(버전 12 이상)에서는 지원합니다.
- Javascript에는 추상 클래스가 없습니다.
- Javascript에는 인터페이스가 없습니다. 따라서 다중 구현이 불가능합니다.
Typescript에서는 이 부분이 보완되어 조금 더 강력하게 객체 지향 코드를 구현할 수 있게 되었습니다.
Class 속성 접근 제어자
- public : 클래스 외부에서 접근 가능 (기본 값으로 생략 가능합니다.)
- private : 해당 클래스 내부에서만 접근 가능합니다.
- protected : Book 클래스를 포함한 서브(자식) 클래스에서만 접근 가능합니다.
class Book {
// 제목
// public: 클래스 외부에서 접근 가능
public title:string;
// 저자
// public은 기본 값으로 생략 가능합니다.
author:string;
// 제조 공장
// private: Book 클래스 내부에서만 접근 가능
private _manufacturing_plant:string;
// 종이 유형
// protected: Book 클래스를 포함한 서브 클래스에서만 접근 가능
protected paper_type:string;
// constructor() 매개변수 앞에
// public, private, protected를 사용하면
// Book 클래스의 타입(type, 속성)을 별도 선언하지 않아도 됩니다.
constructor(title:string, author:string, public pages:number) {
this.title = title;
this.author = author;
this.pages = pages;
}
}
/* 인스턴스 생성 ------------------------------------------------ */
let indRevo = new Book('한 권으로 정리하는 4차 산업혁명', '최진기', 367);
console.log(indRevo); // Book {}
또한 constructor() 매개변수 앞에 접근 제어자를 사용하면, 속성을 별도로 선언하지 않아도 됩니다.
접근 제어 메서드 설정
속성과 마찬가지로 메서드 또한 접근 제어자를 사용해 외부에서의 접근을 제어할 수 있습니다.
class Book {
public title:string;
public author:string;
public pages:number = 150;
private _manufacturing_plant:string = '충무로 공장';
protected paper_type:string = '밍크지';
constructor(title:string, author:string, pages:number) {
this.title = title;
this.author = author;
this.pages = pages;
}
/* 메서드 ------------------------------------------------ */
// public 메서드
// 클래스 외부에서 접근 가능
public printPages(): string {
return `${this.pages}페이지`;
}
// protected 메서드
// Book 클래스를 포함한 서브 클래스에서만 접근 가능
protected changePaperType(type:string): void {
this.paper_type = type;
}
// private 메서드
// Book 클래스 내부에서만 접근 가능
private setManufacturingPlant(plant:string): void {
this._manufacturing_plant = plant;
}
/* 클래스 내부 메서드에서 private, protected 메서드 접근 가능 */
public setPaperType(type:string):void {
// protected 메서드 접근 가능
this.changePaperType(type);
console.log(this.paper_type);
}
public setPlant(plant:string):void {
// private 메서드 접근 가능
this.setManufacturingPlant(plant);
console.log(this._manufacturing_plant);
}
}
/* 인스턴스 생성 ------------------------------------------------ */
let indRevo = new Book('한 권으로 정리하는 4차 산업혁명', '최진기', 367);
console.log(indRevo.printPages()); // '367페이지'
// [오류]
// [ts] 'changePaperType' 속성은 보호된 속성이며
// 'Book' 클래스 및 해당 하위 클래스 내에서만 액세스할 수 있습니다.
// (method) Book.changePaperType(type: string): void
console.log(indRevo.changePaperType('인디언지'));
// [오류]
// [ts] 'setManufacturingPlant' 속성은 private이며
// 'Book' 클래스 내에서만 액세스할 수 있습니다.
// (method) Book.setManufacturingPlant(plant: string): void
console.log(indRevo.setManufacturingPlant('파주 공장'));
상속
Typescript는 ES6에서와 마찬가지로 클래스 상속에 extends 키워드를 사용합니다.
// Book 수퍼 클래스를 상속 받은 E_Book 클래스
class E_Book extends Book {
// paper_type 오버라이딩
paper_type = '스크린';
}
Constructor, super
일반적으로 부모 클래스를 상속받은 자식 클래스는 부모 클래스의 기능에 더하여 좀 더 많은 기능을 갖도록 설계합니다. constructor()를 사용해 상속 받은 부모 클래스의 생성자를 자식 클래스의 생성자로 덮어쓸 수 있습니다. 이때 반드시 super()를 사용해 부모 클래스의 생성자에 요구되는 인자를 전달해야 합니다.
class E_Book extends Book {
paper_type = '스크린';
constructor(
title:string,
author:string,
pages:number,
public is_downloadable:boolean
){
// 수퍼 클래스 constructor를 덮어쓰기 위해서는 super() 실행이 필요합니다.
super(title, author, pages);
this.is_downloadable = is_downloadable;
}
}
부모 클래스의 protected 속성은 서브 클래스에서 접근 가능하지만, private 속성은 접근이 불가능합니다.
class E_Book extends Book {
constructor(
title:string,
author:string,
pages:number,
public is_downloadable:boolean
){
super(title, author, pages);
this.is_downloadable = is_downloadable;
// 수퍼 클래스의 protected 속성은 접근 가능
console.log(this.paper_type);
// 수퍼 클래스의 private 속성은 접근 불가능
// [오류]
// [ts] '_manufacturing_plant' 속성은 private이며 'Book' 클래스 내에서만 액세스할 수 있습니다.
// (property) Book._manufacturing_plant: string
console.log(this._manufacturing_plant);
}
}
getter/setter
비공개로 설정할 필요가 있는 속성은 private로 설정한 후, 이 속성에 접근하여 값을 읽거나, 쓰기 위한 Getter, Setter 함수를 사용하여 속성을 정의할 수 있습니다.
참고 : 자바스크립트에서 비공개 속성 변수 앞에는 _(언더스코어)를 붙여 비공개 속성이라고 정의합니다.
class Plant {
// 비공개 속성 '종(Species)'
private _species:string|null = null;
// getter 함수
get species(): string {
return this._species;
}
// setter 함수
set species(value:string) {
if ( value.length > 3 ) { this._species = value; }
}
}
/* 인스턴스 생성 ------------------------------------------------ */
let plant = new Plant();
console.log(plant.species); // null
plant.species = '줄기';
console.log(plant.species); // null
plant.species = '푸른 식물';
console.log(plant.species); // '푸른 식물'
아니면 자바처럼 이렇게 해도 됩니다.
class Plant {
// 비공개 속성 '종(Species)'
private species:string|null = null;
// getter 함수
get getSpecies(): string | null {
return this.species;
}
// setter 함수
// 참고로 set에는 타입을 지정해주지 않습니다.
// void도 적용 불가능합니다.
set setSpecies(value:string) {
if ( value.length > 3 ) { this.species = value; }
}
}
JavaScript(ES5)로 컴파일된 코드를 살펴보면 Object.defineProperty()를 사용하여 get, set 함수를 사용하여 동일하게 작동하도록 처리한 것을 확인할 수 있습니다.
var Plant = /** @class */ (function () {
function Plant() {
this._species = 'Default';
}
Object.defineProperty(Plant.prototype, "species", {
// getter
get: function () {
return this._species;
},
// setter
set: function (value) {
if (value.length > 3) {
this._species = value;
}
},
enumerable: true,
configurable: true
});
return Plant;
}());
/* 인스턴스 생성 ------------------------------------------------ */
var plant = new Plant();
console.log(plant.species); // null
plant.species = '줄기';
console.log(plant.species); // null
plant.species = '푸른 식물';
console.log(plant.species); // '푸른 식물'
Static 속성, 메서드
정적 속성, 메서드라고도 하며, 클래스를 통해 인스턴스를 생성할 필요 없이, 클래스의 속성 또는 메서드를 사용하고자 한다면 static 키워드를 사용해 속성, 메서드를 정의합니다.
class Mathmatics {
// 스태틱 속성
static PI:number = Math.PI;
// 스태틱 메서드
// circumference = 둘레(원주)
static calcCircumference(radius:number) :number {
return this.PI * radius * 2;
}
static calcCircleWidth(radius:number): number {
return this.PI * Math.pow(radius, 2);
}
}
// radius = 반지름
let radius = 4;
console.log('PI(원주율) = ', Mathmatics.PI);
console.log(`반지름이 ${radius}인 원의 넓이: πr² = `, Mathmatics.calcCircleWidth(radius));
console.log(`반지름이 ${radius}인 원의 둘레: 2πr = `, Mathmatics.calcCircumference(radius));
추상 클래스
추상화
추상 클래스를 정의할 때는 class 앞에 abstract라고 표기합니다. 또한 추상 메서드를 정의할 때도 abstract를 메서드 이름 앞에 붙입니다. 추상 메소드는 정의만 있을 뿐 몸체(body)가 구현되어 있지 않습니다. 몸체는 추상 클래스를 상속하는 클래스에서 해당 추상 메소드를 통해 필히 구현해야 합니다.
그리고 추상 클래스는 추상 메서드 뿐만 아니라, 실 사용이 가능한 메서드도 정의할 수 있습니다. 추상 클래스를 상속하는 클래스를 통해 생성된 인스턴스는 이 메서드를 사용할 수 있습니다. 추상 클래스는 말 그대로 추상이므로 클래스와 달리 인스턴스를 생성하지 않습니다. 생성 구문을 사용하면 오류가 발생합니다.
// 추상 클래스
abstract class Project {
public project_name:string|null = null;
private budget:number = 2000000000; // 예산
// 추상 메서드 정의
public abstract changeProjectName(name:string): void;
// 실제 메서드 정의
public calcBudget(): number {
return this.budget * 2;
}
}
// [오류]
// [ts] 추상 클래스의 인스턴스를 만들 수 없습니다.
// constructor Project(): Project
let new_project = new Project();
Project 추상 클래스를 상속 받은 WebProject 클래스는 추상 클래스 내에 정의된 추상 메서드를 반드시 구현해야 합니다. 구현하지 않을 경우 다음과 같은 오류 메시지를 출력합니다.
// 클래스 ⟸ 추상 클래스 상속
class WebProject extends Project {
// [오류]
// [ts] 비추상 클래스 'WebProject'은(는) 'Project' 클래스에서 상속된
// 추상 멤버 'changeProjectName'을(를) 구현하지 않습니다.
// class WebProject
}
그러므로 추상 클래스를 상속하는 클래스는 추상 클래스에 정의된 메서드를 반드시 구현해야 합니다.
class WebProject extends Project {
// 추상 클래스에 정의된 추상 메서드 구현
changeProjectName(name:string): void {
this.project_name = name;
}
}
/* 인스턴스 생성 ------------------------------------------------ */
let new_project = new WebProject();
console.log(new_project.project_name); // null
new_project.changeProjectName('CJ 올리브 네트웍스 웹사이트 개편');
console.log(new_project.project_name); // 'CJ 올리브 네트웍스 웹사이트 개편'
싱글톤 패턴
private 접근 제어자를 사용해 constructor() 앞에 붙이면 new 키워드를 통해 인스턴스를 생성하지 못하도록 제한할 수 있습니다. 대신 공개된 스태틱 메서드 getInstance()를 통해 오직 한 번만 인스턴스를 생성할 수 있습니다. 이를 싱글턴 패턴이라 부릅니다.
class OnlyOne {
private static instance: OnlyOne;
public name:string;
// new 클래스 구문 사용 제한을 목적으로
// constructor() 함수 앞에 private 접근 제어자 추가
private constructor(name:string) {
this.name = name;
}
// 오직 getInstance() 스태틱 메서드를 통해서만
// 단 하나의 객체를 생성할 수 있습니다.
public static getInstance() {
if (!OnlyOne.instance) {
OnlyOne.instance = new OnlyOne('싱글턴 객체');
}
return OnlyOne.instance;
}
}
/* 인스턴스 생성 ------------------------------------------------ */
// [오류]
// [ts] 'OnlyOne' 클래스의 생성자는 private이며 클래스 선언 내에서만 액세스할 수 있습니다.
// constructor OnlyOne(name: string): OnlyOne
let bad_case = new OnlyOne('오류 발생');
let good_case = OnlyOne.getInstance();
읽기 전용 속성
readonly 키워드를 사용해 클래스 속성 이름 앞에 추가하면 읽기 전용 속성이 되어 속성을 다른 값으로 쓸 수 없습니다. 다른 값을 설정하려고 시도하면 컴파일 과정에서 다음과 같은 오류 메시지를 출력합니다.
class OnlyOne {
private static instance:OnlyOne;
// 읽기 전용 속성 설정
public readonly name:string;
private constructor(name:string) {
this.name = name;
}
public static getInstance(name:string):OnlyOne {
if (!OnlyOne.instance) {
OnlyOne.instance = new OnlyOne(name);
}
return OnlyOne.instance;
}
}
/* 인스턴스 생성 ------------------------------------------------ */
let special_one = OnlyOne.getInstance('스페셜 원');
console.log(special_one.name);
// [오류]
// [ts] 상수 또는 읽기 전용 속성이므로 'name'에 할당할 수 없습니다.
// (property) OnlyOne.name: string
special_one.name = '노멀 원';