메타버스 백엔드 웹반

[Node.js] 클래스와 객체의 진화: 타입스크립트로 더 깔끔하고 효율적인 코드 만들기

thisnorm 2025. 1. 10. 17:52

📌 객체 타입

  • 함수에서 매개변수로 객체 타입을 넣을 수 있다.
  • 넣을 때 구분자로 ‘ , ‘ 또는 ‘ ; ‘ 둘 다 사용 가능하다.
  • 반환 타입으로 any타입은 사용x
    • GPT를 그냥 이용하면 모든 코드에 다 any타입이 붙어있음 조심
function printName(user: { first: string, last?: string }){
last?를 사용함으로써 lastname을 입력하지 않아도 출력할 수 있게 해준다.
first: string, last: string or frist: string; last: string;
	if (user.last !== undefined) {
		console.log("Your First name is " + user.first.toUpperCase());
		console.log("Your Last name is " + user.last.toUpperCase());
	} else {
			console.log("Your name is " + user.first.toLowerCase());
	}
}

print({ first: "Bob" });

📌 Type Alias & Interface

function printCoord(point: {x: number, y: number}) {
    console.log("The coordinate's x value is " + point.x);
    console.log("The coordinate's y value is " + point.y);
}
해당 객체의 속성이 같다면,, 하지만 파라미터가 point1,2,3 ... 255까지 늘어난다면
계속해서 중복코드가 늘어남

이 때,
type Point = {
    x: number,
    y: number
}
타입을 지정해준다면

function printCoord(point: Point) {
    console.log("The coordinate's x value is " + point.x);
    console.log("The coordinate's y value is " + point.y);
}
이렇게 코드가 줄어들 수 있다.
타입 별칭은 타입의 대명사를 만드는 것이라고 생각하면 된다.

type을 두개를 정의할 수도 있다.
function printId(id: number | string){}

type Id = number | string;
function printId(id: Id)

타입 별칭은 생각보다 많이 안쓰고 interface를 권장한다.
interface Point {
    x: number,
    y: number
}
기존 코드는 안건들이고 덧붙이는 것이 가능하기 때문이다.
 

 

예약어

변수 선언 시, let, const, var, type 등 이런 것들을 예약어라고 한다.


📌 리터럴 타입

  • 특정 값 자체가 타입이 되는 것

→ 타입을 강제하는 것이기 때문에 재사용성이 떨어져서 Enum(열거형 타입)으로 대체한다.

→상태나 종류를 구분하는데 많이 사용된다.

function checkDayType(): void {
    const currentDay = new Date().getDay();

    const isWorkoutDay = currentDay === 2 || currentDay === 4;
    const isWeekend = currentDay === 0 || currentDay === 6;

    const isWorkingDay =
    currentDay !== 0 && currentDay !== 6 &&
    currentDay !== 1 && currentDay ! == 3;

    console.log(`Today is day number ${currentDay}.`);
    console.log(`Is today a workout day? ${isWorkoutDay}.`);
    console.log(`Is today a weekent day? ${isWeekend}.`);
    console.log(`Is today a working day? ${isWorkingDay}.`);
}
과연 각 숫자들의 의미는 정확히 무엇인지 불확실하다.
이것만 보면 다른 작업자가 알 수 없기 때문에 enum 열거형 타입으로 대체한다.

function checkDayType(): void {
    const currentDay = new Date().getDay();

    const isWorkoutDay = currentDay === Day.Tuesday || currentDay === Day.Thursday;
    const isWeekend = currentDay === Day.Sunday || currentDay === Day.Saturday;

    const isWorkingDay =
    currentDay !== Day.Sunday && currentDay !== Day.Saturday &&
    currentDay !== Day.Monday && currentDay ! == Day.Wednesday;

    console.log(`Today is day number ${currentDay}.`);
    console.log(`Is today a workout day? ${isWorkoutDay}.`);
    console.log(`Is today a weekent day? ${isWeekend}.`);
    console.log(`Is today a working day? ${isWorkingDay}.`);
}

enum Day {
    Sunday = 0,
    Monday = 1,
    Tuesday = 2,
    Wednesday = 3,
    Thursday = 4,
    Friday = 5,
    Saturday = 6,
}

이러면 0 ~ 6 이 무엇을 의미하는지 코드만 보고도 해석해낼 수 있다!

📌 객체와 인스턴트

  • 객체의 비효율성
// object1
let robot1 = {
    // Property(속성)
    name: "R2-D2",
    model: "AstroMech",
    status: "Active",

    // Method(행동)
    performTask: function (task: string) {
        console.log(`${this.name} is perfroming ${task}.`);
    },
    updateStatus: function (newStatus: string) {
        this.status = newStatus;
        console.log(`${this.name}'s status is now ${this.status}`)
    }
}

// object2
let robot2 = {
    // Property(속성)
    name: "A2-A2",
    model: "Proto",
    status: "Active",

    // Method(행동)
    performTask: function (task: string) {
        console.log(`${this.name} is perfroming ${task}.`);
    },
    updateStatus: function (newStatus: string) {
        this.status = newStatus;
        console.log(`${this.name}'s status is now ${this.status}`)
    }
}

// 1object3
let robot3 = {
    // Property(속성)
    name: "A1-AB2",
    model: "AD-22",
    status: "InActive",

    // Method(행동)
    performTask: function (task: string) {
        console.log(`${this.name} is perfroming ${task}.`);
    },
    updateStatus: function (newStatus: string) {
        this.status = newStatus;
        console.log(`${this.name}'s status is now ${this.status}`)
    }
}

usage with "." dot operator
console.log(robot.name) // Accessing Property
robot.performTask("Getting ready to move") // Calling Method


