본문 바로가기

카테고리 없음

Study_240626 ( TS , React Cookbook , 객체 지향 프로그램, 도서 관리 코드 )

터미널에서 ts-node 사용법 : yarn global add ts-node -> 전역적으로 설치 ->  ts-node 파일명

 

function sum(a: number, b: number): number {
  return a + b;
}

function objSum({ a, b }: { a: number; b: number }): string {
  return `${a + b}`;
}

 

type Person = { id: number; age: number; height: number };

async function getPerson(): Promise<Person[]> {
  const res = await fetch(`http://localhost:5008/people`);
  if (!res.ok) {
    throw new Error();
  }
  return res.json();
  // 이렇게 해도 res[0].id 이런식으로 접근가능 함수 타입 안정해도됨
  return res.json() as any as Person[];
}
//타입 명시로 속성 접근 가능
getPerson().then((res) => console.log(res[0].age));

 

리액트앱 TS로 만들기

npx create-react-app 생성할 폴더명 --template typescript

 

// 제네릭(generic)이란 데이터의 타입(data type)을 일반화한다(generalize)=변수화한다는 것을 의미
type Generic<T> = {
  someValue: T;
};

type Test = Generic<string>;

function someFunc<T>(value: T) {}

someFunc<string>("hello");

 

import React, { useState } from "react";

function App() {
  // 타입 명시 없이 값을 넣을 경우(이니셜 값) 값에 맞는 타입으로 자동으로 들어감
  const [counter, setCounter] = useState<number>(1);
  const increment = () => {
    setCounter((prev) => prev++);
  };
  return <div onClick={increment}>{counter}</div>;
}

export default App;

 

import React, { useState } from "react";

function Parent() {
  // 이니셜값도 맞는 타입으로 할당
  const [count, setCount] = useState("");
  return <Child count={count}></Child>;
}

type Props = {
  count: string;
};

// 타입을 명시안하면 any타입이므로 타입 명시해줘야함
function Child({ count }: Props) {
  return <div>{count}</div>;
}

export default Parent;

 

Props 전달 3가지 예시

import React, { useState } from "react";

type Todo = {
  id: string;
  isDone: boolean;
};

function App() {
  const [todos, setTodos] = useState<Todo[]>([]);

  const deleteTodo = (id: string) => {
    const newTodos = todos.filter((todo) => todo.id === id);
    setTodos(newTodos);
  };
  return (
    <>
      {todos.map(({ id }) => (
        <Todo key={id} id={id} deleteTodo={deleteTodo} />
      ))}
    </>
  );
}

function Todo({
  id,
  deleteTodo,
}: {
  id: string;
  deleteTodo: (id: string) => void;
}) {
  const handleOnClick = () => {
    deleteTodo(id);
  };
  return <div onClick={handleOnClick}></div>;
}

export default App;
import { PropsWithChildren } from "react";

type BaseType = {
  id: string;
};

function Child({ children }: PropsWithChildren<BaseType>) {
  return <div>{children}</div>;
}

export function Parent() {
  return <Child id=""></Child>;
}
import React, { ReactNode } from "react";

type BaseType = {
  id: string;
};

type StrictChildren<T> = T & { children: ReactNode };

function Child({ children }: StrictChildren<BaseType>) {
  return <div>{children}</div>;
}

export function Parent() {
  return (
    <Child id="">
      <div>chlidren</div>
    </Child>
  );
}

 

유틸리티 타입

// UtilityType.tsx
import {
  AddressComponent,
  PersonChildComponent,
  ProfileComponent,
} from "./UtilityTypeChildren";

export type PersonProps = {
  id: string;
  description: string;
  address: string;
  age: number;
  profile: string;
};

export const PersonComponent = ({
  id,
  description,
  address,
  age,
  profile,
}: PersonProps) => {
  return (
    <>
      <PersonChildComponent>
        <div>{id}</div>
      </PersonChildComponent>
      <ProfileComponent
        description={description}
        address={address}
        age={age}
        profile={profile}
      />
      <AddressComponent address={address} />
    </>
  );
};
// UtilityTypeChildren.tsx
import { PropsWithChildren, ReactNode } from "react";
import { PersonProps } from "./UtilityType";

export const PersonChildComponent = ({ children }: PropsWithChildren) => {
  return <>{children}</>;
};

type OmitType = Omit<PersonProps, "id">;

export const ProfileComponent = ({
  description,
  address,
  age,
  profile,
}: OmitType) => {
  return <></>;
};

type PickType = Pick<PersonProps, "address">;

export const AddressComponent = ({ address }: PickType) => {
  return <></>;
};

 

Event Handler

import { useState } from "react";

function App() {
  const [counter, setCounter] = useState<number>(1);
  const eventHandler = (e: React.MouseEvent<HTMLDivElement>) => {};
  return <div onClick={eventHandler}>{counter}</div>;
}

export default App;

 

