타입스크립트 - 클래스와 인터페이스

객체지향 프로그래밍과 클래스 기초

객체지향 프로그래밍(Object Oriented Programming, OOP)은 커다란 문제를 클래스 단위로 나누고 클래스 간의 관계를 추가하면서 코드 중복을
최소화 하는 개발방식
이다. 클래스 간의 관계를 추가할 때는 상속이나 포함 관계를 고려하여 추가한다. OOP를 통해 어플리케이션을 개발하면 코드 중복을
상당히 줄일 수 있다. 타입스크립트는 자바스크립트(ES6)에 비해서 OOP를 지원하는 부분이 훨씬 더 많다.

클래스는 멤버 변수와 멤버 메서드 등으로 구성된 ‘틀’이며 클래스를 실제로 사용하려면 객체로 생성해야 한다.
생성된 객체는 실제 메모리에 위치하고 객체의 참조가 객체 참조변수에 할당되는 과정을 인스턴스화(instantiate)라고 한다.

OOP에서 클래스 간의 관계는 크게 두 가지로 나누어 볼 수 있다. 하나는 상속 관계(IS-A)이며, 다른 하나는 포함 관계(HAS-A)이다.

  • 상속 관계 : 상속은 클래스 계층을 만들어 코드 중복을 줄이는 객체지향 프로그래밍 방법이다.
    타입스크립트는 클래스에 대해 단일 상속만 지원하므로 자식 클래스는 하나의 부모 클래스만 상속받을 수 있다.
    자식 클래스가 부모 클래스를 상속받을 때는 자식 클래스의 생성자에서 super() 메서드를 호출해 부모 클래스의 생성자를 호출해 주어야 한다.
  • 포함 관계 : 포함 관계는 클래스가 다른 클래스를 포함하는 HAS-A 관계이다. 클래스 내부에 다른 클래스를 포함하는 관계는
    합성(composition) 관계와 집합(aggregation) 관계로 나뉜다. 합성 관계는 강한 관계인 반면, 집합 관계는 약한 관계이다.

추상 클래스(abstract class)는 구현 메서드와 추상 메서드(abstract method)가 동시에 존재할 수 있다. 여기에서 구현 메서드는 실제 구현 내용을 포함
한 메서드이고, 추상 메서드는 선언만 된 메서드이다. 이와 같이 추상 클래스는 구현 내용이 없는 추상 메서드를 포함하기 때문에 불완전한 클래스이다.
따라서 추상 클래스는 단독으로 객체를 생성할 수 없고 추상 클래스를 상속하고 구현 내용을 추가하는 자식 클래스를 통해 객체를 생성해야 한다.

구현하지 않은 추상 메서드가 선언되었으므로 자식 클래스에서는 추상 메서드를 오버라이딩(overriding)하여 반드시 구현해 주어야 한다.
또한 추상 클래스에 추상 멤버 변수가 선언되어 있으면 자식 클래스에서도 선언해야 한다. 추상 클래스를 작성할 때 abstract 키워드는
static 이나 private(public, protected는 가능)과 함께 선언할 수 없음에 주의해야 한다.

인터페이스에 대한 이해

인터페이스(interface)는 클래스에서 구현 부분이 빠진 타입으로 이해하면 된다. 인터페이스는 컴파일 후에 사라지게 된다.
인터페이스는 선언만 존재하며, 멤버 변수와 멤버 메서드를 선언할 수 있지만 접근 제한자는 설정할 수 없다.

자식 인터페이스의 경우 부모 인터페이스를 상속해서 확장할 수 있는데, 타입스크립트는 다중 상속이 가능하다. 만약 다중 상속을 받을 때
같은 이름의 메서드를 상속받으면, 상속받는 인터페이스에서 같은 이름의 메서드를 모두 재정의 해야 한다.

인터페이스는 타입 선언이 많아도 컴파일(ES6) 후에는 모두 사라지므로 런타임 성능에 영향을 끼치지 않는다.

오버라이딩(Overriding)

오버라이딩은 부모 클래스에 정의된 메서드를 자식 클래스에서 새로 구현하는 것을 일컫는 개념이다.
부모 클래스에서 오버라이딩을 당하는(?) 메서드를 오버라이든 메서드 라고 하고, 자식 클래스에서 오버라이딩된 메서드를 오버라이딩 메서드라고 했을 때,
오버라이딩이 이루어지기 위해서는 다음 두 가지의 조건을 만족해야 한다.

  • 조건 1. 오버라이든 메서드의 매개변수 타입은 오버라이딩 메서드의 매개변수 타입과 같거나 상위 타입이어야 한다.
    (단, 오버라이딩 메서드의 매개변수 타입이 Any 이면 예외)
  • 조건2. 오버라이든 메서드의 매개변수 갯수가 오버라이딩 메서드의 매개변수 갯수와 같거나 많아야 한다.
    (단, 조건 1이 성립하는 전제가 있어야 함)

오버로딩(Overloading)

메서드 오버로딩은 메서드의 이름이 같지만 매개변수의 타입과 개수를 다르게 정의하는 방법이다.
클래스의 상속을 고려해 오버로딩을 구현하려면 부모 클래스에 상위 타입을 가지는 오버라이든 메서드를 선언해 두고 파생 클래스에서 오버라이딩 메서드를
선언해 구현할 수 있다. 이 때 오버라이딩 메서드가 오버로딩을 수행하려면 오버라이딩 메서드 위에 오버로드를 추가하면 된다.
인터페이스를 이용해서 오버로딩을 구현할 수도 있다. 인터페이스를 이용해 오버로딩을 하려면 인터페이스에 오버로딩할 기본 메서드를 선언해 준다.
그리고 인터페이스를 구현할 클래스에서 기본 메서드를 구현해 준다.

