[2024.06.25 TIL] 내일배움캠프 49일차 (TypeScript 종합 강의, 고급 타입, 객체 리터럴, 유틸리티 타입)

My_Code·2024년 6월 25일
0

TIL

목록 보기
64/113
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.


💻 TIL(Today I Learned)

📌 Today I Done

✏️ 객체 리터럴(object literal)

  • 키 + 값의 쌍(pair)으로 구성된 객체를 정의하는 방식
const obj = {
  a: [1,2,3],
  b: 'b',
  c: 4
}

✏️ 객체 리터럴의 장점

  • 명시한 타입의 값만 대입할 수 있는 enum과는 다르게 어떤 타입의 값도 대입 가능

  • 즉, 다양한 데이터 타입이 가능하기에 유연한 구조를 가짐


✏️ enum과 객체 리터럴 사용 경우

  • enum을 쓰면 좋은 경우

    • 간단한 상수 값을 그룹화해서 관리할 때 사용
    • enum의 값은 상수이기 때문에 각 멤버의 값이 변하면 안됨
  • 객체 리터럴을 쓰면 좋은 경우

    • 멤버의 값이나 데이터 타입을 맘대로 변경할 수 있음
    • 복잡한 구조와 다양한 데이터 타입을 사용해야 할 때 사용

✏️ Partial<T> 유틸리티 타입

  • 타입 T의 모든 속성을 선택적으로 사용 가능하게 만듦
interface Person {
  name: string;
  age: number;
}

const updatePerson = (person: Person, fields: Partial<Person>): Person => {
  return { ...person, ...fields };
};

const person: Person = { name: "Spartan", age: 30 };
const changedPerson = updatePerson(person, { age: 31 });
  • fields 매개변수는 Partial<person> 타입을 사용해서 객체를 선택적으로 접근할 수 있게 함

  • name 속성만 작성, age 속성만 작성, name과 age 속성 모두 작성 하는 경우의 수가 있음

  • 유연하게 타입의 속성을 선택해서 객체를 만들 수 있음


✏️ Required<T> 유틸리티 타입

  • Partial<T> 타입과 반대로 타입 T의 모든 속성을 필수적으로 작성해야 함
interface Person {
  name: string;
  age: number;
  address?: string; // 선택적 속성, 있어도 되고 없어도 됨
}

// 만약 address를 포함한 모든 속성을 받아야하는 경우에는 아래와 같이 설정할 수 있음
type RequiredPerson = Required<Person>;

✏️ Readonly<T> 유틸리티 타입

  • 타입의 이름 그대로 타입 T의 모든 속성을 읽기 전용으로 만듦

  • 즉, 완전 불변 객체로 취급할 수 있음

interface DatabaseConfig {
  host: string;
  readonly port: number; // 인터페이스에서도 readonly 타입 사용 가능해요!
}

const mutableConfig: DatabaseConfig = {
  host: "localhost",
  port: 3306,
};

const immutableConfig: Readonly<DatabaseConfig> = {
  host: "localhost",
  port: 3306,
};

mutableConfig.host = "somewhere";
immutableConfig.host = "somewhere"; // 오류!

✏️ Pick<T, K> 유틸리티 타입

  • 타입 T에서 K속성만 선택해서 새로운 타입을 생성함

  • 일부 속성만 포함하는 객체를 생성할 때 사용

interface Person {
  name: string;
  age: number;
  address: string;
}

type SubsetPerson = Pick<Person, "name" | "age">;

const person: SubsetPerson = { name: "Spartan", age: 30 };

✏️ Omit<T, K> 유틸리티 타입

  • Pick<T, K> 유틸리티 타입과는 반대로 타입 T에서 K속성들만 제외한 새로운 타입을 생성함

  • 특정 속성을 제거한 새로운 타입을 생성할 때 사용

interface Person {
  name: string;
  age: number;
  address: string;
}

type SubsetPerson = Omit<Person, "address">;

const person: SubsetPerson = { name: "Alice", age: 30 };

✏️ 별다방 예제

interface User {
  id: number;
  name: string;
  role: 'admin' | 'customer';
}

interface Beverage {
  name: string;
  price: number;
}

interface Order {
  orderId: number;
  customerId: number;
  customerName: string;
  beverageName: string;
  status: 'placed' | 'completed' | 'picked-up';
}

// 필요한 데이터
let beverages: Beverage[] = [];
let orders: Order[] = [];

// 어드민 권한 체크
function isAdmin(user: User): boolean {
  return user.role === 'admin';
}

// 고객 권한 체크
function isCustomer(user: User): boolean {
  return user.role === 'customer';
}

// 음료 목록에 음료 추가
function addBeverage(user: User, name: string, price: number): void {
  if (user.role !== 'admin') {
    console.log('권한이 존재하지 않습니다.');
    return;
  }
  const newBeverage: Beverage = { name, price };
  beverages.push(newBeverage);
}

// 음료 목록에 음료 삭제
function removeBeverage(user: User, beverageName: string): void {
  if (user.role !== 'admin') {
    console.log('권한이 존재하지 않습니다.');
    return;
  }

  beverages = beverages.filter((beverage) => beverage.name !== beverageName);
}

