타입스크립트를 하면서 부족했던 개념 공부하기

가은·2022년 11월 19일
0
post-thumbnail

블로그 진짜 오랜만에 씀 ㅋㅋㅋ...

자바스크립트를 쓰려면 타입스크립트를 써야한다길래 무작정 시작했는데 너무 재밌다.
근데 자스 개념이 부족해서 아직 이해가 안되는 부분들이 있는 것 같음

기술 블로그를 어떻게 써야할까에 대한 고민도 많이 했었는데 본격적으로 쓰시는 분들은 맞춤법 검사, 띄어쓰기 검사 다 돌리고 예제 코드도 직접 짜시던데,,, 개념 블로깅도 그렇게 해야하나? 고민이 많다.

아무튼 타입스크립트 강의가 절반 정도 끝났을 때 다시 한 번 상기시키는 기분으로 아좌좌...!

근데 다 자바스크립트 개념이 모질라서 해매는거 같음

⚠️ 이제 갓 배우는 독학 예비 개발자의 글입니다. 모든 개념에 의문을 가지고 읽으시길 바랍니다. (정확하지 않다는 뜻)


Class

자바스크립트는 클래스가 필요 없는 객체지향 프로그래밍 언어다.

✏️ 객체지향 프로그래밍은 무엇일까?
▪️ 애플리케이션을 어떻게 구축할 것인지, 논리적으로 어떻게 나눌 수 있는지에 대한 방법
▪️ 객체지향 방법으로 객체를 구성하면 앱이나 애플리케이션 로직을 로직의 일부 관리하는 객체로 분할이 가능하다.

그렇다면 클래스는 왜 쓰는가?

▪️ 타입스크립트의 클래스는 자바스크립트에 있는 객체들을 사용하는 개념과 결합하여 객체를 만들 수 있다.
→ 클래스를 사용하여 객체의 형태, 포함해야하는 데이터, 클래스를 기반으로 객체를 쉽게 만들 수 있도록 어떤 메소드가 필요한지 정의할 수 있다.
→ 이때 객체는 클래스 내의 인스턴스이다.

▪️ 클래스를 기반으로 로직을 생성하면 동일한 구조, 동일한 클래스를 기반으로 하는 동일한 메소드를 여러 객체로 빠르게 복사가 가능하다.

▪️ 클래스는 객체의 형태, 포함해야 할 속성과 메소드를 정의하는데 편리하다.

▪️ 객체에 저장된 정확한 데이터 세부 정보만 다르고 동일한 구조를 유지시킬 수 있다.

▪️ extends와 super 키워드로 명료한 상속 관계 구현이 가능하다.

한마디로 미친 재사용성과 유지보수의 용이함 때문이다.

하지만 난 아직까지 왜 함수를 두고 클래스를 쓰는지 잘 모르겠다.

클래스를 사용해보자

클래스를 정의할 땐 클래스임을 명확히 하기 위해 일반적으로 첫글자는 대문자로 표기한다.

자바스크립트에서 클래스는 표현식으로 정의가 가능하며, 값으로 사용될 수 있는 일급 객체이다. 클래스는 생성자 함수이며 new 연산자와 함께 호출되어 인스턴스를 생성한다.

// 자바스크립트 class 예제
class Person {}

const me = new Person();
console.log(me);	// Person {}

클래스와 인스턴스가 너무 헷갈렸는데 블로그에서 본 말이 조금 명확하게 해주는 것 같다.

블로그 출처

클래스는 특정한 기능을 가진 객체를 생성하게 해주는 설계도이고, 클래스로부터 만들어진 객체를 해당 클래스의 인스턴스라고 한다.

클래스 기본

class Department {
  // 생성자 전 클래스 필드이다.
  // 클래스를 기반으로 만들 객체(키)를 입력하고 마지막에 키가 갖게 될 값의 타입을 정의한다.
  name: string;

