이번 시간에는 타입스크립트로 클래스를 정의하고 사용하는 방법을 알아보기전에 간략히 객체지향 프로그래밍에 대한 개념을 안내합니다. 

1)객체지향 프로그래밍 주요 개념잡기 

클래스객체(현실에 존재하는 실체)를 일반화한 개념으로
현실세계의 실체(객체)를 개별적으로 프로그래밍이 힘든점을 감안해 비슷한 공통점을 가지고있는
실체들의 일반적인 특징들을 속성과 기능으로 일반화해서 정의하는 방법을 제공합니다.

실체들을 일반화한 개념이 클래스이고 클래스를 이용해 실체(객체)를 만들면 해당 실체가 객체가 됩니다.
이러한 클래스를 이용해 객체를 만드는과정을  인스턴스화한다고 말하며 보통 new 클래스() 방법을 통해
클래스를 통해 객체를 생성
합니다.

let myCar = new Car();
myCar.brand ="K5";
myCar.color ="black";
myCar.run(100km);

Car  클래스에는 자동차가 가지는 대부분의 공통적 속성과 기능을 미리 구현해둡니다.
클래스를 통해 생성된 객체 myCar에는 해당 클래스에서 정의한 공통속성과 기능들이 모두 포함되어 있기 때문에 클래스를 통해 만들어진 객체를 기반으로 프로그래밍을 하면 보다 효율적/생산적인 코딩이 가능해집니다. 

이런식으로 클래스를 정의하고 정의된 클래스를 통해 객체를 만들어 현실 세계를 프로그래밍 하는 방법을 객체지향 프로그래밍 이라고 흔히 말합니다.


예를 들면
현실에 존재하는 실체인 K5,Genisys,BMW등의  개별 자동차들은
자동차란 공통적인 특징(드라이브,바퀴,차종,색상...)을 가지고 있습니다.

상속이란 부모 요소의 기능과 속성을 물려받아 자식요소에서 기능과 속성을 확장하거나 부모의 기능과 속성을 반드시 구현해야하는 제약을 발생시킬수 있습니다.
기능과 속성을 확장하고 싶을때는 extends 키워드를 제약하고 싶을때는 implements 키워드를 사용합니다.

클래스는 인터페이스가 extends를 통해 다른 인터페이스를 상속받아 기능과 속성을 확장하는것처럼 동일하게
다른 클래스를 extends를 통해 해당 클래스의 기능과 속성을 확장할수 있습니다.

extends 키워드는 인터페이스가 특정인터페이스를, 클래스가 특정클래스를 상속받아 기능과 속성을 확장하고 싶을때 사용합니다.
extends 키워드를 사용해 클래스의 다중상속은 불가하지만 인터페이스이 다중상속은 가능합니다.


특정 클래스에서 반드시 구현해야하는 기능과 속성이 있다면
implements 키워드를 통해 특정 인터페이스를 상속받고 해당 클래스에서는 반드시 해당 인터페이스에서 정의한
기능과 속성를 실행(구현)해야하는 제약 사항이 제공됩니다.
implements 키워드는 클래스가 인터페이스를 구현할 때 사용됩니다
implements를 통해 인터페이스를 다중 상속받을수 있습니다.


extends는 새로운 클래스의 '상속' 을 위해 사용되고, implements는 새로운 클래스의 모양을 정의할 때 사용됩니다
각각의 키워드의 목적과 차이점을 숙지하고 코딩하시기를 바랍니다. 

D:\TypeScript\3-TS-Core 폴더내에 class.ts파일을 생성 니다.

2) 클래스 기반 타입 정의법 코딩하고 실행하기 
-하기 코드와 같이 코딩을 진행합니다.
-자세한 내용은 주석을 참고바랍니다.
-class.ts

//Case1) 클래스가 다른 클래스를 상속받아 속성/기능을 확장 -extends 사용

class Animal {
    name:string;

    move() {
      console.log(this.name +" is Moving along!");
    }
   
    //생성자함수: 클래스를 통해 객체가 생성될때(인스턴스화=new) 최초 한번만 실행되는 함수
    constructor(name: string ='') {
        //this는 생성자함수가 생성할 인스턴스(객체)를 가리킴
        this.name = name;
    }
  }
 
  class Dog extends Animal {
    bark() {
      console.log(this.name +" is Woof! Woof!");
    }
  }
 
  //new 클래스를 통해 해당클래스를 인스턴화해 새로운 객체를 생성함
  //인스턴스화란 클래스를 이용해 새로운 객체를 만드는 과정을 말함
  let myDog = new Dog('누렁이');
  myDog.move(); // "Moving along!"
  myDog.bark(); // "Woof! Woof!"

 


//Case2) 클래스가 인터페이스를 상속받아 구현하면서 interface의 속성과 기능에 제약을 받아야 할때 -implements
  interface Movable {
    speed: number;
    move(): void;
  }
 
  //인터페이스를 상속받은 클래스는 인터페이스에 정의된 기능과 속성을 반드시 구현해야함
  class Car implements Movable {
    speed: number;

    constructor(speed: number) {
        this.speed = speed;
    }

    move() {
      console.log(`The car is moving at ${this.speed} km/h`);
    }
  }
 
  let myCar = new Car(50);
  myCar.move(); // "The car is moving."


