Skip to content

3. 인터페이스(Interface)


1. 인터페이스

  • 인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다.
  • 타입스크립트에서의 인터페이스는 보통 다음과 같은 범주에 대해 약속을 정의할 수 있다.


1] 객체의 스펙(속성과 속성 타입)

2] 함수의 파라미터

3] 함수의 스펙(파라미터, 반환 타입 등)

4] 배열과 객체를 접근하는 방식

5] 클래스


2. 인터페이스 맛보기

  • 인터페이스에 대해 알아볼 수 있는 간단한 예제를 보자.


let person = { name: "Capt", age: 28 };

function logAge(obj: { age: number }) {
  console.log(obj.age);
}

logAge(person); // 28


  • logAge() 함수에서 받는 인자의 형태는 age를 속성으로 갖는 객체이다.
  • 이렇게 인자를 받을 때 단순한 타입뿐만 아니라 객체의 속성 타입까지 정의할 수 있다.


  • 여기에 인터페이스를 적용하면 다음과 같다.


interface personAge {
  age: number;
}

function logAge(obj: personAge) {
  console.log(obj.age);
}

let person = { name: "Capt", age: 28 };
logAge(person); // 28


  • 이제는 logAge()의 인자가 좀 더 명시적으로 바뀌었다.
  • logAge()의 인자는 personAge라는 타입을 가져야 한다.


  • 그리고 위 코드를 보고 다음과 같이 추론할 수 있다.
  • 인터페이스를 인자로 받아 사용할 때 항상 인터페이스의 속성 갯수와 인자로 받는 객체의 속성 갯수를 일치시키지 않아도 된다.
  • 다시 말해, 인터페이스에 정의된 속성, 타입의 조건만 만족한다면 객체의 속성 갯수가 더 많아도 상관 없다는 의미이다.
  • 또한, 인터페이스에 선언된 속성 순서를 지키지 않아도 된다.


3. 옵션 속성

  • 인터페이스를 사용할 때 인터페이스에 정의되어 있는 속성을 모두 다 꼭 사용하지 않아도 된다.
  • 이를 옵션 속성이라고 한다.


interface 인터페이스_이름 {
 속성? 타입;
}


  • 이처럼 속성의 끝에 ?를 붙인다.


interface CraftBeer {
  name: string;
  hop?: number;
}

let myBeer = {
  name: "Saporo",
};

function brewBeer(beer: CraftBeer) {
  console.log(beer.name);
}

brewBeer(myBeer); // Saporo


  • 코드를 보면 brewBeer() 함수에서 Beer 인터페이스를 인자의 타입으로 선언했음에도 불구하고, 인자로 넘긴 객체에는 hop 속성이 없다.
  • 왜냐하면 hop을 옵션 속성으로 선언했기 때문이다.


4. 옵션 속성의 장점

  • 옵션 속성의 장점은 단순히 인터페이스를 사용할 때 속성을 선택적으로 적용할 수 있다는 것뿐만 아니라 인터페이스에 정의되어 있지 않은 속성에 대해서 인지시켜줄 수 있다는 점이다.


interface CraftBeer {
  name: string;
  hop?: number;
}

let myBeer = {
  name: "Saporo",
};

function brewBeer(beer: CraftBeer) {
  console.log(beer.brewery); // error
}
brewBeer(myBeer);


  • 위처럼 인터페이스에 정의되어 있지 않은 속성에 대해서 오류를 표시하며, 만약 오탈자가 났었다면 그것 역시 알려줬을 것이다.


5. 읽기 전용 속성

  • 읽기 전용 속성은 인터페이스로 객체를 처음 생성할 때만 값을 할당하고 그 이후에는 변경할 수 없는 속성을 의미한다.
  • 문법은 다음과 같이 readonly 속성을 앞에 붙인다.


interface CraftBeer {
  readonly brand: string;
}


  • 인터페이스로 객체를 선언하고 나서 수정하려고 하면 다음과 같이 오류가 난다.


interface CraftBeer {
  readonly brand: string;
}

let myBeer: CraftBeer = {
  brand: "Belgian Monk",
};

myBeer.brand = "Korean Carpenter"; // error


6. 읽기 전용 배열

  • 배열을 선언할 때 ReadonlyArray<T> 타입을 사용하면 읽기 전용 배열을 생성할 수 있다.