  // constructor는 생성자 메소드(예약어)이다.
  // 클래스와 연결되어 객체가 생성되면서 실행되는 클래스에 기반하여 만드는 모든 객체에 연결되는 함수를 의미한다.
  constructor (n:string) {
	this.name = n;
  }
  
  describe() {    // 메소드 생성
    console.log('Department: ' + this.name);
    // this는 일반적으로 생성된 클래스의 구체적인 인스턴스를 참조한다.
    // . 표기로 인스턴스의 모든 속성과 메소드에 접근이 가능하다.
  }
}

❓ 문제 1.

class Department {
  name: string;
  constructor (n: string) {
    this.name = n;
  }
  describe() {   
    console.log('Department: ' + this.name); 
  }
}

const accounting = new Department('Accounting');

accounting.describe();

const accountingCopy = { describe: accounting.describe };

accountingCopy.describe(); // Dapartment: undefined

복사한 것에 대해서는 왜 부서 이름이 출력되지 않을까? 해결 방법은 이미 강의를 통해 알고 있지만 여기서 내 궁금점은 this의 움직임이였다. (어차피 곧 쓸 내용임)

이해해보려고 했는데 알아볼 수 있는 사람이 있을까 싶지만 이 흐름이 맞는걸까?

아무튼 강의에서 undefined 해결은 다음과 같았다.

class Department {
    name: string;
    constructor (n: string) {
        this.name = n;
    }
    describe(this: Department) {    
      // this 추가, 값을 전달하지 않고도 describe() 호출 가능, ts는 this가 무엇으로 참조되어야 하는지 인식
      console.log('Department: ' + this.name); 
    }
}

const accounting = new Department('Accounting');

accounting.describe();

const accountingCopy = { name: 'Dummy', describe: accounting.describe };     
// 에러 해결을 위해 name 프로퍼티 추가 - 2 

// 매개변수 자리에 this: Department로 적으면 객체는 결국 Department 타입이 되어 에러 발생 - 1
accountingCopy.describe();     // Dapartment: Dummy

위 방법은 타입스크립트에서만 가능하다.

describe(this: Department)의 this는 리액트의 props 사용하는 것처럼 느껴졌는데 비슷한게 맞나?

클래스 조금 깊게 1단계

✔️ 클래스 필드 타입 선언

class Department {
  name: string;
  constructor (n: string) {}
}

위에서 클래스 필드와 생성자를 보면 타입 선언이 반복되는 것을 알 수 있다.
이는 아래와 같이 생성자에 몰아 쓸 수 있다.

class Department {  
  constructor (name: string, id: string) {}	// 프로퍼티가 두개 이상일땐 이렇게 쓴다.
}

✔️ 접근제어자
클래스에서는 접근제어자를 사용할 수 있다. 어디까지 접근할 수 있도록 하느냐를 의미한다.

  • public : 기본값, 어디서나 접근이 가능하다.
  • protected : 상속받은 하위클래스만 접근이 가능하다.
  • private : 선언한 클래스 내에서만 접근이 가능하다.

번외로 읽기 전용 속성(readonly)도 있다. 변경은 불가능하다.

class DepartmentData {
  constructor(protected readonly id: string, private name: string){}
}

✔️ 정적 메소드
클래스 필드에서 static 키워드로 정적메소드임을 선언할 수 있는데, 정적이 아닌 곳에서 이를 사용하려면 클래스 이름으로 접근해야하며 this로는 접근이 불가능하다.

class DepartmentData {
  static fiscalYear = 2020;
  constructor (n: string) {
    console.log(DepartmentData.fiscalYear);
  }
}

호출은 다음과 같이 할 수 있다.

const employee = DepartmentData.createEmployee('Max');
console.log(employee, DepartmentData.fiscalYear);

새 키워드 없이 직접 클래스에서 호출하며 클래스를 그룹화 매커니즘으로 사용할 수 있다.

✏️ 정적 메소드는 일반 프로토타입 메소드와 무엇이 다를까?

