본문 바로가기
TIL

TypeScript 강의 (12) ( 실습 : 도서관 프로그램 )

by 황민도 2024. 7. 31.

내일배움캠프 스파르타 코딩클럽

 

[ 도서관 프로그램 ]

도서관 프로그램 구성

1. 도서 추가 기능 - 사서

2. 도서 삭제 기능 - 사서

3. 도서 대여 기능 - 유저

4. 도서 반납 기능 - 유저

 

 

< package.json >

{
  "name": "lastts",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
	  "start": "tsc && node ./dist/index.js",
	  "build": "tsc --build",
	  "clean": "tsc --build --clean"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

 

< src. package.json >

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

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

class Member extends User {
	constructor(name: string, age: number) {
		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: User, book: Book): void; // 사용자가 책을 빌릴 때 호출하는 함수
	returnBook(user: User, 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) { // indexOf 는 해당 배열에 찾는 인자가 없으면 -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, book);
	myLibrary.returnBook(member2, book2);
}

main();

 

 

windo@DESKTOP-6SMB85M MINGW64 ~/lastTs
$ npm run start

> lastts@1.0.0 start
> tsc && node ./dist/index.js

대여할 수 있는 도서 목록: [
  {
    title: 'TypeScript 문법 종합반',
    author: '강창민',
    publishedDate: '2024-08-02T07:28:13.563Z'
  },
  {
    title: '금쪽이 훈육하기',
    author: '오은영',
    publishedDate: '2024-08-02T07:28:13.563Z'
  },
  {
    title: '요식업은 이렇게!',
    author: '백종원',
    publishedDate: '2024-08-02T07:28:13.563Z'
  }
]
예비개발자님이 [TypeScript 문법 종합반] 책을 빌렸습니다.
독서광님이 [금쪽이 훈육하기] 책을 빌렸습니다.
예비개발자님이 [TypeScript 문법 종합반] 책을 반납했어요!
독서광님은 [TypeScript 문법 종합반] 책을 빌린적이 없어요!
독서광님이 [금쪽이 훈육하기] 책을 반납했어요!

 

 

 

강의 요약

 

TypeScript

 - JavaScript의 상위 집합(superset)으로 정적 타입 검사를 지원한다.

 - 더 안전하고 예측 가능한 코드를 작성할 수 있게 도와준다.

 - .ts 파일을 .파일로 컴파일하여 실행한다.

 

기본 타입 및 고급 타입

 - JavaScript에서 파생된 기본 타입

  1. string

  2. number

  3. boolean

  4. null

  5. undefined

 - 새로 추가된 타입

  1. tuple

  2. enum

  3. any

  4. unknown

 

타입의 집합화

 - union 타입

  1. 여러 타입 중 하나를 가질 수 있는 타입을 정의할 때 사용한다. 취사선택!

  2. 사용 방법: 타입 1 | 타입 2

 - intersection 타입

  1. 여러 타입의 조합을 가질 수 있는 타입을 정의할 때 사용한다. 퓨전!

  2. 사용 방법: 타입 1 & 타입 2

 - 타입과 인터페이스의 차이

  1. 타입

     - 별칭을 사용하여 새로운 타입을 정의한다.

  2. 인터페이스

     - 객체의 구조를 정의하는 데 사용된다.

     - 클래스에서 구현할 수 있다.

 - 클래스와 상속

  1. 클래스

     - 객체의 설계도로 사용되며 생성자, 메서드, 속성 등을 포함한다.

  2. 상속

     - 클래스 간의 관계를 정의하는 방법이다.

     - 부모 클래스의 기능을 자식 클래스가 상속받는다.

 - 객체지향 설계 원틱 => S.O.L.I.D

  1. 단일 책임 원칙(SRP)

     - 한 클래스는 하나의 책임만 가져야 한다.

  2. 개방 폐쇄 원칙(OCP)

     - 확장에는 열려 있고 수정에는 닫혀 있어야 한다.

  3. 리스코프 치환원칙(LSP)

     - 서브타입은 슈퍼 타입을 대체할 수 있어야 한다.

  4. 인터페이스 분리 원칙(ISP)

     - 인터페이스는 클라이언트가 필요로 하는 메서드만 제공해야 한다.

  5. 의존성 역전 원칙(DIP)

     - 추상화에 의존해야 하며 구체화에 의존하면 안된다.

 

 

 

추가 학습 자료 

- 공식 매뉴얼 https://www.typescriptlang.org/docs/

 

The starting point for learning TypeScript

Find TypeScript starter projects: from Angular to React or Node.js and CLIs.

www.typescriptlang.org

 

 

- 공식 튜토리얼 https://www.typescriptlang.org/docs/handbook/intro.html

 

Handbook - The TypeScript Handbook

Your first step to learn TypeScript

www.typescriptlang.org

 

 

- 온라인 책 https://radlohead.gitbook.io/typescript-deep-dive/getting-started

 

시작하기 | TypeScript Deep Dive

타입스크립트 시작하기 타입스크립트는 자바스크립트로 컴파일이 되고 자바스크립트는 브라우저 또는 서버에서 실행될 것 입니다. 그래서 다음에 정의된 목록이 타입스크립트를 시작하는데

radlohead.gitbook.io