// 음료 목록 조회
function getBeverages(user: User): Beverage[] {
  if (!user) {
    return [];
  }
  return beverages;
}

// 음료 찾기
function findBeverage(beverageName: string): Beverage | undefined {
  return beverages.find((beverage) => beverage.name === beverageName);
}

// 음료 주문
function placeOrder(user: User, beverageName: string): number {
  if (!isCustomer(user)) {
    console.log('권한이 존재하지 않습니다.');
    return -1;
  }

  if (!findBeverage(beverageName)) {
    console.log('해당 음료는 존재하지 않습니다.');
    return -1;
  }

  let orderId;
  if (orders.length === 0) {
    orderId = 1;
  } else {
    orderId = orders[orders.length - 1].orderId + 1;
  }

  const newOrder: Order = {
    orderId,
    customerId: user.id,
    customerName: user.name,
    beverageName,
    status: 'placed',
  };

  orders.push(newOrder);

  return newOrder.orderId;
}

// 음료 준비 완료
function completeOrder(user: User, orderId: number): void {
  if (!isAdmin(user)) {
    console.log('권한이 존재하지 않습니다.');
    return;
  }
  const order = orders.find((order) => order.orderId === orderId);
  if (order) {
    order.status = 'completed';
    console.log(
      `[고객 메세지] ${order.customerName}님~ 주문하신 ${order.beverageName} 1잔 나왔습니다.`
    );
  }
}

// 음료 수령
function pickUpOrder(user: User, orderId: number): void {
  if (!isCustomer(user)) {
    console.log('권한이 존재하지 않습니다.');
    return;
  }

  const order = orders.find(
    (order) => order.orderId === orderId && order.customerId === user.id
  );
  if (order) {
    order.status = 'picked-up';
    console.log(
      `[어드민 메세지] 고객 ID ${order.customerId}님이 주문 ID ${orderId}를 수령했습니다.`
    );
  }
}

function main() {
  const admin: User = {
    id: 1,
    name: '바리스타',
    role: 'admin',
  };

  // 유저 생성
  const member1: User = {
    id: 2,
    name: '르탄이',
    role: 'customer',
  };

  const member2: User = {
    id: 3,
    name: '꿈꾸는개발자',
    role: 'customer',
  };

  // 음료 등록
  addBeverage(admin, '아메리카노', 4000);
  addBeverage(admin, '카페라떼', 4500);
  addBeverage(admin, '에스프레소', 3000);

  // 음료 삭제
  removeBeverage(admin, '에스프레소');

  console.log(
    `안녕하세요~ ${
      member1.name
    } 고객님! 별다방에 오신 것을 환영합니다. 저희는 ${JSON.stringify(
      getBeverages(member1)
    )}를 판매하고 있습니다.`
  );
  // 음료 주문
  const orderId1 = placeOrder(member1, '아메리카노');
  if (orderId1 > 0) {
    setTimeout(() => {
      // 음료 제작 완료
      completeOrder(admin, orderId1);
      // 음료 수령
      pickUpOrder(member1, orderId1);
    }, 1000);
  }

  console.log(
    `안녕하세요~ ${
      member2.name
    } 고객님! 별다방에 오신 것을 환영합니다. 저희는 ${JSON.stringify(
      getBeverages(member2)
    )}를 판매하고 있습니다.`
  );
  // 음료 주문
  const orderId2 = placeOrder(member2, '카페라떼');
  if (orderId2 > 0) {
    setTimeout(() => {
      // 음료 제작 완료
      completeOrder(admin, orderId2);
      // 음료 수령
      pickUpOrder(member2, orderId2);
    }, 3000);
  }
}

main();


📌 Tomorrow's Goal

✏️ TypeScript 문법 종합 강의 시청

  • 오늘 미처 끝내지 못한 TypeScript 강의를 끝내고 내용을 정리할 예정

  • 객체지향 설계 쪽 내용이 조금 어렵지만 조금 더 내용을 찾아보면서 진행할 예정


✏️ Nest.js 강의 시청

  • TypeScript에 대한 강의 시청과 정리가 끝나면 시청할 예정

  • Nest.js는 TypeScript 내용을 기반으로 한다고 하기에 TypeScript를 먼저 시청했음

  • 새로운 웹 프레임워크인데 Express에 Spring의 구조가 약간 섞여 있다고 들음

  • 요즘 취업 시장에서 Nest.js에 대한 질문이 많기 때문에 이번에 확실히 학습할 예정

  • 그리고 강의 예제 말고도 혼자서 예제를 만들어서 실습하는 것이 중요하다고 함



📌 Today's Goal I Done

✔️ TypeScript 문법 종합 강의 시청

  • 오늘은 TypeScript의 타입에 대한 내용을 학습함

  • 타입을 명시하는 것도 규칙이 존재하고 잘 명시된 코드는 가독성과 유지 보수 향상에 도움이 됨

  • 어떻게 명시하는 게 좋은지는 예제나 과제를 통해서 조금 더 학습할 예정

  • 초반에는 아는 내용이 많아서 수월했지만, 뒤로 갈 수록 어려운 타입에 대해 배우니 강의 말고 구글링을 통해서 조금 더 학습함


profile
조금씩 정리하자!!!

0개의 댓글