본문 바로가기

Front-End/TypeScript

TypeScript #13 (OOP 연습 문제 #1~10)

반응형

✅ 1단계: 클래스와 객체 만들기

※ 목표: 클래스를 만들고 객체를 생성해보기
▶ Person이라는 클래스를 만들고, 이름(name)과 나이(age)를 속성으로 가진 후,

introduce() 메서드를 만들어 "안녕하세요, 저는 20살의 Alice입니다."와 같은 문장을 출력

class Person {          // Person 클래스 생성 : name 속성, age 속성
    name: string;
    age: Number;

    introduce() {           // 클래스 안에서 지정한 속성 사용 - this.속성
        console.log(`안녕하세요, ${this.age}살의 ${this.name}입니다.`)
    }
}

// Person 클래스의 객체(인스턴스) 생성
let person = new Person();
person.age = 22;
person.name = "Alice";

// 생성된 객체에서 클래스 내 메소드 호출
person.introduce();

 

 

 

✅ 2단계: 생성자(Constructor) 사용

목표: 생성자를 통해 속성 초기화 연습
  Product 클래스를 만들고, 생성자를 통해 name과 price를 입력받아 초기화하기.

그리고 display()라는 메서드를 만들어 "제품명: MacBook, 가격: 150만원"을 출력하기.

class Product {         // Product 클래스 생성
    
    // 생성자에서 클래스 name, price 속성 설정
    constructor(public name: string, public price: number) {}
    
    // 클래스 내의 속성들을 이용한 display 메소드 생성
    display() {
        console.log(`제품명: ${this.name}, 가격: ${this.price/10000}만원`)
    }
}

// Product 클래스의 객체 생성, 속성값 import
let product = new Product("MacBook", 1500000);

// 생성된 객체에서 display 메소드 호출
product.display();

 

 

 

✅ 3단계: 접근 제한자 (캡슐화)

※목표: private 속성 사용해보기
▶ BankAccount 클래스를 만들고, #balance(잔액)를 private 속성으로 선언하기.
deposit(amount: number)와 getBalance() 메서드를 구현해 입금 및 조회 기능을 제공하기

class BankAccount {         // BankAccount 클래스 생성
    // 계좌 잔고 설정 - private
    private balance: number = 100;       // #속성 : 속성을 private로 선언, 최신 버전에서만 인식

    deposit(amount: number) {           // amount를 속성으로 갖는 예금 메서드 생성
        this.balance += amount;         // 예금 액수 만큼 잔고에 추가하는 기능
    }

    getBalance() {          // 계좌 잔고를 확인하는 메서드
        console.log(`${this.balance}원`);       // 클래스 내의 속성을 이용 (this.balance)
    }

    setBalance(amount: number) {        // private 속성에는 setter가 필요
        amount = amount * 1.1;          // 금리 기능 추가. 예금 액수에 10% 가산
        this.balance = amount;          // 금리가 적용된 최종 금액을 계좌 잔고로 업데이트
    }
}

// BankAccount 클래스의 객체 생성
let account = new BankAccount();

// 생성된 객체에서 금액 1만원 예금
account.setBalance(10000);

// 계좌 잔고 확인
account.getBalance();

 

 

 

✅ 4단계: 상속(Inheritance)

※ 목표: 클래스를 상속해서 기능 확장하기
▶ Animal 클래스를 만들고 sound() 메서드를 정의하기.

Dog, Cat 클래스를 Animal로부터 상속받아 각각 "멍멍!", "야옹!"을 출력하도록 오버라이딩하기

class Animal {          // Animal 클래스 생성 (부모 클래스)

    animal: string;     // 클래스에서 받을 animal 속성 설정

    sound() {           // 동물이 짖는 메소드 생성
        console.log("!!!")
    }
}


// Animal 클래스에게 상속 받는 Doggo 클래스 생성 (자식 클래스, extends 사용)
class Doggo extends Animal {        
    sound() {           // bark 메소드 생성 (오버라이딩)
        console.log("멍멍!")
    }
}


// Animal 클래스에게 상속 받는 Kitty 클래스 생성 (자식 클래스, extends 사용)
class Kitty extends Animal {
    sound() {           // meow 메소드 생성 (오버라이딩)
        console.log("야옹!")
    }
}


// 각각의 자식 클래스로부터의 객체 생성
let doggo = new Doggo();
let kitty = new Kitty();

// 생성된 각각의 객체에서 메소드 호출
doggo.sound();
kitty.sound();

 

 

 

✅ 5단계: 다형성(Polymorphism)

※ 목표: 하나의 타입으로 여러 객체 다루기
▶ 위에서 만든 Animal 클래스를 이용해 const animals: Animal[] = [new Dog(), new Cat()] 배열을 만들고,

반복문을 통해 sound()를 호출하여 각각의 소리를 출력하기.

// 4단계에서 만든 animal.ts, 원본 파일에서 각 클래스에 export를 붙여 모듈화
export class Animal {        
	...
}

export class Doggo extends Animal {        
	...
}

export class Kitty extends Animal {
	...
}

...

// 4단계에서 호출한 라인들은 모두 주석 처리 !
// // 생성된 각각의 객체에서 메소드 호출
// doggo.bark();
// kitty.meow();
import { Animal, Doggo, Kitty } from "./animal";            // 원본 파일로부터 모듈화된 각 클래스 호출

// animals 변수에 Animal이라는 배열 만들어서 배열 안에 Doggo와 Kitty 클래스의 객체들 넣기
const animals: Animal[] = [new Doggo(), new Kitty()];       

