본문 바로가기
내일배움 강의/강의-TypeScript 문법 종합반

5주차

by GREEN나무 2025. 1. 18.
728x90

추가 학습 자료
    - 공식 매뉴얼
        - https://www.typescriptlang.org/docs/
    - 공식 튜토리얼
        - https://www.typescriptlang.org/docs/handbook/intro.html
    - 온라인 책
        - https://radlohead.gitbook.io/typescript-deep-dive/getting-started
베이직반수업 https://teamsparta.notion.site/TypeScript-2ffe288e7dcf4b728e508b08274a3592

01. 클래스
객체 지향 프로그래밍(OOP)의 핵심 구성 요소 중 하나
객체를 만들기 위한 틀(template)
 속성(attribute)과 메서드(method)
  속성은 객체의 성질을 결정하는 것
  메서드는 객체의 성질을 변화시키거나 객체에서 제공하는 기능들을 사용하는 창구

객체는 클래스를 기반으로 생성되며 클래스의 인스턴스(instance)

- 생성자(constructor)
    - 생성자는 클래스의 인스턴스를 생성하고 초기화
    - 클래스 내에 오직 하나만 존재.
    - 인스턴스를 생성할 때 자동으로 호출.    
    - 클래스 내에서 `constructor`라는 이름으로 정의됨.
    - 보통, 생성자로 객체 속성을 초기화 하는것 뿐 아니라 객체가 생성이 될 떄 꼭 되어야 하는 초기화 로직을 집어넣기도 함
        - ex - 클래스 DBConnector가 생성이 될 때 생성자에서 DB를 미리 연결한다
class Person { ... }
const person = new Person('Spartan', 30);


- 클래스 접근 제한자
  - 클래스에서 속성과 메서드에 접근 제한자를 사용해 접근을 제한.
- TypeScript에서 제공하는 접근 제한자
    - `public`
        - 클래스 외부에서도 접근이 가능한 접근 제한자.
        - 접근 제한자 선언이없으면 기본적으로 접근 제한자는 public
        - 민감하지 않은 객체 정보, 누구나 해당 클래스의 특정 기능을 사용해야 할 때 사용.
    - `private`
        - 클래스 내부에서만 접근이 가능한 접근 제한자.
        - 보통은 외부에서 직접적으로 클래스의 속성을 변경하는 것을 제한함. 클래스의 속성은 대부분 private
        - 클래스의 속성을 보거나 편집하고 싶다면 별도의 getter/setter 메서드를 사용.
    - `protected`
        - 클래스 내부와 해당 클래스를 상속받은 자식 클래스에서만 접근이 가능한 접근 제한자.

☑️ ex - 변수나 함수를 선언할 때 앞에 let처럼 붙인다.
class Person {
  private name: string;
  private age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  public sayHello() {
    console.log(`안녕하세요! 제 이름은 ${this.name}이고, 나이는 ${this.age}살입니다.`);
  }
}



 02. 상속


    https://intellipaat.com/blog/tutorial/python-tutorial/python-classes-and-objects/?US*
    - 상속을 통해 기존 클래스(부모클래스)의 속성과 메서드를 물려받아 새로운 클래스를 정의할 수 있다.
    - 자식 클래스에서 생성자를 정의하기위해 부모 클래스의 생성자를 호출할 때 사용(상속구현 : extends)
    
    ☑️ ex
        
    class Animal { // 부모클래스 Animal
      name: string;
    
      constructor(name: string) {
        this.name = name;
      }
    
      makeSound() {
        console.log('동물 소리~');
      }
    }
    
    class Dog extends Animal { // 자식클래스 Dog
      age: number;
    
      constructor(name: string) {
        super(name);
        this.age = 5;
      }
    
      makeSound() {
        console.log('멍멍!'); // 오버라이딩 : 부모의 클래스의 함수의 동작을 새롭게 정의
      }
    
      eat() { // Dog 클래스만의 새로운 함수 정의
        console.log('강아지가 사료를 먹습니다.');
      }
    }
    
    class Cat extends Animal { // 자식클래스 Cat. Animal과 동일함
    }
    
    const dog = new Dog('누렁이');
    dog.makeSound(); // 출력: 멍멍!
    
    const cat = new Cat('야옹이');
    cat.makeSound(); // 출력: 동물 소리~
    
    
