Today I Learned/TIL 08

2023 - 08 - 04 타입스크립트 프로젝트 - 도서관 프로그램

sangwoo_rhie 2023. 8. 4. 01:10

 

오늘할일 : 타입스크립트 클래스, 상속, 인터페이스를 활용해서 도서관 프로그램 만들기.

- 도서 추가 기능 - 사서
- 도서 삭제 기능 - 사서
- 도서 대여 기능 - 유저
- 도서 반납 기능 - 유저

1. 프로그램 셋팅

npm init -y

tsc --init --rootDir ./src --outDir ./dist --esModuleInterop --module commonjs --strict true --allowJS true --checkJS true
--rootDir ./src
    - 프로그램의 소스 파일이 들어가는 경로는 src 디렉토리
    
--outDir ./dist
    - 컴파일이 된 파일들이 들어가는 디렉토리는 dist 디렉토리
    
--esModuleInterop
    - CommonJS 방식의 모듈을 ES모듈 방식의 import 구문으로 가져올 수 있다

https://teamsparta.notion.site/tsconfig-json-18e9fb5007194b3a9e659b151f1253ae

 

tsconfig.json 해부하기

[수업 목표]

teamsparta.notion.site

2. package.json "script" 항목 변경하기.

"scripts": {
    "start": "tsc && node ./dist/index.js",
    "build": "tsc --build",
    "clean": "tsc --build --clean"
},

 

3. src 디렉토리 생성

 

4. Role이라는 enum을 정의하기

enum Role {
  LIBRARIAN, // 사서
  MEMBER, // 멤버
}

 

5. 유저 추상 클래스

abstract class User {
  constructor(public name: string, public age: number) {}
  abstract getRole(): Role;
}

 

6. Member 클래스: 추상클래스를 상속받는 클래스 1

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

 

7. Librarian 클래스 : 추상클래스를 상속받는 클래스 2

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

 

8. Book 클래스 : 이름, 저자, 출판일로 구성됨

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

 

9. RentManager 인터페이스 : 도서관이 꼭 갖추어야 할 기능을 정의한 명세서

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;
}

 

10. 구현하기:

getBooks함수는 books를 깊은 복사해서 던져주기

addBook, removeBook은 사서만 호출할 수 있게 하기

rentBook은 유저만 호출할 수 있게 하기

rentBook에서는 다른 책을 대여한 유저는 책을 대여할 수 없어야 함.

returnBook에서는 책을 빌린 사람들만 반납할 수 있게 하기.

 

class Library implements RentManager {
  private books: Book[] = [];
  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();

깊은복사 얕은복사 개념

https://sangwoorhie.tistory.com/47

 

4. 불변 객체 (깊은 복사 얕은 복사)

이름을 변경하는 함수 이름을 변경하는 함수, 'changeName'을 정의 입력값 : 변경대상 user 객체, 변경하고자 하는 이름 출력값 : 새로운 user 객체 특징 : 객체의 프로퍼티(속성)에 접근해서 이름을 변

sangwoorhie.tistory.com