let arr: ReadonlyArray<number> = [1, 2, 3];

arr.splice(0, 1); // error
arr.push(4); // error
arr[0] = 100; // error


  • 위처럼 배열을 ReadonlyArray로 선언하면 배열의 내용을 변경할 수 없다.
  • 선언하는 시점에만 값을 정의할 수 있다.


7. 객체 선언과 관련된 타입 체킹

  • 타입스크립트는 인터페이스를 이용하여 객체를 선언할 때 좀 더 엄밀한 속성 검사를 진행한다.


interface CraftBeer {
  brand?: string;
}

function brewBeer(beer: CraftBeer) {
  // ...
}

brewBeer({ brandon: "what" }); // error


  • 위 코드를 보면 CraftBeer 인터페이스에는 brand라고 선언되어 있지만 brewBeer() 함수에 인자로 넘기는 myBeer 객체에는 brandon이 선언되어 있어 오탈자 점검을 요하는 오류가 난다.


  • 만약 이런 타입 추론을 무시하고 싶다면 다음과 같이 선언한다.


interface CraftBeer {
  brand?: string;
}

function brewBeer(beer: CraftBeer) {
  // ...
}

let myBeer = { brandon: "what" };
brewBeer(myBeer as CraftBeer);


  • 그럼에도 불구하고 만약 인터페이스에 정의하지 않은 속성들을 추가로 사용하고 싶을 때는 다음과 같은 방법을 사용한다.


interface CraftBeer {
  brand?: string;
  [propName: string]: any;
}

function brewBeer(beer: CraftBeer) {
  // ...
}

let myBeer = { brandon: "what" };
brewBeer(myBeer as CraftBeer);


8. 함수 타입

  • 인터페이스는 함수의 타입을 정의할 때에도 사용할 수 있다.


interface login {
  (username: string, password: string): boolean;
}


  • 함수의 인자의 타입과 반환 값의 타입을 정한다.


interface login {
  (username: string, password: string): boolean;
}

let loginUser: login = function (id: string, pw: string): boolean {
  console.log("로그인 했습니다");
  return true;
};


9. 클래스 타입

  • C#이나 자바처럼 타입스크립트에서도 클래스가 일정 조건을 만족하도록 타입 규칙을 정할 수 있다.


interface CraftBeer {
  beerName: string;
  nameBeer(beer: string): void;
}

class myBeer implements CraftBeer {
  beerName: string = "Baby Guinness";
  nameBeer(beer: string): void {
    this.beerName = beer;
  }

  constructor() {}
}


10. 인터페이스 확장

  • 클래스와 마찬가지로 인터페이스도 인터페이스간 확장이 가능하다.


interface Person {
  name: string;
}

interface Developer extends Person {
  skill: string;
}

let feOne = {} as Developer;
feOne.name = "josh";
feOne.skill = "TypeScript";

let feTwo: Developer = {
  name: "josh",
  skill: "TypeScript",
};


  • 혹은 다음과 같이 여러 인터페이스를 상속받아 사용할 수 있다.


interface Person {
  name: string;
}

interface Drinker {
  drink: string;
}

interface Developer extends Person, Drinker {
  skill: string;
}

let feOne = {} as Developer;
feOne.name = "josh";
feOne.skill = "TypeScript";
feOne.drink = "Beer";

let feTwo: Developer = {
  name: "josh",
  skill: "TypeScript",
  drink: "Beer",
};


11. 하이브리드 타입

  • 자바스크립트의 유연하고 동적인 타입 특성에 따라 인터페이스 역시 여러 가지 타입을 조합하여 만들 수 있다.
  • 예를 들어, 다음과 같이 함수 타입이면서 객체 타입을 정의할 수 있는 인터페이스가 있다.


interface CraftBeer {
  (beer: string): string;
  brand: string;
  brew(): void;
}

function myBeer(): CraftBeer {
  let my = function (beer: string) {} as CraftBeer;
  my.brand = "Beer Kitchen";
  my.brew = function () {};
  return my;
}

let brewedBeer = myBeer();
brewedBeer("My First Beer");
brewedBeer.brand = "Pangyo Craft";
brewedBeer.brew();

References