본문 바로가기
Frontend/엘리스 SW 엔지니어 트랙

[ 엘리스 SW 엔지니어 트랙 ] 23일차

by YWTechIT 2021. 11. 25.
728x90

📍 23일 차 11.25. 목(실시간 강의)

오늘은 interface, generic 문법에 대해서 배웠다.


❏ Interface

  1. 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용 가능
  2. 직접 인스턴스를 생성할 수 없음. 모든 메서드는 추상 메서드다. (이때 abstract 키워드를 사용하지 않는다)
  3. 선언만 존재한다. (JS로 변환되면 인터페이서는 사라진다.)
  4. interface 간에 extends 를 사용하여 다중 상속 가능(class와 비슷)
  5. 추상 클래스와 다른 점: 추상 class는 전체적인 구조, interface는 프로그래머 간의 협업 개발을 할 때 사용
  6. 함수의 매개변수(파라미터로)로 사용
  7. API 응답에서 데이터의 구조를 결정할 때 사용
// 키 값을 다음처럼 정해주면 키 값은 제한이 없다.
interface User {
  [grade: number]: "A" | "B" | "C" | "D"
}

let user: User = {
  1: "A",
  2: "B",
  5: "D",
}
  1. 선택적 프로퍼티(?): ?가 붙는 경우는 반드시 구현되지 않고 선택 가능
// 키 값을 다음처럼 정해주면 키 값은 제한이 없다.
interface User {
  1: string;
  2?: string;
  5: string
}

let user: User = {
  1: "A",
  5: "D",
}
  1. 클래스 선언문의 implements 를 붙여서 사용 가능(인터페이스를 구현하는 클래스의 일관성을 유지할 수 있는 장점을 가짐)
// 모든 메소드는 추상 메소드여야 한다.
// 인터페이스에서 정의한 타입은 클래스 안에서도 모두 적용해야한다.
interface ITodo {
    id: number;
    content: string;
    completed: boolean;
        sayHello(): void;
}

class Todo implements ITodo {
    constructor(
        public id: number,
        public content: string,
        public completed: boolean
    ){} 

        sayHello(){
      console.log(`안녕하세요!!`)
    }
}

const today = new Todo(1, "TED", true);  // 인스턴스
console.log(today)  // Todo { id: 1, content: 'TED', completed: true }
today.sayHello();  // 안녕하세요!!
  1. 함수와 변수에서도 덕타입이 적용된다.(덕 타이핑: 타입 체크에서 중요한 것은 값을 실제 가지고 있느냐에 관한 것, 인터페이스에서 정의한 프로퍼티나 메서드를 가지고 있다면 그 인터페이스를 구현한 것으로 여김 덕 타이핑 또는 구조적 타이핑이라 부름)
type IPerson = {
  name: string
}

function sayHello(person: IPerson): void{
  console.log(`Hello ${person.name}`)
}

const me = {name : "TED", age: 27};
sayHello(me);

❏ 디자인패턴

  1. 생성패턴: 객체를 생성하는데 관련한 패턴
  2. 구조패턴: 프로그램의 구조에 관련한 패턴
  3. 행위패턴: 반복적으로 사용되는 객체들의 상호작용 패턴
// 전략 패턴
interface Weapon {
  attack: () => void;
}

class Sword implements Weapon {
  public attack() {
      console.log("검 공격");
  }
}

class Spear implements Weapon {
  public attack() {
      console.log("창 공격");
  }
}

class Bow implements Weapon {
  public attack() {
      console.log("활 공격");
  }
}

class GameUser {
  private weapon: Weapon | null = null;

  public setWeapon(weapon: Weapon) {
      this.weapon = weapon;
  }

  public attack() {
      if (this.weapon === null) {
          console.log("맨손 공격");
      } else {
          this.weapon.attack();
      }
  }
}

const testUser = new GameUser();
testUser.setWeapon(new Spear());
testUser.attack();  // 창 

testUser.setWeapon(new Bow()); 
testUser.attack();  // 활 공격

testUser.setWeapon(new Sword());
testUser.attack();  // 검 공격

testUser.setWeapon(null);
testUser.attack();  // 맨손 공격

 

728x90

