타입스크립트의 인터페이스

타입스크립트에서의 인터페이스 알아보기

Intro


인터페이스


인터페이스는 객체의 구조를 설명한다. 인터페이스 또한 클래스와 마찬가지로 단어의 첫 글자는 관례상 대문자로한다. 해당 인터페이스를 타입으로 지정한 경우 구조를 따라 객체를 생성한다.

ts
interface Preson {
    // 필드의 구조 타입 뒤에 ; 을 붙여준다.
    // 이는 객체 생성시엔 ,로 구분한다.
    name: string;
    age: number;

    // 메소드의 구조
    greet(phrase: string): void;
}

// 인터페이스를 타입으로 사용 user1은 Person 구조의 객체를 저장한다.
let user1: Person 

user1 = {
    name: 'Lim',
    age: 27,
    greet(phrase: string){
        console.log(phrase + ' ' + this.name');
    }
}

user1.greet('Hi there - I am');
>> Hi there - I am Lim

왜 사용하는가?


사용자 타입(type)과의 차이점이 무엇이 있을까. 실제로 위 코드의 interface 구문을 type 으로 변경해도 동일하게 작동한다.

차이점으로 인터페이스는 객체의 구조를 설명하기 위해서만 사용한다. 실제로 인터페이스 구문을 더 많이 사용하며 사용자 정의 타입으로 가능한 작업은 클래스 내 인터페이슬르 구현한다.
객체의 구조를 설계한데 초점을 맞춘다. 일종의 약속과 같다. 이러한 약속을 지키도록 -able로 명시한다. 이를 구현하는 클래스는 인터페이스의 필드 및 메서드를 구현하되 추가로 해당 클래스만의 필드 및 메서드를 추가한다.

인터페이스는 여러 클래스에서 사용할 수 있다.

ts
interface Greetable {
    name: string;

    // 메소드의 구조
    greet(phrase: string): void;
}

// Person 클래스는 Greetable 인터페이스의 구조를 설계해야한다.
// 클래스는 상속(extends)과 달리 복수의 인터페이스를 구현할 수 있다.
class Person implements Greetable{
    name: string;
    age = 27;

    constructor(n: string){
        this.name = n;
    }

    greet(phrase: string){
        console.log(phrase + ' ' + this.name');
    }
}

let user1: Greetable;
// 인터페이스를 구현하는 클래스를 기반으로 인터페이스를 타입으로 사용한다.
user1 = new Person('Lim');

console.log(user1);
>> Person {age: 27, name: "Lim"}

추상클래스와 다소 비슷한데, 인터페이스는 기본 설계도라고 보면 되고 추상 클래스는 미완성 설계도라고 보면 된다. 인터페이스 자체에서 구현된 함수는 없으며 추상 클래스의 경우 구현된 함수도 있지만 구현되지 않은 함수는 abstract키워드로 상속받은 자식에서 구현하도록 한다.

  • 추상클래스는 여러 자식클래스가 있는데, 자식 클래스에서 일부 완벽히 똑같은 기능을 수행하는 경우.

  • 인터페이스는 특정 혹은 같은 기능이 필요한 경우 사용한다.

  • 이 내용에 대해 잘 정리된 것은 마이자몽님 블로그의 [JAVA] 추상클래스 VS 인터페이스 왜 사용할까? 차이점, 예제로 확인 :: 마이자몽님의 글을 참고하면 좋을 것 같다.

  • 장점 해당 인터페이스를 구현하는 클래스는 공통적인 변수 및 메소드를 가지고 있기 때문에 클래스간 기능을 쉽게 공유할 수 있다.
    Greetable인터페이스를 구현하는 클래스는 모두 greet메소드를 가지고 있으며 자신의 이름을 호출할 수 있다.

읽기 전용 인터페이스


인터페이스 내 readonly 제어자를 추가할 수 있다. 다만 public, private는 지정할 수 없다.

ts
interface Greetable {
    // pname은 한 번만 설정되어야 하며 이후에는 읽기만 가능하다.
    // 즉 객체 내부에서 초기화가 일어나면 이후에는 변경이 불가능하다.
    readonly pname: string;

    greet(phrase: string): void;
}

class Person implements Greetable{
    name: string;
    age = 27;

    constructor(n: string){
        this.name = n;
    }

    greet(phrase: string){
        console.log(phrase + ' ' + this.name');
    }
}

let user1: Greetable;

user1 = new Person('Lim');

user1.name = 'Jin'; // Error!

이 부분에서도 type과 유사하다.

ts
type Greetable {
    readonly pname: string;

    greet(phrase: string): void;
}

인터페이스의 확장


인터페이스는 상속 구현도 가능하다. 인터페이스를 생성한 후

ts
interface Named {
    readonly name: string
}

interface Greetable extends Named {
    // name: string 부모(Named)의 변수를 상속받는다.

    greet(phrase: string): void;
}

class Person implements Greetable {
    name: string;
    age = 30;

}

상속을 통해 인터페이스의 확장할 수 있다. 또한 인터페이스는 상속시 여러 인터페이스의 상속도 가능하다. 결국 모두 병합되기 때문이다.(클래스에선 불가능했다.)

함수 타입으로서의 인터페이스


객체의 구조를 설명하는 인터페이스를 함수의 구조를 정의할 때 사용할 수 있다. 함수 타입을 대체할 수 있다. 타입의 경우 아래처럼 사용자 정의 타입(type)을 사용한다.

ts
type AddFn = (a: number, b: number) => number;

let add: AddFn;

add = (n: number, n2: number2) => {
    return n1 + n2;
}

함수도 객체의 일부이므로 인터페이스로 설계가 가능하다.

ts
interface AddFn {
    // 인터페이스가 함수 타입으로서 사용될 경우 아래와 같이 익명함수를 이용한다.
    (a: numeber, b: number): number;
}

let add: AddFn;

add = (n: number, n2: number2) => {
    return n1 + n2;
}

선택적 매개변수와 속성


클래스에서 인터페이스를 구현하고자 할때 일부 변수 혹은 속성을 선택적으로 사용할 수 있다 바로 ? 키워드를 사용하는 것이다.
?는 있을 수 있지만 없을 수 있다는 의미이다.

ts
interface Named {
    readonly name?: string;
    outputName?: string;
}

interface Greetable extends Named { 
    greet(phrase: string): void;
}

class Person implements Greetable {
    // 클래스에서도 선택적 속성 사용을 위해 인터페이스에서도 ? 키워드를 이용한다.
    name?: string;
    age = 30;

    constructor(n?: string) {
        if (n) {
            this.name = n;
        }
    }

    greet(phrase: string) {
        if(this.name) {
            console.log(phrase + ' ' + this.name);
        }else {
            console.log('Hi!');
        }
    }
}

let user1 : Greetable;

user1 = new Person();

user1.greet('Hi there - I am');
console.log(user1);
>> Hi!

인터페이스에 ?키워드를 사용하고 이를 구현하는 클래스에서도 선택적 속성을 사용할 경우 ?키워드를 사용하고 없을때와 있을때의 로직을 수행하는 것으로 선택적 속성을 이용하기 위해 느슨한 연관관계가 필요하다.

자바스크립트로 인터페이스 컴파일


컴파일 이후 인터페이스 관련해서는 흔적을 찾아볼 수 없다. 인터페이스에 대한 변환이 이루어지지 않기 때문이다. 즉 컴파일 이전에 사용할 수 있는 타입스크립트 전용 기능임을 알 수 있다.