[TypeScript] 타입스크립트로 객체지향 프로그래밍하기: Class

summereuna🐥·2023년 8월 30일
0

TypeScript

목록 보기
8/13

타입스크립트로 객체지향 프로그래밍하기


TypeScript는 객체지향 코드를 더 안전하고 좋게 만들도록 도와주는 기능을 제공한다.


클래스 (Class)



보다시피 TS와 JS는 조금 다르다.

JS에서 class 만들기

JS에서 class를 만들 때는 constructor() 함수를 사용하여 this.firstName = firstName; 이런식으로 보내줘야 한다.

class Player {
  constructor(firstName, lastName, nickname){
    this.firstName = firstName;
    this.lastName = lastName;
    this.nickname = nickname;
  }
}

const rm = new Player("Kim", "Namjoon", "DuDu");

TS에서 class 만들기

하지만 TS에서는 constructor()파라미터만 보내주면 된다.
필드가 어떤 보호 등급인지(접근 제어자: private, protected, public), 이름, 타입을 작성하면 된다.\
그러면 TS가 알아서 해준다.

  • 접근 제어자는 JS에서는 보이지 않는다.
class Player {
  constructor(
  //파라미터 보내기
  //JS에서는 private, public 같은 기능 없지만 TS에서는 설정가능
  private firstName: string,
  private lastName: string,
  public nickname: string,
  ) {}
}

const rm = new Player("Kim", "Namjoon", "DuDu");

rm.firstName; //❌ private이라서 이렇게는 접근 불가 => 오류 띄워줌
rm.lastName; //❌ private이라서 이렇게는 접근 불가 => 오류 띄워줌
rm.nickname; //✅ public이라서 접근 가능

추상 클래스 (Abstract Class): 클래스 상속


TS와 객체지향 프로그램이 가지고 있는 좋은 것은 추상클래스(Abstract Class)이다.

  • 추상 클래스(Abstract Class)오직 다른 클래스가 상속(inheritance) 받을 수만 있는 클래스이다.
//추상클래스(Abstract Class)는 다른 클래스가 상속 받을 수 있는 클래스
abstract class User {
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string,
     ) {}
}

//Player가 User를 상속한다고 알리기
class Player extends User {
}

const rm = new Player("Kim", "Namjoon", "DuDu")

rm.nickname; //✅ public이라서 접근 가능
  • 하지만 추상 클래스는 직접 새로운 인스턴스를 만들 수는 없다.
    그래서 new User 이렇게 사용할 수는 없다.

추상 클래스 안에서 구현된 메서드

abstract class User {
    constructor(
        private firstName: string,
        private lastName: string,
        public nickname: string,
     ) {}
  
     //클래스 안에서 구현된 메서드(abstract method) 만들기
     //firstName, lastName 합친 string 리턴해주는 메서드
     getFullName(){
        return `${this.firstName} ${this.lastName}`;
     }
}

//클래스 생성
//Player 가 User를 상속한다고 알리기
class Player extends User {
}

//인스턴스 생성
const rm = new Player("Kim", "Namjoon", "DuDu")

rm.nickname; //✅ public이라서 접근 가능

rm.getFullName(); //✅ User에서 상속받았으니까 Player에서도 메서드 호출 가능
  • 만약 getFullName 메서드를 보호하고 싶다면 public대신 private을 사용하면 된다.
    private getFullName(){...} 그러면 이 메서드는 호출 할 수 없다.

추상 메서드 (Abstract Method)


(참고)
메서드(method)는 클래스(class)안에 존재하는 함수이다.

일반적인 메서드는 메서드 안에 코드를 구현(implementation)한다.

getFullName(){
  //이 부분이 메서드의 구현(implementation) 부분이다.
  //👉 이런 구현 부분을 넣지 않으면 된다는 뜻임
  return `${this.firstName} ${this.lastName}`; 
  //
}

추상 메서드는 메서드 안에 코드를 구현하지 않는다. 대신 콜 시그니처만 적어 둔다.

  • 추상 메서드는 메서드를 구현(implementation)하지 않은, 즉 코드가 없는 메서드이다.

  • 추상 클래스 안에서 추상 메서드를 만들 수 있는데, 메서드를 구현하는 대신 메서드의 call signature만 적어 두면 된다.