- 2 서브타입, 슈퍼타입


    https://www.zhenghao.io/posts/type-hierarchy-tree*
    
    - 서브타입의 정의       
        > 두 개의 타입 A와 B가 있고 B가 A의 서브타입이면 
          A가 필요한 곳에서 B를 안전하게 사용할 수 있다. 

    - 슈퍼타입의 정의        
        > 두 개의 타입 A와 B가 있고 B가 A의 슈퍼타입이면 
          B가 필요한 곳에서 A를 안전하게 사용할 수 있다.
      
    - any는 모든 것의 슈퍼타입이다.
    - Animal은 Dog, Cat의 슈퍼타입.
    - Dog, Cat은 Animal의 서브타입.
    

 


- 3 upcasting, downcasting
    - upcasting(서브 -> 슈퍼)과 downcasting(슈퍼 -> 서브)은 슈퍼타입, 서브타입으로 변환할 수 있는 타입 변환 기술
    
  - 3-1. upcasting  서브타입 → 슈퍼타입
    - 타입 변환은 암시적으로 이루어져 별도의 타입 변환 구문이 필요가 없다.
        TypeScript가 자동으로 처리함
    - upcasting이 필요한 이유 : 서브타입 객체를 슈퍼타입 객체로 다루면 유연하게 활용할 수 있다.
        - 예를 들어, Dog, Cat, Lion 그리고 기타 등등 다양한 동물을 인자로 받을 수 있는 함수를 만들고 싶다면?
            - 올바른 선택: 아! Animal 타입의 객체를 받으면 모두 다 받을 수 있겠구나!
            - 잘못된 선택: 아! union으로 새로운 타입을 만들어서 해당 타입의 객체를 받게해야겠구나!

    let dog: Dog = new Dog('또순이');
    let animal: Animal = dog; // upcasting 발동 : 슈퍼타입의 변수에 서브타입의 객체를 넣음
    animal.eat(); // 에러. 슈퍼타입(Animal)으로 변환이 되어 eat 메서드를 호출할 수 없다. 
        
    
  - 3-2. downcasting  슈퍼타입 → 서브타입
    - 이 경우에는 `as` 키워드로 명시적으로 타입 변환을 해줘야 한다.
    - 필요한 경우에 Dog와 같은 서브타입의 메서드를 사용해야 될 때 변신을 해야 될 수 있습니다!

    
    let animal: Animal;
    animal = new Dog('또순이');
    
    let realDog: Dog = animal as Dog; // downcasting 
    realDog.eat(); // 서브타입(Dog)로 변환이 되었기 때문에 eat 메서드를 호출할 수 있죠!
    
    
    
03. 추상 클래스
    - 추상 클래스는 클래스와는 다르게 인스턴스화를 할 수 없는 클래스    
    - 추상 클래스의 목적은 상속을 통해 자식 클래스에서 메서드를 제각각 구현하도록 강제를 하는 용도
    - 추상 클래스도 최소한의 기본 메서드는 정의를 할 수 있지만, 골자는 핵심 기능의 구현은 전부 자식 클래스에게 위임을 하는 것
    
    -사용 방법    
      - 추상 클래스 및 추상 함수는 `abstract` 키워드를 사용하여 정의.
      - 추상 클래스는 1개 이상의 추상 함수가 있는 것이 일반적이이다.     
      - 이 추상 클래스를 상속 받은 자식 클래스들은 반드시 추상함수(getArea 함수)를 구현해야 한다.
    
    ☑️ ex      
    abstract class Shape { // 추상 클래스 정의
      abstract getArea(): number; // 추상 함수 정의
    
      printArea() {
        console.log(`도형 넓이: ${this.getArea()}`);
      }
    }
    
    class Circle extends Shape {
      radius: number;
    
      constructor(radius: number) {
        super();
        this.radius = radius;
      }
    
      getArea(): number { // 원의 넓이를 구하는 공식은 파이 X 반지름 X 반지름
        return Math.PI * this.radius * this.radius;
      }
    }
    
    class Rectangle extends Shape {
      width: number;
      height: number;
    
      constructor(width: number, height: number) {
        super();
        this.width = width;
        this.height = height;
      }
    
      getArea(): number { // 사각형의 넓이를 구하는 공식은 가로 X 세로
        return this.width * this.height;
      }
    }
    
    const circle = new Circle(5);
    circle.printArea();
    
    const rectangle = new Rectangle(4, 6);
    rectangle.printArea();
    

 04. 인터페이스

- 1 인터페이스
    - 인터페이스는 TypeScript에서 객체의 타입을 정의하는데 사용.
    - 객체가 가져야 하는 속성과 메서드를 정의함함.
    - 인터페이스를 구현한 객체는 인터페이스를 반드시 준수해야함. 규약과 같아서 어길 수가 없음.
    - 인터페이스를 사용하면 코드의 안정성을 높이고 유지 보수성을 향상.

