[타입스크립트 입문 - 기초부터 실전까지] 두 번째 프로젝트 - 전화번호부 애플리케이션

23392 단어 typescripttypescript

프로젝트 소개

프로젝트 실습 파일:
실습자료 - learn-typescript - quiz - 2_address-book

  • $ npm i을 이용해서 package.json에 정의해놓은 라이브러리들을 설치하자.

프로젝트 실습 방법 안내

✔️ tsconfig.json

  • "compilerOptions"에서 "noImplicitAny"true로 바꿔주자.
    암묵적으로 any를 추론하게 하지 말고 any라도 무조건 선언하게 하도록 한 것.
  • "compilerOptions""strict": true, "strictFunctionTypes": true도 추가 해주자.

✔️ .eslintrc.js

  • 에러를 명시적으로 표시해주기 위해서
    '@typescript-eslint/no-explicit-any': 'off',
    "@typescript-eslint/explicit-function-return-type": 'off'를 주석처리 해주자.

애플리케이션에 정의된 타입 설명 및 API 함수 타입 정의 힌트

실제 애플리케이션 개발시 API를 호출해 와서 API의 응답에 규칙, 규격을 정의할 때 제네릭을 가장 많이 사용하게 된다.


Promise를 이용한 API 함수 타입 정의

동기 처리

function fetchItems(): string[] {
    var items = ['a',  'b', 'c'];
    return items;
}
var result = fetchItems();
console.log(result);

fetchItems()함수의 반환값이 string으로 이루어진 배열이라는 것을 타입스크립트가 추론할 수 있다.

비동기 처리

function fetchItems() {
    var items = ['a', 'b', 'c'];
    return new Promise(function(resolve) {
        resolve(items);
    })
}
  • 위 코드를 저장하면 fetchItems(): Promise<unknown>이라는 메세지가 뜨게 된다.
    즉, new Promise()를 반환하게 되면 함수에서 기본적으로 추론하는게 Promise가 오는데 그 안의 타입은 잘 모르겠다라고 보는 것이다.
  • 현재 fetchItem()함수를 실행하는 시점에서 타입스크립트가 Promise안에 들어오는 비동기 코드들에 대해 알 수 없는 것이다.

    Promise에 어떤 타입이 올것인지 즉, 비동기 처리를 통해 돌려받을 반환 값이 무엇인지 명시를 해줘야 Promise를 제대로 사용할 수 있다.

function fetchItems(): Promise<string[]> {
	   var items = ['a', 'b', 'c'];
	   return new Promise(function(resolve) {
	       resolve(items);
	   })
}
fetchItems();

전화번호부 클래스 선언부 타입 정의
전화번호부 검색 메서드의 타입 정의
이넘을 이용한 타입 정의
주요 메서드(조회) 타입 정의 및 실습 마무리

✔️ 아래 코드완성본의 주석 참고하자.


✔️ 코드 완성본

코드에 작성한 주석 잘 읽어보자

실습자료 - learn-typescript - quiz - 2_address-book - src - index.ts

interface PhoneNumberDictionary {
  [phone: string]: {
    num: number;
  };
}

interface Contact {
  name: string;
  address: string;
  phones: PhoneNumberDictionary;
}

// 실제로 코드 작성시 오타로 인한 에러가 나는 경우가 발생할 수 있다.
// 따라서, 오타 등으로 인한 에러를 막기 위해 '변수화' 하는 것이 좋다.
// => 타입 관점에서 안전한 코딩
enum PhoneType {
  Home = 'home',
  Office = 'office',
  Studio = 'studio'
}

// api
// TODO: 아래 함수의 반환 타입을 지정해보세요.
function fetchContacts(): Promise<Contact[]> {
  // TODO: 아래 변수의 타입을 지정해보세요.
  const contacts: Contact[] = [
    {
      name: 'Tony',
      address: 'Malibu',
      phones: {
        home: {
          num: 11122223333,
        },
        office: {
          num: 44455556666,
        },
      },
    },
    {
      name: 'Banner',
      address: 'New York',
      phones: {
        home: {
          num: 77788889999,
        },
      },
    },
    {
      name: '마동석',
      address: '서울시 강남구',
      phones: {
        home: {
          num: 213423452,
        },
        studio: {
          num: 314882045,
        },
      },
    },
  ];
  return new Promise(resolve => {
    setTimeout(() => resolve(contacts), 2000);
  });
}

// main
class AddressBook {
  // TODO: 아래 변수의 타입을 지정해보세요.
  contacts: Contact[] = [];  // 메인 변수의 타입 정의

  constructor() {
    this.fetchData();
  }

  fetchData(): void { 
    fetchContacts().then(response => { 
      // response는 Contact[]라고 나온다.
      // 위에서 fetchContacts()함수의 반환값이 Promise<Contact[]>이라고 정의했기 때문이다.
      // (=> fetchContacts()함수가 Promise를 반환하고, 이 Promise의 타입이 Contact[]라고 정의된 것이다.) 
      // 따라서, fetchContacts()함수 실행 후 then으로 받았을 때 타입이 Contact[]가 되는 것이다.
      this.contacts = response;
    });
  }

  /* TODO: 아래 함수들의 파라미터 타입과 반환 타입을 지정해보세요 */
  // return이 filter를 사용하기 때문에 그냥 Contact가 아닌 Contact[] 배열이라는 것을 적어줘야 한다. 
  // ( filter는 배열에 사용할 수 있다. )
  findContactByName(name: string): Contact[] { 
    return this.contacts.filter(contact => contact.name === name);
  }

  findContactByAddress(address: string): Contact[] {
    return this.contacts.filter(contact => contact.address === address);
  }

  // home, office, studio를 phoneType으로 받으려고 한다. 
  // 1개 이상이며 home, office, studio만 사용한단 것을 알기 때문에 '이넘'을 사용하면 좋다.
  findContactByPhone(phoneNumber: number, phoneType: PhoneType): Contact[] { // 이넘을 이용한 타입 정의
    return this.contacts.filter(
      contact => contact.phones[phoneType].num === phoneNumber
    );
  }

  addContact(contact: Contact): void { // 반환값이 없기때문에 void로 타입 설정한 것.
    this.contacts.push(contact);
  }

  displayListByName(): string[] {
    return this.contacts.map(contact => contact.name);
  }

  displayListByAddress(): string[] {
    return this.contacts.map(contact => contact.address);
  }
  /* ------------------------------------------------ */
}

new AddressBook();

💡참고

좋은 웹페이지 즐겨찾기