abstract class User {
    constructor(
        private firstName: string,
        private lastName: string,
        private nickname: string,
         //❗️ 닉네임을 private으로 바꿔보자
     ) {}
  
     //말은 안되지만 getNickname 메서드를 만들어 보자.
     //🔥 추상 메서드(abstract method)
     //❗️ 메서드 안에 코드가 없음
     //❗️ 즉, 구현하는 코드가 없고 콜 시그니처만 적어야 함
     abstract getNickname():void

     //📝 일반적인 메서드
     //: 클래스 안에서 메서드 만들 때 메서드 안에 구현된 코드 있음
     //firstName, lastName 합친 string 리턴해주는 메서드
     getFullName(){
      //메서드의 구현 부분
      return `${this.firstName} ${this.lastName}`;
      //
     }
}

//클래스 생성
//Player 가 User를 상속한다고 알리기
class Player extends User {
}

//인스턴스 생성
const rm = new Player("Kim", "Namjoon", "DuDu") //🔥🔥🔥 여기서 오류뜸!

rm.nickname; //❌ private이라서 접근 불가

rm.getFullName(); //✅ User에서 상속받았으니까 Player에서 메서드 사용가능
  • 추상 클래스가 아닌 Player가 User클래스로 부터 추상 멤버인 getNickname을 상속받았지만, 구현하지 않았다고 오류가 뜬다.
    즉, Player가 getNickName을 구현해야 한다고 오류를 띄워 알려준다.

  • 따라서 추상 메서드는 추상 클래스를 상속받는 모든 것들이 구현 해야 하는 메서드를 의미한다.

추상 메서드가 있는 경우, 추상 클래스를 상속받는 하위 클래스에서 추상 메소드를 구현해야 한다.

//🔥 User 추상 클래스의 nickname 프로퍼티가 private인 경우

class Player extends User {
   //🔥 추상 메서드를 구현하려면 하위 클래스 안에서 구현해주면 된다.
   getNickname(){
     //그런데 
     //❌ User 추상 클래스의 nickname 프로퍼티가 private이기 때문에 이렇게 사용할 수가 없다.
      console.log(this.nickname);
   }
}

//인스턴스 생성
const rm = new Player("Kim", "Namjoon", "DuDu")

rm.nickname; //❌ User 인스턴스 바깥에서는 프로퍼티에 접근 불가

📝 private, public, protected: 프로퍼티 접근 가능한 위치

구분선언한 클래스 안
(ex. 추상 클래스 안)
상속받은 클래스 안
(자식 클래스 안)
인스턴스 밖
private
protected
public
  • private으로 작성된 프로퍼티들은 인스턴스 밖에서 접근할 수 없고, 다른 자식 클래스에서도 접근할 수 없다.
  • 오직 그 프로퍼티를 선언한 클래스인 User 클래스의 인스턴스나 메서드에서만 접근할 수 있다.
    • 그런데 이 User 클래스는 추상 클래스여서 인스턴스화 할 수 없다.
    • 따라서 필드가 외부로부터는 보호(인스턴스에서 사용x)되지만 다른 자식 클래스에서는 사용될 수 있게 하기 원한다면, private대신 protected를 사용하면 된다.
//추상클래스(Abstract Class)는 오직 다른 클래스가 상속 받을 수만 있는 클래스
//직접 새로운 인스턴스를 만들 수는 없다. 따라서 new User 이렇게 못 만듦
abstract class User {
    constructor(
        //✅ 필드 보호를 원하지만 하위클래스에서는 프로퍼티에 접근 가능하게 하려면
        //private 대신 protected 적어주기
        protected firstName: string,
        protected lastName: string,
        protected nickname: string, 
     ) {}
  
     //추상 메서드(abstract method) 만들기
     //구현 하면 안되고 콜 시그니처만 적어야 함
     abstract getNickname():void