- 2 추상 클래스와 인터페이스의의 차이
    - 기본 구현을 제공하고 상속을 통해 확장하는데 초점을 맞추고 싶다면 → `추상 클래스`
    - 객체가 완벽하게 특정 구조를 준수하도록 강제하고 싶다면 → `인터페이스`

     구현부 제공 여부    
    - 추상 클래스
        - 클래스의 기본 구현을 제공.
    - 인터페이스
        - 객체의 구조만을 정의하고 기본 구현을 제공하지 않는다다.
    
     상속 메커니즘    
    - 추상 클래스
        - 단일 상속만 지원.
    - 인터페이스
        - 다중 상속을 지원. 하나의 클래스는 여러 인터페이스를 구현가능능
    
     구현 메커니즘    
    - 추상 클래스
        - 추상 클래스를 상속받은 자식 클래스는 반드시 추상 함수를 구현해야 함.
    - 인터페이스
        - 인터페이스를 구현하는 클래스는 인터페이스에 정의된 '모든 메서드'를 전부 구현해야 함.
    
05. 객체 지향 설계 원칙 - S.O.L.I.D
  S.O.L.I.D로 설계를 하면 프로그램이 유연하고, 확장 가능하며, 이해하기 쉬운 구조를 가지도록 도와준다 

- 1. S(SRP. 단일 책임 원칙) ⭐
    - SRP 원칙    
     - 클래스는 하나의 책임만 가져야 한다

     ex. 유저 서비스라는 클래스가 있다.
       - 이 유저 서비스에서는 유저 관련된 액션만 해야되고 다른 액션을 해서는 안된다다.
    
    ☑️ 잘못된 사례    
    
    class UserService {
      constructor(private db: Database) {}
    
      getUser(id: number): User {
        // 사용자 조회 로직
        return this.db.findUser(id);
      }
    
      saveUser(user: User): void {
        // 사용자 저장 로직
        this.db.saveUser(user);
      }
    
      sendWelcomeEmail(user: User): void {
        // 갑분 이메일 전송 로직이 여기 왜?
        const emailService = new EmailService();
        emailService.sendWelcomeEmail(user);
      }
    }
    
    
    ☑️ 올바른 사례 - 분리되어야 한다    
    
    class UserService {
      constructor(private db: Database) {}
    
      getUser(id: number): User {
        // 사용자 조회 로직
        return this.db.findUser(id);
      }
    
      saveUser(user: User): void {
        // 사용자 저장 로직
        this.db.saveUser(user);
      }
    }
    
    class EmailService {
      // 이메일 관련된 기능은 이메일 서비스에서 총괄하는게 맞습니다.
      // 다른 서비스에서 이메일 관련된 기능을 쓴다는 것은 영역을 침범하는 것이에요!
      sendWelcomeEmail(user: User): void {
        // 이메일 전송 로직
        console.log(`Sending welcome email to ${user.email}`);
      }
    }
    
    
- 2. O(OCP. 개방 폐쇄 원칙) 
    - 클래스는 확장에 대해서는 열려 있어야 하고 수정에 대해서는 닫혀 있어야 한다는 원칙.
    - 클래스의 기존 코드를 변경하지 않고도 기능을 확장할 수 있어야 한다.
       - `인터페이스`나 `상속`을 통해서 이를 해결.
        - 부모 클래스의 기존 코드 변경을 하지 않고 기능을 확장할 수 있다.

- 3. L(LSP. 리스코프 치환 원칙)    
    - 서브타입은 기반이 되는 슈퍼타입을 대체할 수 있어야 한다는 원칙.
      - 자식 클래스는 부모 클래스의 기능을 수정하지 않고도 부모 클래스와 호환되어야 한다.
        - 논리적으로 엄격하게 관계가 정립이 되어야 한다
    
    ☑️ 잘못된 사례
    class Bird {
      fly(): void {
        console.log("펄럭펄럭~");
      }
    }
    
    class Penguin extends Bird {
      // 정확하지 않은 사례는 서브타입 클래스에서 따로 선언
    }
    
    
    ☑️ 올바른 사례         
    abstract class Bird {
      abstract move(): void;
    }
    
    class FlyingBird extends Bird {
      move() {
        console.log("펄럭펄럭~");
      }
    }
    
    class NonFlyingBird extends Bird {
       move() {
        console.log("뚜벅뚜벅!");
      }
    }
    
    class Penguin extends NonFlyingBird {} 
    
    
- 4. I(ISP. 인터페이스 분리 원칙)
    - 클래스는 자신이 사용하지 않는 인터페이스의 영향을 받지 않아야 한다.
      - 해당 클래스에게 무의미한 메소드의 구현을 막자
    - 따라서, 인터페이스를 너무 크게 정의하기보단 필요한 만큼만 정의하고 클래스는 입맛에 맞게 필요한 인터페이스들을 구현합니다