인터페이스를 사용하면 선언과 구현을 분리할 수 있고 구현부의 구조를 강제할 수 있다.
이 점에서 로직과 구조가 섞여 있는 클래스를 상속해 오버로딩하는 것 보다 구조만을 포함하고 있는 인터페이스를 이용하는 것이 복잡도가 낮다.

다형성(polymorphism)

다형성은 ‘여러 모양’을 의미하는 그리스 단어이고, 다형성에서 형은 타입을 의미한다. 프로그래밍 언어에서 다형성이란,
여러 타입을 받아들임으로써 여러 형태를 가지는 것을 의미한다. 타입스크립트에서 살펴볼 수 있는 다형성의 예로는 다음 세 가지가 있다.

  • 클레스의 다형성
  • 인터페이스의 다형성
  • 매개변수의 다형성

클래스의 다형성

부모 클래스 A를 자식 클래스 B가 상속할 때 부모 클래스 A가 변수의 타입으로 지정되면 자식 클래스의 객체에 할당될 수 있다.
이 때 부모 클래스 A는 부모 클래스 A를 상속하는 어떤 자식 클래스의 타입이라도 받아들일 수 있는 다형 타입이 되고, 다형성을 띠게 한다.
런타임 시에 호출될 메서드가 결정되는 특성을 런타임 다형성이라고 한다. 런타임 다형성의 대표적인 예로 덕 타이핑(duck typing)이 있다.

인터페이스의 다형성

인터페이스 A가 있고 인터페이스 A를 구현한 클래스 B가 있을 때 클래스 B가 인터페이스 A 타입으로 지정된 변수에 할당될 때 생기는 다형성을 의미한다.
인터페이스를 구현한 클래스를 가지고 객체를 생성하면, 해당 객체 참조변수는 인터페이스에 정의된 멤버 변수, 메서드 등에 접근할 수는 있지만, 구현 클래스에 추가된 멤버 변수나 클래스에는 접근할 수가 없다.

매개변수의 다형성

메서드의 매개변수가 여러 타입을 받아들이면서(유니언 타입, 인터페이스 타입 등) 생기는 다형성을 말한다.
매개변수의 타입이 여러 서브 타입을 받아들아면 해당 매개변수의 타입이 서브 타입 다형성이 된다. 반대로 자바스크립트의 매개변수처럼 타입을 지정하지 않고 여러 타입을 받아들이면 매개변수 다형성이 된다.

Getter와 Setter

자바스크립트에서는 객체의 멤버에 접근할 수 있는 방법으로 ES6의 getter와 setter를 지원한다. getter는 일반적으로 접근자(accessor)라 하고,
setter는 설정자(mutation)라 한다.
타입스크립트에서는 클래스 내에 get과 set 키워드를 통해 getter와 setter를 선언할 수 있다.
값을 설정하거나 읽을 때 로직을 추가하고 싶다면 get/set 키워드로 접근자와 설정자를 추가해 줄 수 있다.

정적 변수와 정적 메서드

타입스크립트에서는 static 키워드를 지원한다. static 키워드는 클래스에 정적 멤버 변수나 정적 메서드 등을 선언할 때 사용할 수 있는데
객체 생성 없이 바로 접근이 가능하므로 메모리 절약 효과가 있다.

static 키워드는 클래스에 선언된 멤버 변수를 객체 생성 없이 접근할 수 있게 해주는 장점이 있다.
이 경우 단일 상태를 관리하지만 외부에 변수를 둘 수 없는 문제점이 있다. 반드시 클래스를 통해 정적 멤버에 접근해야 하기 때문이다.
외부에 변수를 두면서 프로그램 단위에서 유일한 객체를 유지할 수 있게 하려면 싱글턴 패턴(Singleton Pattern)을 도입해야 한다.
싱글턴 패턴은 유일한 객체를 생성해 공유해서 사용하는 방식이다. 싱글턴 패턴에는 크게 두 가지가 있다.

  • 부지런한 초기화(eager initialization)
  • 게으른 초기화(lazy initialization).

부지런한 초기화

부지런한 초기화는 프로그램이 구동될 때 초기화가 일어나고 공개된 정적 메서드를 통해 생성된 객체를 얻는다.
싱글턴 객체는 사용자가 정의한 임의의 변수에 할당돼 접근할 수 있다.

게으른 초기화

게으른 초기화는 프로그램이 구동될 때 초기화되지 않지만 공개된 정적 메서드를 호출하는 시점에 객체를 생성한다. 싱글턴 객체는 변수에 할당될 수 있다.

readonly 제한자

readonly는 타입스크립트 2.0부터 지원하는 제한자이다. readonly가 선언된 변수는 초기화되면 재할당이 불가능하다.
const와 readonly의 공통점은 상수 선언이 가능하다는 점이다. 차이점은 다음과 같다.

  1. const는 초기화가 필수이지만, readonly는 초기화가 선택이다.
  2. const는 값 재할당이 불가능하지만, readonly는 가능하다.
  3. const는 선언 가능한 대상이 전역 변수, 클래스 메서드의 변수, 함수의 변수 등이 있고 readonly는 선언 가능한 대상이 인터페이스의 멤버 변수,
    클래스의 멤버 변수, 객체 리터럴의 속성, 새롭게 정의하는 타입 등이 있다.
  4. const는 상수로 사용하고, readonly는 읽기 전용 속성을 가지고 있다.
  5. const는 ES6인 경우 컴파일 후 선언이 유지되고, readonly는 컴파일 후 사라진다.

댓글

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×