// 반복문으로 각 동물의 소리 출력
// 같은 sound() 메소드를 호출하지만, 각 객체의 실제 타입에 따라 다른 소리가 출력
animals.forEach(animal => {
    animal.sound();  // 다형성! 같은 메소드명이지만 다른 결과
});

 

 

 

✅ 6단계: 추상 클래스(Abstract)

※ 목표: 공통 구조 정의와 구현 강제화
▶ Shape라는 추상 클래스를 만들고, getArea(): number 추상 메서드를 정의하기

Rectangle, Circle 클래스를 상속받아 각각 면적을 계산하는 로직을 구현하기

abstract class Shape {          // abstract로 추상 클래스 생성
    size: number;
    // calculateArea() {
    //     return 1;
    // }

    // 추상 클래스 안에 있는 메서드도 무조건 추상 abstract 붙여야 함
    // 이 클래스를 상속 받는 자식 클래스에서 "필수로 설정"해야할 메서드를 선언
    abstract getAreaRectangle(width: number, height: number): number;
    abstract getAreaCircle(radius: number): number;
}


// 부모 클래스를 상속 받는 자식 클래스에서 추상 메서드를 구체적으로 설계
class Rectangle extends Shape {
    getAreaRectangle(width: number, height: number): number {
        return width * height;
    }
    // 다른 추상 클래스에 대해서는 예외처리 해야함. 에러 출력
    getAreaCircle(radius: number): number {
        throw Error("Unimplemented method")
    } 
}


class Circle extends Shape {
    getAreaCircle(radius: number): number {
        return radius ** 2 * Math.PI
    }
    getAreaRectangle(width: number, height: number): number {
        throw Error("Unimplemented method")
    }
}

 

 

※ 오류를 출력하는 메서드를 호출할 경우.

 

 

 

✅ 7단계: 인터페이스(Interface)

※ 목표: 객체의 형태를 정의하고 클래스에 구현
▶ Movable이라는 인터페이스를 만들고 move(): void 메서드를 포함시키기.

Car, Robot 클래스에 이 인터페이스를 구현하고 각각 다르게 move()를 정의하기.

interface Moveable {            // 특정 클래스들에게 꼭 지켜야 하는 규약 설정, 타입 정리할 때 많이 씀
    move(): void;
}

// 클래스에 implements를 넣어 해당 인터페이스를 따름
class Car0 implements Moveable {
    move(): void {
        console.log("car");
    }
}


class Robot implements Moveable {
    move(): void {
        console.log("robot");
    }
}

 

 

 

✅ 8단계: 메서드 오버로딩

※ 목표: 하나의 메서드 이름으로 다양한 인자 처리
▶ Calculator 클래스를 만들고 add() 메서드를 오버로딩하여 숫자 2개를 더할 수 있도록 하고,
문자열 2개도 연결할 수 있도록 구현하기

class Calculator {
    add(a: number, b: number): void {
        console.log(a + b);
    }


    add(a: string, b: number): void {
        console.log(a + b);
    }
}

let calculator = new Calculator();
calculator.add(1, 2);
calculator.add("1", "2");

 

 

 

 

✅ 9단계: 정적 속성/메서드 (static)

※ 목표: 클래스 소속 메서드와 속성 다루기
▶ Counter 클래스를 만들고 모든 객체가 공유하는 count 값을 가지게 하기
increment()를 호출할 때마다 count가 1씩 증가하도록 하기. getCount() 메서드로 현재 값을 출력

class Counter {
    static count: number = 0;

    static increment() {
        Counter.count++;
    }

    static getCount() {
        console.log(Counter.count);
    }
}


// 인스턴스(객체) 생성 없이 사용 가능
// 클래스 이름으로 직접 접근
// 모든 곳에서 같은 값 공유
Counter.increment();
Counter.increment();
Counter.increment();
Counter.getCount();

 

 

 

✅ 10단계: 종합 실습 프로젝트 (간단한 주문 시스템)

※ 목표: 객체지향 종합 적용

User, Product, Order 클래스를 만들어
사용자 User가 제품 Product를 주문하여 Order를 생성하고
Order 클래스에 summary() 메서드를 만들어 "사용자 A가 상품 B를 구매했습니다."를 출력하세요.

// 주문 시스템은 그림을 그리든 ERD를 그리든 그려서 이해하는 게 더 빠르다.
class User0 {           // 이용자, 고객 클래스
    userName: string;
}

class Product {         // 제품, 상품 클래스
    productName: string;
}

class Order {           // 주문 클래스 (고객과 상품을 받음)
    user: User0;
    product: Product;

    summary() {         // 다른 클래스에서 받은 속성들을 이용하여 로그 출력
        console.log(`사용자 ${this.user.userName}이(가) 상품 ${this.product.productName}을 주문했습니다.`)
    }
}


// 각 클래스의 객체 생성

let user1 = new User0();
user1.userName = "홍길동"

let product1 = new Product();
product1.productName = "책"


// 주문 클래스의 객체는 생성할 때 다른 클래스의 속성을 호출

let order1 = new Order();
order1.user = user1;
order1.product = product1;


// 주문 객체에서의 메서드 호출

order1.summary();

반응형

'Front-End > TypeScript' 카테고리의 다른 글

TypeScript #14 (제네릭)  (0) 2025.07.04
TypeScript #12 (OOP 예제 #1~5)  (0) 2025.07.03
TypeScript #11 (객체 지향)  (0) 2025.07.03
TypeScript #10 (함수 유효성 검사)  (0) 2025.07.02
TypeScript #9 (함수)  (0) 2025.07.02