▪️ 서로 속해있는 프로토타입 체인이 다르다.
▪️ 정적 메소드는 클래스로 호출하지만 프로토타입 메소드는 인스턴스로 호출한다.
▪️ 정적 메소드는 인스턴스 프로퍼티를 참조할 수 없지만 프로토타입 메소드는 인스턴스 프로퍼티를 참조할 수 있다.

클래스 좀 깊게 2단계_추상 클래스

✏️ 클래스도 어려운데 추상 클래스는 무엇이며 왜 쓰는 것일까?

추상클래스 사용이유?
추상과 구체에 관해 살펴보면 구체 란 어떠한 형태가 갖추어져 있는 어떤 '것'이고 추상 이란 특정 부분들만을 뽑아내어 표현한 것입니다.
위의 코드에서 '직업' 을 나타내는 Job 클래스를 new(인스턴스화) 하였을 시 어떤 객체가 나오는가? 에 대한 답은 없습니다. 직업은 추상적이기 때문입니다.
그렇다면 인터페이스를 사용하지 왜 추상클래스를 사용하는가? 에 대한 답변은 객체지향 프로그래밍에서 상속관계를 나타내기 위함입니다. 인터페이스는 어떤 '행동'에 대한 명세일 뿐, 그 자체가 상속관계를 나타내주진 않습니다.
블로그 발췌

강의에서는 일부 상위 클래스를 기반으로 하는 모든 클래스가 일부 공통 메소드 또는 속성을 공유하도록 하기 위해서라고 설명한다.

✔️ 추상 클래스 정의하기

추상 클래스 정의 시에는 class 앞에 abstract 키워드를 붙인다. 또한 추상 메소드를 정의할 때도 abstract를 붙여준다.

추상 메소드는 정의만 있을 뿐 몸체(실제 내용)이 구현되어 있진 않다. 내용은 추상 클래스를 상속하는 클래스에서 해당 추상 메소드를 통해 구현해야 한다.

추상 클래스는 실제 사용이 가능한 메소드도 정의할 수 있지만, 말 그대로 추상이기 때문에 일반 클래스와 달리 인스턴스를 생성하지 않는다.

abstract class DepartmentData {
    static fiscalYear = 2020;
	protected employees: string[] = [];

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

	abstract describe(this: DepartmentData): void;	// 추상 메소드
}

추상 메소드는 중괄호 제거 후 추가 보유해야하는 타입을 반환한다.
→ abstract 키워드를 붙이고 내용은 없이 이름과 타입만 쓴다.

클래스 안에 추상 메소드가 하나라도 있으면 클래스 앞에도 abstract를 추가해야한다.

추상 클래스를 상속 받은 클래스는 추상 클래스 내에 정의된 추상 메서드를 반드시 구현해야한다.

클래스 쫌 더 깊게 3단계_상속

상속을 통해 클래스를 확장하려면 extends 키워드를 사용하여 상속받을 클래스를 정의한다.

상속을 통해 확장된 클래스를 서브클래스라 부르고, 서브클래스에게 상속된 클래스를 슈퍼클래스라 부른다.

extends 키워드는 슈퍼클래스와 서브클래스 간의 상속 관계를 설정한다.

class 클래스명 extends 상속받을 클래스명

super 키워드는 함수처럼 호출할 수 있고 this와 같이 식별자처럼 참조할 수 있는 특수 키워드이다.

다른 클래스로부터 상속받는 클래스에 고유 생성자를 추가할 때 상속하는 클래스에 super를 추가하고 이를 함수처럼 실행한다.

동작 방식은 두가지 이다.

  • super를 호출하면 슈퍼클래스의 (super)constructor를 호출한다.
  • super를 참조하면 슈퍼클래스의 메소드를 호출할 수 있다.

✏️ 슈퍼클래스의 constructor를 호출하기
슈퍼클래스의 constructor를 호출하면 constructor 내부에서 추가한 프로퍼티를 그대로 갖는 인스턴스를 생성할 수 있다.