❏ 제네릭(Generic)

  1. 데이터 타입을 일반화함
  2. 자료형을 정하지 않고 여러 타입을 사용할 수 있게 한다.(범용적인 함수를 쓰고 싶을 때?, 여러 가지 타입을 사용하고 싶을 때)
  3. 재사용성이 높은 함수를 만들어줄 때 사용됨
  4. 한 가지 타입보다 여러 가지 타입에서 동작하는 함수를 생성한다.
// class 문법에서의 제네릭
class Stack<T> {
  private data: T[] = []; 

  constructor(){}

  push(item: T): void {
    this.data.push(item);
  }

  pop():T | undefined{
    return this.data.pop()
  }
}

// 인스턴스의 타입을 유연하게 설정할 수 있다.
const numberStack = new Stack<number>();
const stringStack = new Stack<string>();
numberStack.push(1);
stringStack.push('a')

// 함수에서의 제네릭
function getSize<T>(arr: T[]): number {
  return arr.length;
}

const arr1 = [1, 2, 3];
getSize<number>(arr1);

const arr2 = ['a', 'b', 'c'];
getSize<string>(arr2);
  1. 제네릭 한 변수를 추가하고 싶을 때 <T, U>
// 1
function toPair<T, U>(a: T, b: U): [T, U]{
  return [a, b];
}

toPair<number, string>(1, "1");
toPair<string, boolean>("Hello", true);

// 2
function logText<T>(text: T[]): number{
  return text.length;
}

// 3
interface Mobile<T>{
  name: string;
  price: number;
  option: T;
}

let myInfo: Mobile<boolean> = {
  name: "TED",
  price: 6000,
  option: true
}

let yourInfo: Mobile<object> = {
  name: "TED",
  price: 6000,
  option: {
    age: 28,
    color: "orange",
  }
}

// 함수에서의 제네릭
// <T>는 text의 인자는 T로 받고 리턴값도 T 타입으로 해준다.
interface GenericLogTextFn {
  <T>(text: T): T;
}

interface GenericLogTextFn<T> {
  (text: T): T
}

function logText<T>(text: T): T{
  return text;
}

let myString: GenericLogTextFn<string> = logText;
console.log(myString)

interface Mobile<T> {
  name: string;
  price: number;
  option: T;
}

interface OptionType {
  color: string;
  coupon: boolean;
}

const m1: Mobile<OptionType> = {
  name: "s21",
  price: 1000,
  option: { color: "red", coupon: false },
};

// 제네릭 상속
interface User {
  name: string;
  age: number;
}

interface Book {
  price: number;
}

let user: User = { name: "a", age : 10 };
let book: Book = { price: 3000 };

function showName<T extends { name : string }>(data: T){
  return data.name;
}

❏ Non-null assertion operator

  1. Null 이 아닌 어선별 연산자는 피연산자가 null 이 아니라고 컴파일러에게 전달하여 일시적으로 Null 제약조건을 완화합니다. 권장하지 않는 문법이고 대신 && 을 사용한다.

❏ Union type

// example1
function getAge(age: number | string){
  return typeof age === "number" ? age.toFixed() : age;
}

const myAge = getAge(28);
console.log(myAge)

const yourAge = getAge("25");
console.log(yourAge)

// union 기호
interface Person {
  name: string;
  age: number;
}

interface Developer {
  name: string;
  skill: string;
}

// 겹치는 속성만 사용 가능
function introduce(someone: Person | Developer){
  someone.name;
  someone.age;  // Error
  someone.skill;  // Error
}

// 모든 속성 사용 가능
function introduce(someone: Person & Developer){
  someone.name;
  someone.age;  // 사용 가능
  someone.skill;  // 사용 가능
}

❏ 제약 조건(constraints / keyof)

  1. 제네릭 타입에 어느 정도 힌트를 주고 제약을 걸기 위해 사용, 최소한 length 속성이 있어야 에러가 나지 않는다
interface LengthWise {
  length: number;
}

interface Rect {
  length: number;
  area: number;
}

function logText1<T extends LengthWise | Rect>(text: T){
  console.log(text.length);
  console.log(text.area);  // 공통되는 속성이 아니므로 Error가 난다.
}