//C) 접근 제어자(access modifier) 사용 및 이해하기

//사용자 유형 열거형 타입
enum UserType {
  Admin = "admin",
  User = "user",
}

//관리자 역할 열거형 타입
enum AdminRole {
  SystemAdmin = "SA",
  GeneralAdmin = "GA",
}

class User {
  public name: string;
  private password: string;
  protected email: string;
  protected userType: UserType;

  constructor(name: string, password: string, email: string) {
    this.name = name;
    this.password = password;
    this.email = email;
    this.userType = UserType.User;
  }

  public greet() {
    console.log(`Hello, ${this.name}!`);
  }

  private config(){
    this.sendEmail('Personalization info is Configed');
    console.log(`Personalization info is Configed. sended email to ${this.email}`);
  }

  changePassword(newPassword: string) {
    this.password = newPassword;
    console.log(`password is changed to ${this.password}.`);
  }

  //시스템에서 사용자에게 메일 발송하기
  protected sendEmail(message:string) {
    console.log(`Email sent to ${this.email} by admin`);
  }


}

let user = new User("John", "123456", "john@example.com");
user.greet();
//user.password =  "1234"; //password 속성 접근불가 에러발생
//user.email = "test"; //email 속성 접근불가 에러발생 자식클래스에서만 접근가능

user.changePassword("1234"); //password 변경
//user.sendEmail("test"); //protected 속성은 상속받은 클래스 안에서만 접근가능//에러발생
//user.config(); //private 메소드는 해당 클래스 밖에서는 접근불가

class AdminUser extends User {

  private adminRole: AdminRole;

  constructor(name: string, password: string, email: string,adminRole?:AdminRole) {
      //super()는 부모클래스의 생성자함수를 호출함
      super(name,password,email);

      this.userType = UserType.Admin;
      this.adminRole = adminRole == undefined ? AdminRole.GeneralAdmin : adminRole;

      console.log(this.name); // public 속성은 상속받은 클래스에서 접근 가능
      //console.log(this.password); // private 속성은 상속받은 클래스에서 접근 불가
  }

  changeUserType(userType:UserType) {
    console.log(`User Type will chagne from ${this.userType} to ${userType}`);
    this.userType = userType;
    console.log(`now User Type is ${this.userType}`);
  }


  changeRoleType(roleType:AdminRole) {
      console.log(`Role Type will chagne from ${this.adminRole} to ${roleType}`);
      this.adminRole = roleType;

      //부모 클래스의 protected 메소드 호출실행
      this.sendEmail(`Role Type is changed for ${this.email} account`); //protected 속성은 상속받은 클래스에서 접근 가능
  }
}

let admin1 = new AdminUser("Admin", "123456", "admin@example.com");
//admin1.userType = UserType.Admin;  userType 속성 접근불가 에러발생
admin1.changeUserType(UserType.Admin); //changeUserType() 메소드를 통해 부모 클래스의 속성값을 변경
admin1.changeRoleType(AdminRole.SystemAdmin); //관리자 역할 을 바꾸려면

//admin1.sendEmail("test"); //protected 속성은 상속받은 클래스 안에서만 접근가능//에러발생

// 선택적 매개변수를 이용한 클래스 생성자 함수 오버로딩처럼 구현하기
let admin2= new AdminUser("Admin", "123456", "admin@example.com",AdminRole.SystemAdmin);



  //타입스크립트 컴파일과 JS모듈 파일 실행하기
//tsc --strictNullChecks class.ts
//node class.js


-코딩을 모두 진행 저장하고 아래 명령어를 통해 터미널창에서 ts파일을 컴파일하고 컴파일 결과.js파일을 실행합니다. 

D:\TypeScript\3-TS-Core
tsc --strictNullChecks class.ts
node class.js


3)코딩 핵심 내용 설명
- 클래스를 정의하고 생성자 함수를 통해 속성값을 초기화하는 방법을 이해합니다.
- 클래스내 생성자 함수는 클래스를 new연산자를 통해 객체를 생성시키는 시점에 호출되어 객체의 정해진 속성값을 초기화하거나  특정 기능을 실행시킵니다.
- 객체를 생성하는 시점에 외부에서 해당 클래스내로 생성자함수를 통해 초기데이터를 전달할수 있습니다.
- 클래스는 new 클래스() 형태로 객체를 만들어내며 이러한 과정을 클래스의 인스턴스화라고 표현합니다.
-클래스 인스턴스화는 미리 정의된 클래스를 기반으로 객체를 생성하는
기본 개념이며 클래스가 인스턴스화되면 해당 클래스의 복사본(=객체)이 메모리에 생성되어 해당 속성과 메서드에 액세스할 수 있으며
클래스의 복사본인 메모리상의 객체를 통해 프로그래밍(객체지향 프로그래밍)이 가능해집니다.
-열거형 타입(enum)은 상수형태의 데이터 값을 미리 정의해두고 사용하는 방법을 제공합니다. 


마지막 주제인 Generic 학습링크로 이동합니다.