     //클래스 안에서 메서드 만들기
     //firstName, lastName 합친 string 리턴해주는 메서드
     getFullName(){
      //메서드의 구현 부분
      return `${this.firstName} ${this.lastName}`;
      //
     }
}

//클래스 생성
//Player 가 User를 상속한다고 알리기
class Player extends User {

   //🔥 추상 메서드 구현하기
   getNickname(){
      //✅ User를 상속하는 Player 하위 클래스에서는 protected 프로퍼티에 접근 가능
      console.log(this.nickname);
   }
}

//인스턴스 생성
const rm = new Player("Kim", "Namjoon", "DuDu")

rm.nickname; //❌ User 인스턴스 바깥에서는 protected 프로퍼티에 접근 불가

rm.getFullName(); //✅ User에서 상속받았으니까 Player에서 추상 메서드 사용가능

연습: 해시맵 만들기


해싱 알고리즘을 사용하는 해시맵으로 단어 사전 만들어 보기

//Words 타입은 오브젝트인데, string만을 프로퍼티로 가짐
type Words = {
   [key:string]: string
   //이 형식은 제한된 양의 프로퍼티 혹은 키를 가지는 타입을 정의하는 방법임
}

//예를 들면 이렇게
// let dict :Words = {
//    "potato": "food",
//    "strawberry": "food"
// }


//사전 클래스
class Dictionary {
   //워드 오브젝트를 프라이빗으로 만들기
   //그런데 컨스트럭터가 words를 지정하기를 원하지 않으므로 
   //컨스트럭터로부터 바로 초기화되지 않는 프로퍼티로 만들자
   private words:Words
   //그러고 나서 수동으로 초기화 하기
   constructor(){
      this.words = {}
   }
   
  //✅메서드 만들기
  //사전에 새 단어를 추가하기 / 단어 찾기 / 단어 삭제 하기 

   //✅단어 추가하는 메서드
   //🔥 Word 클래스를 타입처럼 사용할 수 있음
   //word 파라미터가 Word 클래스의 인스턴스 이길 원한다면 이렇게 사용할 수 있다.
   add(word:Word){
      //주어진 단어가 아직 사전에 존재하지 않으면
      if(this.words[word.term] === undefined){
         //사전에 그 단어에 대한 정의에 추가하기
         this.words[word.term] = word.definition; 
      }
   }

   //✅term 이용해 deffinition 찾는 메서드
   deffintion(term:string){
      return this.words[term]
   }

   //✅단어 삭제하는 메서드
   remove(term: string){
      //단어를 찾았을 때 단어가 있으면
      if(this.words[term] !== undefined){
         //그 단어 삭제
         delete this.words[term]
      }
   }

   //✅단어 업데이트하는 메서드
   update(word: Word){
      //단어 찾았을 때 그 단어가 있으면
      if(this.words[word.term] !== undefined){
         //단어 정의 업데이트하기 위해 새로 넣어주기
         this.words[word.term] = word.definition;
      }
   }
}



//단어 클래
class Word {
   constructor(
      public term: string, //용어
      public definition: string, //정의
   ){}
}

//단어 인스턴스 생성
const kimchi = new Word("kimchi", "a Korean dish of spicy pickled cabbage.")
const coffee = new Word("coffee", "커피가 커피지")
const greentea = new Word("greentea", "녹차 말차 루룰루루")

//사전 인스턴스 생성
const dict = new Dictionary();

//✅사전에 단어 추가하기
dict.add(kimchi);
dict.add(coffee);
dict.add(greentea);

//사전에 있는 단어
console.log(dict)

//✅단어의 정의 찾아보기
dict.deffintion("kimchi");
dict.deffintion("coffee");
dict.deffintion("greentea");

//업데이할 인스턴스 생성
const newCoffee = new Word("coffe", "콩 볶아 만든 카페인 차차차")

//✅커피 정의 업데이트하기
dict.update(newCoffee)

//✅사전에 단어 삭제하기
dict.remove("greentea")

아 근데 업데이트가 안되네 ;;;;

profile
Always have hope🍀 & constant passion🔥

0개의 댓글