function logText1<T extends LengthWise & Rect>(text: T){
  console.log(text.length);
  console.log(text.area);  // 모든 속성 사용 가능하므로 Error가 나지 않는다.

❏ keyof를 사용한 제약

  1. 객체의 속성을 제약하고 싶을 때 사용
// 객체 안에서의 속성만 사용하는 문법
function getProperty<T, O extends keyof T>(obj: T, key: O){
  return obj[key]
}

let obj = { a: 1, b: 2, c: 3 }
// 'a' | 'b' | 'c'
console.log(getProperty(obj, 'c'));

❏ 기타 기능 및 개념

  1. Type Assertion: 타입 추론기능은 강력하지만 한계 존재, 타입단언(type assertion)은 타입스크립트가 추정하지 못하는 부분까지 개발자가 선언하는 것
  2. as
let a;
a = 10;
a = 'abc';
let b = a;

b = a as string;

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

interface Developer{
  name: string;
  skill: string;
}

function introduce(someone: Person | Developer){
  someone.name;
  (someone as Person).age;
  (someone as Developer).skill;
}
  1. typeGuard
// 함수
function doSomeThing(id: string | number){
  if (typeof id === 'string'){
      console.log(id.trim());
  }else {
    console.log(id);
  }
}

// 클래스
class Diner {}
class Merchant {}

function doSomething(user: Diner | Merchant){
  if(user instanceof Diner){
  }else{   
  }
}

// 메소드
interface Bird {
  fly(): string;
}

interface Fish {
  swim(): number;
}

function doSomething(animal: Fish | Bird){
  if('swim' in animal){
    console.log((animal as Fish).swim())
  }else{
    console.log((animal as Bird).fly())
  }
}

doSomething({
  swim: () => {
    return 82
  }
})

doSomething({
  fly: () => {
    return "Hello"
  }
})

// 타입 확인
function isFish(animal: Fish | Bird): animal is Fish {
  return (animal as Fish).swim !== undefined;
}

function doSomething(animal: Fish | Bird){
  if (isFish(animal)) {
    animal.swim()
  }else{
    animal.fly()
  }
}

❏ 실습

  1. 클래스 vs 추상클래스: 어떤 메서드는 그냥 구현해도 되고, 어떤 메서드는 정의되지 않은 메서드 일 수 있다.(사용하려면 상속이 필요하다)
  2. 인터페이스: 추상클래스와 달리 abstract 를 사용하지 않음. 구성되는 것의 타입만 명시해서(생성자나 메서드 구현을 작성할 필요가 없다.) (하나의 interface에 여러 데이터가 뭉치지 않도록 분산시킬 수 있기 때문에 다중 상속이 가능하다.)
  3. interface vs type vs abstract
// 인터페이스: 상속이 가능하며, 다중 상속도 가능하다. 내부에서 메서드 구현을 할 수 없다.
interface Test {
    name: string;
    id: number;
    introduce(): void;
}

// 상속이 불가능하다. 내부에서 메서드 구현을 할 수 없다.
type Test {
    name: string;
    id: number;
    introduce(): void;
}

// 클래스이기 대문에 상속은 가능하지만, 다중 상속은 불가하다. 원하는 메서드는 구현을 할 수 있다.
abstract class Test {
    name: string;
    id: number;
  abstractintroduce(): void;
}
  1. T extends number | string : 2개 이상 상속이 가능함.
  2. interface 활용 예시: 데이터베이스 조작(로그인, 게시물, 등등)
  3. ORM: SQL문을 대신해서 내가 알고 있는 프로그래밍 언어로 DB로 조작하는 방법(나중에 SQL문으로 변환된다)

❏ Generic

  1. 선언한 시점에 타입을 사용하는 것이 아니라 사용할 때 모든 타입을 마음대로 넣을 수 있다.
  2. 타입에 제한을 걸기 위해 <T extends string | number> 처럼 사용할 수 있다.

❏ 자바스크립트의 동적인 타입 예시

  1. true == "true": false abstract equality composition (피연산자에 number 씌우고 비교한다)
  2. 010-03=5, 0 붙어서 8진수가 된다. (16진수는 ox, 2진수는 ob)
  3. 1+'1' = '11'(+ 는 문자열 우선순위), 11-'1' = 10(-Number만 지원한다.)
  4. 0/0=NaN , 숫자로 취급하지 않는다.
  5. 1 / 0 > 10 * 1000 = true : True, infinity는 표현 범위를 넘어서면 취급한다
  6. true++=>Error : 값에다가 붙이는 게 아니라 변수에다 선언해야 한다.
  7. 1+2+"3" = 33
반응형

댓글