[TypeScript] 타입스크립트 인터페이스(Interface), Strategy pattern
글 작성자: 망고좋아
반응형
🎯 타입스크립트 Interface
- 인터페이스는 상호 간에 정의한 약속 혹은 규칙을 의미한다.
- 일반적으로 타입 체크를 위해 사용되며 변수, 함수, 클래스에 사용할 수 있다.
- 직접 인스턴스를 생성할 수 없고 모든 메서드가 추상 메서드이다.
- 추상 클래스의 추상 메서드와 달리 abstract 키워드는 사용할 수 없다.
- TS에서만 interface를 지원한다.
📝 interface 사용 이유
- 타입의 이름을 짓고 코드 안의 계약을 정의
- 프로젝트 외부에서 사용하는 코드의 계약을 정의하는 강력한 방법이다.
- 객체의 스펙(속성과 속성의 타입)
- 함수의 파라미터
- 함수의 스펙(파라미터, 반환 타입 등)
- 배열과 객체에 접근하는 방식
- 클래스
📝 기본 예제
function sayName(obj: { name: string } ) {
console.log(obj.name);
}
let person = { name: 'John' }
sayName(person)
// 인터페이스 사용
interface Person {
name: string
}
function sayName(obj: Person) {
console.log(obj.name);
}
let person = { name: 'John' }
sayName(person)
- interface를 추가하여 함수 매개변수 프로퍼티를 정의할 수 있다.
- 정의한 프로퍼티 값을 누락하거나 정의하지 않는 값을 인수로 전달 시 컴파일 에러가 발생한다.
📝 Properties
- 컴파일러는 프로퍼티의 두 가지 요소를 검사한다.
- 필수요소 프로퍼티의 유무
- 프로퍼티 타입
- 예약어로 프로퍼티를 세밀하게 컨트롤할 수 있다.
?
: Optional Propertiesreadonly
interface User {
readonly 1: string;
2: string;
3?: string;
}
let user: User = {
1: 'A',
2: 'B',
}
user.1 = 'test'; // error!
📕 Optional Properties
- 프로퍼티 선언 시 이름 끝이
?
를 붙여서 사용 - 인터페이스에 속하지 않는 프로퍼티의 사용을 방지하면서, 사용 가능한 프로퍼티를 기술할 때 사용한다.
📕 readonly
- 객체가 처음 생성될 때만 값 설정이 가능하고 이후 수정이 불가능하다.
- 프로퍼티 이름 앞에 readonly를 붙여 사용한다.
📕 readonly vs const
- 공통점 : 생성 후에 배열을 변경하지 않음을 보장한다.
- 변수는 const를 사용하고 프로퍼티는 readonly를 사용한다.
📝 Interface types
- 타입스크립트에서 인터페이스 함수, 클래스에서 사용할 수 있다.
- 함수
- JS객체가 가질 수 있는 넓은 범위의 형태를 기술한다.
- 프로퍼티로 객체를 기술하는 것 외에도, 인터페이스는 함수 타입을 설명한다.
- 클래스
- 클래스가 특정 통신 프로토콜을 충족하도록 명시적으로 강제한다.
- C#과 Java와 같은 언어에서 일반적으로 인터페이스를 사용하는 방법과 동일하다.
📕 function type
interface SEarchFunc {
(source: string, subString: string): boolean
}
// 변수로 직접 함수 값이 할당되었기 때문에 인수 타입 생략 가능
// TS의 문맥상 타이핑(contextual typing)이 인수 타입 추론
let mySearch: SEarchFunc
mySearch = function(src, sub) {
let result = src.search(sub);
return result > -1;
}
// error : Type '(src: string, sub: string) => string' is not assignable to type 'SEarchFunc'. Type 'string' is not assignable to type 'boolean'
mySearch = function(src, sub) {
let result = src.search(sub);
return 'string';
}
- 함수의 인자의 타입과 반환 값의 타입을 정의한다.
- 함수의 타입을 정의할 때에도 사용된다.
📕 class type
// 인터페이스의 정의
interface ITodo {
id: number;
content: string;
completed: boolean;
makeSound(): void
}
// Todo 클래스는 ITodo 인터페이스를 구현하여야 한다.
class Todo implements ITodo {
constructor (
public id: number,
public content: string,
public completed: boolean
) { }
makeSound(): void {
console.log("멍멍")
}
}
const todo = new Todo(1, 'Typescript', false);
console.log(todo);
/*
Todo: {
"id": 1,
"content": "Typescript",
"completed": false
}
*/
console.log(todo.makeSound()); // 멍멍
- 클래스가 특정 계약(contract)을 충족하도록 명시적으로 강제한다.
- 클래스 선언문의
implements
뒤에 인터페이스를 선언하면 해당 클래스는 지정된 인터페이스를 반드시 구현하여야 한다. - 이는 인터페이스를 구현하는 클래스의 일관성을 유지할 수 있는 장점을 갖는다.
- 인터페이스는 프로퍼티와 메서드를 가질 수 있다는 점에서 클래스와 유사하나 직접 인스턴스를 생성할 수는 없다.
// 인터페이스의 정의
interface IPerson {
name: string;
sayHello(): void;
}
/*
인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메소드를 반드시 구현하여야 한다.
*/
class Person implements IPerson {
// 인터페이스에서 정의한 프로퍼티의 구현
constructor(public name: string) {}
// 인터페이스에서 정의한 추상 메소드의 구현
sayHello() {
console.log(`Hello ${this.name}`);
}
}
function greeter(person: IPerson): void {
person.sayHello();
}
const me = new Person('Lee');
greeter(me); // Hello Lee
- 인터페이스는 프로퍼티뿐만 아니라 메서드도 포함할 수 있다.
- 단, 모든 메서드는 추상 메서드이어야 한다. 인터페이스를 구현하는 클래스는 인터페이스에서 정의한 프로퍼티와 추상 메서드를 반드시 구현하여야 한다.
📝 interface 확장
interface Animal {
makeSound(): void
}
interface Dog extends Animal {
speed: number
}
class Bulldog implements Dog {
speed: 10;
makeSound(): void {
console.log('멍멍');
}
}
- 클래스와 마찬가지로 인터페이스도 인터페이스 간의 확장이 가능하다.
extends
키워드를 사용하여 인터페이스 또는 클래스를 상속받을 수 있다.
interface Person {
name: string;
age?: number;
}
interface Developer {
skills: string[];
}
interface WebDeveloper extends Person, Developer {}
const webDeveloper: WebDeveloper = {
name: 'Lee',
age: 20,
skills: ['HTML', 'CSS', 'JavaScript']
}
- 복수의 인터페이스를 상속받을 수도 있다.
class Person {
constructor(public name: string, public age: number) {}
}
interface Developer extends Person {
skills: string[];
}
const developer: Developer = {
name: 'Lee',
age: 20,
skills: ['HTML', 'CSS', 'JavaScript']
}
- 인터페이스는 인터페이스뿐만 아니라 클래스도 상속받을 수 있다.
- 단, 클래스의 모든 멤버(public, protected, private)가 상속되지만 구현까지 상속하지는 않는다.
📝 hybrid type
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = function (start: number) {} as Counter;
counter.interval = 123;
counter.reset = function () {};
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
- 자바스크립트의 유연하고 동적인 타입 특성에 따라 인터페이스 역시 여러 타입을 조합할 수 있다.
- 위 코드와 같이 함수 타입이면서 객체 타입을 정의할 수 있는 인터페이스도 구현할 수 있다.
📝 interface를 활용한 디자인 패턴 (Strategy pattern)
- 객체가 할 수 있는 행위들을 전략(strategy)으로 만들어두고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 수정이 가능하도록 만든 패턴이다.
🛠 활용 사례
class VendingMachine {
pay() {
console.log("cash pay!");
}
}
// 전략 패턴 도입
interface PaymentStrategy {
pay(): void;
}
class CardPaymentStrategy implements PaymentStrategy {
pay(): void {
console.log("card pay!");
}
}
class CashPaymentStrategy implements PaymentStrategy {
pay(): void {
console.log("Cash pay!");
}
}
class VendingMachine {
private paymentStrategy: PaymentStrategy;
setPaymentStrategy(paymentStrategy: PaymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
pay() {
this.paymentStrategy.pay();
}
}
const vendingMachine =new VendingMachine();
vendingMachine.setPaymentStrategy(new CashPaymentStrategy());
vendingMachine.pay(); // cash pay
vendingMachine.setPaymentStrategy(new CardPaymentStrategy());
vendingMachine.pay(); // card pay
- 자판기 결제 방법을 현금 결제에서 카드 결제로 변경할 때, Pay 메서드 구현 변경이 필요하다.
- 메서드 수정 방식의 문제점
- OCP(Open Closed Principle)를 위배한다.(OCP 설계 원칙)
- OPC : 소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다.
- 시스템이 커져서 확장될 경우 연동되는 시스템에도 영향을 줄 수 있다.
- OCP(Open Closed Principle)를 위배한다.(OCP 설계 원칙)
- 디자인 패턴으로 문제를 해결할 수 있다.
📌 참고
반응형
'프로그래밍 > TypeScript' 카테고리의 다른 글
[TypeScript] 타입스크립트 유니온 타입 & 인터섹션 타입 (Union Type & Intersection Type) (0) | 2021.11.29 |
---|---|
[TypeScript] 타입스크립트 제네릭(Generic), Factory Pattern with Generics (0) | 2021.11.27 |
[TypeScript] 타입스크립트 Getter & Setter / readonly / static (0) | 2021.11.24 |
[TypeScript] 타입스크립트 Class 접근 제어자 (0) | 2021.11.24 |
[TypeScript] 타입스크립트 클래스 사용하기 - 상속, 추상 클래스, 추상 클래스를 활용한 디자인 패턴(Template Method Pattern) (0) | 2021.11.24 |
댓글
이 글 공유하기
다른 글
-
[TypeScript] 타입스크립트 유니온 타입 & 인터섹션 타입 (Union Type & Intersection Type)
[TypeScript] 타입스크립트 유니온 타입 & 인터섹션 타입 (Union Type & Intersection Type)
2021.11.29 -
[TypeScript] 타입스크립트 제네릭(Generic), Factory Pattern with Generics
[TypeScript] 타입스크립트 제네릭(Generic), Factory Pattern with Generics
2021.11.27 -
[TypeScript] 타입스크립트 Getter & Setter / readonly / static
[TypeScript] 타입스크립트 Getter & Setter / readonly / static
2021.11.24 -
[TypeScript] 타입스크립트 Class 접근 제어자
[TypeScript] 타입스크립트 Class 접근 제어자
2021.11.24