방명록
- [TypeScript] 유데미 강의 "Typescript :기초부터 실전형 프로젝트까지 with React + NodeJS" 정리(섹션 5: 클래스 & 인터페이스)2024년 01월 24일 11시 19분 08초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
섹션 5: 클래스 & 인터페이스
📌 OOP(Object-oriented Programming)
코드에서 실제 사물과 최대한 유사한 객체를 사용해 코드를 이해하기 쉽도록 만드는 것!
📌 class == blueprint
청사진: 아키텍처 또는 공학 설계를 문서화한 기술 도면을 인화로 복사하거나 복사한 도면
🤔 class와 instance의 관계에 대한 내 생각
붕어빵틀과 붕어빵, 필름과 사진, 도장과 도장 찍기📌 this
class Department { name: string; // 필드라고 함. 객체가 아님. // 생성자: 생성하는 객체(인스턴스)의 초기화 작업 constructor(n: string) { this.name = n; } describe() { console.log("Department: " + this.name); } } const accounting = new Department("Accounting"); // 키와 값을 가진 객체 생성 accounting.describe(); // 출력 결과: Department: Accounting const accountingCopy = { describe: accounting.describe }; accountingCopy.describe(); // 출력 결과: Department: undefined // this가 가리키는 accountingCopy에 name 속성이 없기 때문
위의 코드를 보면 accounting과 accountingCopy 객체의 describe 메서드의 출력 결과가 다른 것을 볼 수 있다. 그 이유는 this가 각각 다른 객체를 가리키고 있기 때문이다. 아래 코드는 this 문제를 해결한 코드이다.
class Department { name: string; constructor(n: string) { this.name = n; } // describe() { // console.log("Department: " + this.name); // } // this를 인수로 넣고 타입을 Department 클래스로 하였다. // 이 인수를 넣지 않아도 Department: DUMMY를 출력하지만 name에 string 타입이 아닌 다른 타입도 입력이 가능하기 때문에 인수와 타입을 지정하였다. describe(this: Department) { console.log("Department: " + this.name); } } const accounting = new Department("Accounting"); // 키와 값을 가진 객체 생성 console.log(accounting.name); accounting.describe(); // Department: Accounting // const accountingCopy = { name: 123, describe: accounting.describe }; // Department의 name의 타입이 string이기 때문에 name 속성의 값으로 숫자가 들어 올 수 없음 const accountingCopy = { name: "DUMMY", describe: accounting.describe }; // name 속성에 string 타입의 값을 추가 accountingCopy.describe(); // Department: DUMMY
📌 접근 제한자
접근 제한자는 public, private가 있다. public은 default이기 때문에 생략 가능하다. 접근 제한자 public, protected, private은 자바스크립트에는 없는 타입스크립트 문법이다.
class Department { private employees: string[] = []; // 접근 제한자 private 적용 addEmployee(employee: string) { this.employees.push(employee); } printEmployeeInformation() { console.log("직원 수 : ", this.employees.length); console.log("직원 명단 : ", this.employees); } } const accounting = new Department(); accounting.addEmployee("김일남"); accounting.addEmployee("김이남"); // accounting.employees[2] = "Anna"; // 접근 제한자 private 때문에 employees에 직접 접근할 수 없음 accounting.printEmployeeInformation();
📌 약식 생성자
생성자는 아래 코드와 같이 약식으로 작성이 가능하다.
// 주석 처리된 부분은 생성자 약식 표현 전 코드임 class Department { // private id: string; // private name: string; constructor(private id: string, public name: string) { // this.id = id; // this.name = n; } }
📌 "읽기 전용" 속성
readonly는-자바스크립트에서는 지원하지 않는-타입스크립트에서만 지원하는 기능이다.
class Department { // 생성자의 인수 id에 읽기 전용 속성 적용 constructor(private readonly id: string, public name: string) { } }
📌 상속
class Department { constructor(private readonly id: string, public name: string) { } } class ITDepartment extends Department { // 상속하면 super 키워드로 부모 클래스의 필드를 상속할 수 있다. // 이 예제에서는 생성자의 인수로 name 속성이 빠져있음을 유의하라! // name 속성이 빠져있지만 ITDepartment 클래스는 name 속성을 가지고 있기 때문에 super에서 name을-"IT"로-하드코딩하였다. constructor(id: string, admins: string[]) { super(id, "IT"); // 문자열 "IT" 주의 } }
📌 메서드 오버라이딩과 protected
class Department { // private 적용된 필드는 상속 받은 클래스에서는 사용할 수 없다. // protected는 외부의 액세스를 제한하면서도 "상속 받은 클래스"에서 사용할 수 있다. protected employees: string[] = []; // 접근 제한자를 protected로 변경 constructor(private readonly id: string, public name: string) {} addEmployee(employee: string) { this.employees.push(employee); } printEmployeeInformation() { console.log(this.employees.length); console.log(this.employees); } } class AccountingDepartment extends Department { constructor(id: string, private reports: string[]) { super(id, "Accounting"); } // 메서드 오버라이딩 addEmployee(name: string) { if (name === "김일남") { return; } this.employees.push(name); } } const accounting = new AccountingDepartment("d2", []); // employees 필드의 접근 제한자를 protected로 변경했기 때문에 추가 가능 accounting.addEmployee("김일남"); accounting.addEmployee("김이남"); accounting.printEmployeeInformation();
📌 getter, setter
getter, setter는-자바스크립트에는 없는-타입스크립트 문법이다.
class Department { constructor(private readonly id: string, public name: string) {} } class AccountingDepartment extends Department { private lastReport: string; // get, set 키워드를 이용함 // getter get mostRecentReport() { if (this.lastReport) { return this.lastReport; } throw new Error("No report found."); } // setter set mostRecentReport(value: string) { if (!value) { throw new Error("Please pass in a valid value!"); } this.addReport(value); } constructor(id: string, private reports: string[]) { super(id, "Accounting"); this.lastReport = reports[0]; } addReport(text: string) { this.reports.push(text); this.lastReport = text; } } const accounting = new AccountingDepartment("d2", []); // accounting.mostRecentReport = ""; // Uncaught Error: Please pass in a valid value! accounting.mostRecentReport = "Year End Report"; // setter로 값 변경 console.log(accounting.mostRecentReport); // getter로 접근 accounting.addReport("Something went wrong..."); // 메서드로도 접근 가능 console.log(accounting.mostRecentReport);
📌 static property, static method
class Department { static fiscalYear = 2024; // static property constructor(private readonly id: string, public name: string) { // console.log(this.fiscalYear); // this로 접근할 수 없음 console.log(Department.fiscalYear); // 2024 } static createEmployee(name: string) { return { name }; } } console.log(Department.fiscalYear); // 2024 console.log(Department.createEmployee("김일남")); // {name: '김일남'}
📌 추상 클래스
추상 클래스는 인스턴스를 생성할 수 없고, 자식 클래스가 상속받는 클래스이다. 추상 클래스 내 추상 메서드를 자식 클래스가 오버라이딩하도록 강제한다.
// 클래스에 abstract 키워드 붙임 abstract class Department { constructor(protected readonly id: string, public name: string) {} // 오버라이딩을 강제할 메서드에 abstract 키워드 붙임 abstract describe(this: Department): void; } class ITDepartment extends Department { admins: string[]; constructor(id: string, admins: string[]) { super(id, "IT"); this.admins = admins; } // 오버라이딩 describe() { console.log("IT Department - ID: " + this.id); } } class AccountingDepartment extends Department { private lastReport: string; constructor(id: string, private reports: string[]) { super(id, "Accounting"); this.lastReport = reports[0]; } // 오버라이딩 describe() { console.log("Accounting Department - ID: " + this.id); } } const it = new ITDepartment("d1", []); it.describe(); // IT Department - ID: d1 const accounting = new AccountingDepartment("d2", []); accounting.describe(); // Accounting Department - ID: d2
📌 싱글톤(Singleton) 패턴
싱글톤 패턴은 특정 클래스의 인스턴스를 1개만 생성되는 것을 보장하는 디자인 패턴이다. getter를 활용하여 정의한다.
class Department { constructor(protected readonly id: string, public name: string) {} } const d1 = new Department("A", "김일남"); const d2 = new Department("B", "김이남"); console.log(d1 === d2); // false // 싱글톤(Singleton) 패턴 // 특정 클래스의 인스턴스를 1개만 생성되는 것을 보장하는 디자인 패턴 class AccountingDepartment { // 하나의 인스턴스를 할당할 정적 필드 private static instance: AccountingDepartment; constructor(protected readonly id: string, public name: string) {} // 정적 메서드 getInstance를 통해서만 AccountingDepartment의 인스턴스를 생성할 수 있음 static getInstance(id: string, name: string) { // 만약 기존에 생성된 인스턴스가 있다면 기존 인스턴스를 반환 if (AccountingDepartment.instance) { return this.instance; } // 기존 생성된 인스턴스가 없다면 인스턴스를 생성 this.instance = new AccountingDepartment(id, name); return this.instance; } } const a1 = AccountingDepartment.getInstance("C", "김삼남"); const a2 = AccountingDepartment.getInstance("D", "김사남"); console.log(a1 === a2); // true // "김사남" 인스턴스는 생성되지 않음 console.log(a1); // AccountingDepartment {id: 'C', name: '김삼남'} console.log(a2); // AccountingDepartment {id: 'C', name: '김삼남'}
📌 인터페이스
- 인터페이스는 자바스크립트에는 없는 문법
- 개발 전용 기능으로서 컴파일시 버려짐
- 객체의 구조를 정의하는 데 사용
- 객체의 구조가 인터페이스의 구조와 일치하는지 타입을 확인함
interface Person { name: string; age: number; greet(phrase: string): void; } let user1: Person; user1 = { name: "김일남", age: 99, greet(phrase: string) { console.log(phrase + " " + this.name); }, }; user1.greet("Hi, there!"); // Hi, there! 김일남
📌 인터페이스와 클래스
- 객체의 구조를 정의하는 데 interface와 type을 모두 사용할 수 있지만 차이점이 있다.
- 인터페이스로 정의하면 객체 구조를 정의하고자 한다는 것을 명확하게 나타낼 수 있다. 따라서 객체 구조 정의에 있어서 인터페이서가 더 적합하다.
- 인터페이스는 클래스가 구현해야 하는 구조를 강제할 수 있다.
interface Greetable { name: string; greet(phrase: string): void; } class Person implements Greetable { name: string; age = 99; constructor(n: string) { this.name = n; } greet(phrase: string) { console.log(phrase + " " + this.name); } } let user1: Greetable; user1 = new Person("김일남"); user1.greet("Hi there - I am"); // Hi there - I am 김일남 console.log(user1); // {age: 99, name: '김일남'}
📌 인터페이스 상속
interface Named { readonly name: string; // readonly는 읽기 전용으로서 interface, type에서 사용 가능 } // extends 키워드로 상속 가능. 콤마(,)를 사용해 다수 인터페이스 상속 가능 interface Greetable extends Named { greet(phrase: string): void; } // (위의 코드와 같으므로 생략)
📌 함수 타입으로서의 인터페이스
// 아래와 같은 type 키워드로 함수 타입을 정의하는 경우가 더 많이 쓰이지만 // type AddFn = (a: number, b: number) => number; // interface로 함수 타입을 정의할 수 있다. 아래 예제를 보라! interface AddFn { (a: number, b: number): number; } let add: AddFn; add = (n1: number, n2: number) => { return n1 + n2; }; console.log(add(1, 2)); // 3
📌 Optional
interface Named { readonly name?: string; // 선택적 속성(Optional Properties) } interface Greetable extends Named { greet(phrase: string): void; } class Person implements Greetable { name?: string; // 선택적 속성(Optional Properties) age = 30; constructor(n?: string) { // 선택적 매개변수(Optional Parameter) this.name = n; } greet(phrase: string) { if (this.name) { console.log(phrase + " " + this.name); } else { console.log("Hi!"); } } } let user1: Greetable; user1 = new Person(); user1.greet("Hi there - I am"); // Hi! console.log(user1); // {age: 30, name: undefined}
728x90반응형'언어·프레임워크 > TypeScript' 카테고리의 다른 글
다음글이 없습니다.이전글이 없습니다.댓글