추가와 수정에 비효율적임
 
  • robot 1, 2, 3을 만들고 싶으면 똑같은 코드로 객체를 3개 만들어야한다.
  • 코드를 업데이트하게 되면 3개를 다 고쳐야 하므로 여러개 일때는 비효율적이다.

 

 

그래서 class를 만들고 인스턴트를 이용하자!

// Class 이용
class Robot { // Members
    // Property 또는 Field(속성, 필드)
    name: string;
    model: string;
    status: string = "Active";

    // Constructor(생성자)
    constructor(name: string, model: string) {
        this.name = name;
        this.model = model;
    }

    // Method(행동)
    performTask(task: string) {
        console.log(`${this.name} is perfroming ${task}.`);
    };

    updateStatus(newStatus: string) {
        this.status = newStatus;
        console.log(`${this.name}'s status is now ${this.status}`)
    };
}

// Create Instance of the Robot class;
let r1 = new Robot("R2-aD1", "Optimus");
let r2 = new Robot("R5-AA3", "Bumble");
let r3 = new Robot("Ad-aD1", "Rotus");

// Accessing fields and Calling methods
console.log(r1.name);
console.log(r2.model);
console.log(r3.status);

r1.performTask("Charging");
r2.performTask("Explorering");
r3.updateStatus("On Repair");
 

인스턴트를 만드는데 생성자가 필요하지만

생성자가 없는 경우

// No-args Constructor
class Pet {
    category: string = "Cat";
    name: string = "Chu";

    // 기본 생성자
    // constructor() {}
}

let p1 = new Pet();
console.log(p1.name);
 

javascript 자체에서 기본 생성자를 제공해주기 때문에 typescript에서도 제공한다.

 

👴 부모클래스와 👦 자식클래스간의 상속

기존 코드는 
class Robot { // Members
    // Property 또는 Field(속성, 필드)
    name: string;
    model: string;
    status: string = "Active";

    // Constructor(생성자)
    constructor(name: string, model: string) {
        this.name = name; 
        this.model = model;
    }

    // Method(행동)
    performTask(task: string) {
        console.log(`${this.name} is perfroming ${task}.`);
    };

    updateStatus(newStatus: string) {
        this.status = newStatus;
        console.log(`${this.name}'s status is now ${this.status}`)
    };
}
Robot 클래스와 별개로 CleaningRobot 클래스를 만들고 싶다면
class CleaningRobot { // Members
    // Property 또는 Field(속성, 필드)
    name: string;
    model: string;
    cleaningSchedule: string[];
    status: string = "Active";

    // Constructor(생성자)
    constructor(name: string, cleaningSchedule: string[], model: string) {
        this.name = name;
        this.cleaningSchedule = cleaningSchedule;
        this.model = model;
    }

    // Method(행동)
    performTask(task: string) {
        console.log(`${this.name} is perfroming ${task}.`);
    };

    updateStatus(newStatus: string) {
        this.status = newStatus;
        console.log(`${this.name}'s status is now ${this.status}`)
    };

    performCleaning() {
        console.log(`${this.name} is cleaning according to the schedule ${this.cleaningSchedule.join(", ")}.`);
    }
}

이런식으로 따로 정의해줘야 한다. 이런식으로 비슷한 형태의 로봇이 많으면 계속해서 만들어서
업데이트할 때마다 따로 해줘야한다.

그래서 상속을 통하여 확장성과 유연성을 확보한다!
class CleaningRobot extends Robot { // Members
    // Property 또는 Field(속성, 필드)
    cleaningSchedule: string[];

    // Constructor(생성자)
    constructor(name: string, cleaningSchedule: string[], model: string) {
        super(name, model);
        this.cleaningSchedule = cleaningSchedule;
    }

    // Method(행동)
    // performTask() {
    override performTask() {
        console.log(`${this.name} is cleaning according to the schedule ${this.cleaningSchedule.join(", ")}.`);
    }
}
이런식으로 하면 Robot 클래스에 있는 동일한 기능들은 가져와 쓰고 수정도 Robot 클래스 내에서만
하면 된다. 그리고 부모 클래스에 있는 속성을 가지고 오는 함수로는 super()를 쓰면 된다.


override
부모 클래스에 있는 메소드를 가져와 새롭게 정의하는 내장 함수
overloading
메소드의 이름만 같다면 매개변수의 타입과 개수에 의해 호출되게 하는 내장 함수
 

🚫 접근 제어자

  • 자바

public - protected - default - private

 

  • 타입스크립트

public(default) - protected - private

 

  • public

모든 클래스에서 접근 가능

 

  • protected

같은 클래스와 자식 클래스에서 접근 가능

 

  • private

해당 클래스 내에서만 접근 가능

 

클래스 외부에서 수정하는 것을 막기 위해서 private 접근 제어자를 사용한다.

class Robot { // Members
    // Property 또는 Field(속성, 필드)
    private name: string;
    private model: string;
    protected status: string = "Active";

    // Constructor(생성자)
    constructor(name: string, model: string) {
        this.name = name; 
        this.model = model;
    };

    // Getter for name
    public getName(): string {
        return this.name;
    }
    public getModel(): string {
        return this.model;
    }
}
name과 model의 접근 제어자가 private으로 바꼈으므로 외부에서는 참조할 수 없다.
그러므로 참조하고 싶다면 Getter 함수를 이용해야 한다.
외부에서 사용할 때도 this.name -> this.getName() 이런식으로 사용해야 한다.
이러면 데이터의 일관성을 만족할 수 있다.
 

 

 

긴 글 읽어주셔서 감사합니다.

내일도 열심히 포스팅해볼게요!

 

다들 화이팅~