인터페이스

    1. 인터페이스란?
    • 인터페이스는 TypeScript에서 객체의 타입을 정의하는데 사용
    • 인터페이스는 객체가 가져야 하는 속성과 메서드를 정의
    • 인터페이스를 구현한 객체는 인터페이스를 반드시 준수해야함! 규약과 같아서 어길 수가 없음
    • 인터페이스를 사용하면 코드의 안정성을 높이고 유지 보수성을 향상 가능
  • ☑️ 구현부 제공 여부
    • 추상 클래스
      • 클래스의 기본 구현을 제공.
    • 인터페이스
      • 객체의 구조만을 정의하고 기본 구현을 제공하지 않음
    ☑️ 상속 메커니즘
    • 추상 클래스
      • 단일 상속만 지원
    • 인터페이스
      • 다중 상속을 지원
      • 즉, 하나의 클래스는 여러 인터페이스를 구현 가능
    ☑️ 구현 메커니즘
    • 추상 클래스
      • 추상 클래스를 상속받은 자식 클래스는 반드시 추상 함수를 구현해야 함
    • 인터페이스
      • 인터페이스를 구현하는 클래스는 인터페이스에 정의된 모든 메서드를 전부 구현해야 함
    ☑️ 언제 쓰면 좋을까요?
    • 기본 구현을 제공하고 상속을 통해 확장하는데 초점을 맞추고 싶다면 → 추상 클래스
    • 객체가 완벽하게 특정 구조를 준수하도록 강제하고 싶다면 → 인터페이스

도서 관리 코드

// 열거형 타입
enum Role {
  LIBRARIAN, // 사서
  MEMBER, // 멤버
}

// 추상 클래스 -> 공통 속성과 메서드 정의
abstract class User {
  // 생성자 함수 -> name age매개변수 받아서 클래스 속성으로 설정
  constructor(public name: string, public age: number) {}
  // 추상 함수 -> 상속 받는 클래스에서 구현해야함
  abstract getRole(): Role;
}

class Member extends User {
  constructor(public name: string, public age: number) {
    // 부모 클래스 생성자 호출하여 name과 age를 초기화
    super(name, age);
  }
  getRole(): Role {
    return Role.MEMBER;
  }
}

class Librarian extends User {
  constructor(name: string, age: number) {
    super(name, age);
  }
  getRole(): Role {
    return Role.LIBRARIAN;
  }
}

class Book {
  constructor(
    public title: string,
    public author: string,
    public publishedDate: Date
  ) {}
}

interface RentManager {
  getBooks(): Book[];
  addBook(user: User, book: Book): void;
  removeBook(user: User, book: Book): void;
  rentBook(user: Member, book: Book): void;
  returnBook(user: Member, book: Book): void;
}

class Library implements RentManager {
  private books: Book[] = [];
  // rentedBooks는 유저의 대여 이력을 관리
  private rentedBooks: Map<string, Book> = new Map<string, Book>();

  getBooks(): Book[] {
    // 깊은 복사를 하여 외부에서 books를 수정하는 것을 방지
    return JSON.parse(JSON.stringify(this.books));
  }

  addBook(user: User, book: Book): void {
    if (user.getRole() !== Role.LIBRARIAN) {
      console.log("사서만 도서를 추가할 수 있습니다.");
      return;
    }

    this.books.push(book);
  }

  removeBook(user: User, book: Book): void {
    if (user.getRole() !== Role.LIBRARIAN) {
      console.log("사서만 도서를 삭제할 수 있습니다.");
      return;
    }

    const index = this.books.indexOf(book);
    if (index !== -1) {
      this.books.splice(index, 1);
    }
  }

  rentBook(user: User, book: Book): void {
    if (user.getRole() !== Role.MEMBER) {
      console.log("유저만 도서를 대여할 수 있습니다.");
      return;
    }

    if (this.rentedBooks.has(user.name)) {
      console.log(
        `${user.name}님은 이미 다른 책을 대여중이라 빌릴 수 없습니다.`
      );
    } else {
      this.rentedBooks.set(user.name, book);
      console.log(`${user.name}님이 [${book.title}] 책을 빌렸습니다.`);
    }
  }

  returnBook(user: User, book: Book): void {
    if (user.getRole() !== Role.MEMBER) {
      console.log("유저만 도서를 반납할 수 있습니다.");
      return;
    }

    if (this.rentedBooks.get(user.name) === book) {
      this.rentedBooks.delete(user.name);
      console.log(`${user.name}님이 [${book.title}] 책을 반납했어요!`);
    } else {
      console.log(`${user.name}님은 [${book.title}] 책을 빌린적이 없어요!`);
    }
  }
}

function main() {
  const myLibrary = new Library();
  const librarian = new Librarian("르탄이", 30);
  const member1 = new Member("예비개발자", 30);
  const member2 = new Member("독서광", 28);

  const book = new Book("TypeScript 문법 종합반", "강창민", new Date());
  const book2 = new Book("금쪽이 훈육하기", "오은영", new Date());
  const book3 = new Book("요식업은 이렇게!", "백종원", new Date());

  myLibrary.addBook(librarian, book);
  myLibrary.addBook(librarian, book2);
  myLibrary.addBook(librarian, book3);
  const books = myLibrary.getBooks();
  console.log("대여할 수 있는 도서 목록:", books);

  myLibrary.rentBook(member1, book);
  myLibrary.rentBook(member2, book2);

  myLibrary.returnBook(member1, book);
  myLibrary.returnBook(member2, book2);
}

main();

 

 

  • extends: 클래스를 상속받아 기존 클래스의 속성과 메서드를 재사용하고 확장할 때 사용
  • implements: 인터페이스를 구현하여 해당 인터페이스가 정의한 메서드와 속성을 반드시 구현하도록 강제할 때 사용