이때 new 연산자와 함께 서브클래스를 호출하면서 전달한 인수는 모두 서브클래스에 암묵적으로 정의된 constructor의 super 호출을 통해 슈퍼클래스의 constructor에 전달된다.

⚠️ 주의 사항
▪️ 서브클래스에서 constructor를 생략하지 않은 경우 서브클래스의 constructor에서는 반드시 super를 호출해야 한다.
▪️ 서브클래스의 constructor에서 super를 호출하기 전에는 this를 참조할 수 없다.
▪️ super는 반드시 서브클래스의 constructor에서만 호출한다.

abstract class DepartmentData {
    static fiscalYear = 2020;
	protected employees: string[] = [];

    constructor(protected readonly id: string, public name: string){
      console.log(DepartmentData.fiscalYear);
    }

	static createEmployee(name: string){
        return {name: name};
    }

	abstract describe(this: DepartmentData): void;

    addEmployees(employees: string){
        this.employees.push(employees);
    }
    printEmplyeesInformation(){
        console.log(this.employees.length);
        console.log(this.employees);
    }
}

다음과 같은 슈퍼 클래스가 있다.

class ITDepartment extends DepartmentData { 
    admins: string[];
    constructor(id: string, admins: string[]){
        super(id, 'IT');
        this.admins = admins;
    }
    describe(){
        console.log('IT Department - ID: ' + this.id);
    }
}

ITDepartment는 서브클래스가 되며 슈퍼클래스인 DepartmentData의 상속을 받는다.

const it = new ITDepartment('d1', ['Max']);  // 괄호 안에 있는 생성자 호출 가능

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

it.describe();
it.printEmplyeesInformation();

console.log(it);

new 연산자를 통해 서브클래스를 호출하고 id와 admins의 값을 넣어준다.
그러면 슈퍼클래스의 console.log부터 하나씩 나타난다.

이 파트는 타입스크립트 가이드북을 많이 참고했다.
아직 내가 설명하기엔 부족한 부분으로 느껴진다.

추상클래스, 인터페이스?

추상클래스와 인터페이스는 구분이 애매모호하다. 추상 클래스 부분에 이를 짚어주는 블로그 글을 발췌했지만 더 자세히 알아보려한다.

(인터페이스는 흔히 객체의 구조, 형태를 설명한다. 클래스와 달리 사용자 정의 타입으로만 사용하게 된다.)

✏️ 공통점은 무엇일까?

  • 추상화
    추상적인 메소드를 정의한다.

  • 자식클래스에서의 강제 구현
    - 인터페이스 : 인터페이스에 있는 메소드는 자식클래스에서 반드시 구현해야한다.
    - 추상클래스 : abstract로 시작하는 메소드는 자식클래스에서 반드시 구현해야한다.

⭐차이점⭐

  • 인터페이스는 모든 메소드가 추상 메소드이다.
  • 추상 클래스는 추상 메소드만 포함할 수 있는 것이 아니라 실제 구현이 있는 메소드도 포함할 수 있다.
  • 추상 클래스는 상속을 통해 자식 클래스에서 구현화하여 완성하도록 유도한다.
  • 인터페이스도 다른 클래스를 작성하는데 도움을 주지만 클래스와 다르게 인터페이스 간에 extends를 사용하여 다중 상속을 가능케 한다.

❓ 그렇다면 Type Alias와 interface의 차이점은?

  • 인터페이스는 객체의 구조를 설명하기 위해서만 사용하기 때문에 클래스는 인터페이스 구조에 맞춰서 구현해야한다.
  • 인터페이스는 구체적인 구현이 아닌 서로 다른 클래스 간의 기능을 공유하기 위해 사용된다는 점에서 타입을 사용자가 "정의"하는 Type Alias와는 차이가 있다.

다음 게시글은 싱글턴 패턴(OOP)과 게터/세터, JS this에 대한 내용을 다루어야겠다.
클래스만 다루다가 끝날 줄이야,,,

profile
일이 재밌게 진행 되겠는걸?

0개의 댓글