- 5. D(DIP. 의존성 역전 원칙)    
    - DIP는 Java의 Spring 프레임워크나 Node.js의 Nest.js 프레임워크와 같이 웹 서버 프레임워크 내에서 많이 나오는 원칙.
    - 하위 수준 모듈(구현 클래스)보다 상위 수준 모듈(인터페이스)에 의존을 해야한다.

    - ex. 클래스 '데이터베이스'의 원천은 로컬 스토리지가 될 수도 있고 클라우드 스토리지가 될 수도 있다다.
      - 이 때, 데이터베이스의 원천을 로컬 스토리지 타입 혹은 클라우드 스토리지 타입으로 한정하는 것이 아니라 그보다 상위 수준인 스토리지 타입으로 한정한다.
    
    ☑️ ex 
    interface MyStorage {
      save(data: string): void;
    }
    
    class MyLocalStorage implements MyStorage {
      save(data: string): void {
        console.log(`로컬에 저장: ${data}`);
      }
    }
    
    class MyCloudStorage implements MyStorage {
      save(data: string): void {
        console.log(`클라우드에 저장: ${data}`);
      }
    }
    
    class Database {
      // 상위 수준 모듈인 MyStorage 타입을 의존! 
      // 여기서 MyLocalStorage, MyCloudStorage 같은 하위 수준 모듈에 의존하지 않는다다
      constructor(private storage: MyStorage) {}
    
      saveData(data: string): void {
        this.storage.save(data);
      }
    }
    
    const myLocalStorage = new MyLocalStorage();
    const myCloudStorage = new MyCloudStorage();
    
    const myLocalDatabase = new Database(myLocalStorage);
    const myCloudDatabase = new Database(myCloudStorage);
    
    myLocalDatabase.saveData("로컬 데이터");
    myCloudDatabase.saveData("클라우드 데이터");
    
______________
실습3

index.ts
0.00MB

 

 

 

_______________
07. 강의 요약

- 1. TypeScript
    - JavaScript의 상위 집합(superset)으로 정적 타입 검사를 지원
    - 더 안전하고 예측 가능한 코드를 작성할 수 있게 도움.
    - .ts 파일을 .js 파일로 컴파일하여 실행.

- 2. 기본 타입 및 고급 타입    
    ☑️ JavaScript에서 파생된 기본 타입    
    - string
    - number
    - boolean
    - null
    - undefined
    
    ☑️ 새로 추가된 타입    
    - tuple
    - enum
    - any
    - unknown

- 3. 타입의 집합화    
    ☑️ union 타입    
    - 여러 타입 중 하나를 가질 수 있는 타입을 정의할 때 사용.
    - 사용 방법: 타입1 `|` 타입2
    
    ☑️ intersection 타입    
    - 여러 타입의 조합을 가질 수 있는 타입을 정의할 때 사용. 
    - 사용 방법: 타입1 `&` 타입2

- 4. 타입과 인터페이스의 차이    
    ☑️ 타입    
    - 별칭을 사용하여 새로운 타입을 정의.
    
    ☑️ 인터페이스    
    - 객체의 구조를 정의하는 데 사용.
    - 클래스에서 구현.

- 5. 클래스와 상속    
    ☑️ 클래스    
    - 객체의 설계도로 사용되며 생성자, 메서드, 속성 등을 포함
    
    ☑️ 상속    
    - 클래스 간의 관계를 정의.
    - 부모 클래스의 기능을 자식 클래스가 상속받음음.

- 6 객체지향 설계 원칙 - S.O.L.I.D
    
    ☑️ 단일 책임 원칙(SRP)    
    - 한 클래스는 하나의 책임만 가져야 한다.
    
    ☑️ 개방 폐쇄 원칙(OCP)    
    - 확장에는 열려 있고 수정에는 닫혀 있어야 한다.
    
    ☑️ 리스코프 치환 원칙(LSP)    
    - 서브타입은 슈퍼타입을 대체할 수 있어야 한다.
    
    ☑️ 인터페이스 분리 원칙(ISP)    
    - 인터페이스는 클라이언트가 필요로 하는 메서드만 제공해야 한다.
    
    ☑️ 의존성 역전 원칙(DIP)    
    - 추상화에 의존해야 하며 구체화에 의존하면 안 된다.

    

'내일배움 강의 > 강의-TypeScript 문법 종합반' 카테고리의 다른 글

4주차  (0) 2025.01.18
3주차  (1) 2025.01.18
2주차  (0) 2025.01.17
nvm 설치  (0) 2025.01.17